static public function factory($driver, $params = array())
{
$driver = basename($driver);
- $class = 'Ingo_Driver_' . $driver;
+ $class = 'Ingo_Driver_' . ucfirst($driver);
return class_exists($class)
? new $class($params)
--- /dev/null
+<?php
+/**
+ * Ingo_Driver_Ldap:: implements the Sieve_Driver api to allow scripts to be
+ * installed and set active via an LDAP server.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Jason M. Felice <jason.m.felice@gmail.com>
+ * @package Ingo
+ */
+class Ingo_Driver_Ldap extends Ingo_Driver
+{
+ /**
+ * Constructor.
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('ldap')) {
+ Horde::fatal(PEAR::raiseError(_("LDAP support is required but the LDAP module is not available or not loaded.")), __FILE__, __LINE__);
+ }
+
+ $default_params = array(
+ 'hostspec' => 'localhost',
+ 'port' => 389,
+ 'script_attribute' => 'mailSieveRuleSource'
+ );
+
+ parent::__construct(array_merge($default_params, $params));
+ }
+
+ /**
+ * Create a DN from a DN template.
+ * This is done by substituting the username for %u and the 'dc='
+ * components for %d.
+ *
+ * @param string $templ The DN template (from the config).
+ *
+ * @return string The resulting DN.
+ */
+ protected function _substUser($templ)
+ {
+ $domain = '';
+ $username = $this->_params['username'];
+
+ if (strpos($username, '@') !== false) {
+ list($username, $domain) = explode('@', $username);
+ }
+ $domain = implode(', dc=', explode('.', $domain));
+ if (!empty($domain)) {
+ $domain = 'dc=' . $domain;
+ }
+
+ if (preg_match('/^\s|\s$|\s\s|[,+="\r\n<>#;]/', $username)) {
+ $username = '"' . str_replace('"', '\\"', $username) . '"';
+ }
+
+ return str_replace(array('%u', '%d'),
+ array($username, $domain),
+ $templ);
+ }
+
+ /**
+ * Connect and bind to ldap server.
+ */
+ protected function _connect()
+ {
+ if (!($ldapcn = @ldap_connect($this->_params['hostspec'],
+ $this->_params['port']))) {
+ return PEAR::raiseError(_("Connection failure"));
+ }
+
+ /* Set the LDAP protocol version. */
+ if (!empty($this->_params['version'])) {
+ @ldap_set_option($ldapcn,
+ LDAP_OPT_PROTOCOL_VERSION,
+ $this->_params['version']);
+ }
+
+ /* Start TLS if we're using it. */
+ if (!empty($this->_params['tls'])) {
+ if (!@ldap_start_tls($ldapcn)) {
+ return PEAR::raiseError(sprintf(_("STARTTLS failed: (%s) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+ }
+
+ /* Bind to the server. */
+ if (isset($this->_params['bind_dn'])) {
+ $bind_dn = $this->_substUser($this->_params['bind_dn']);
+ if (is_a($bind_dn, 'PEAR_Error')) {
+ return $bind_dn;
+ }
+
+ if (isset($this->_params['bind_password'])) {
+ $password = $this->_params['bind_password'];
+ } else {
+ $password = $this->_params['password'];
+ }
+
+ if (!@ldap_bind($ldapcn, $bind_dn, $password)) {
+ return PEAR::raiseError(sprintf(_("Bind failed: (%s) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+ } elseif (!(@ldap_bind($ldapcn))) {
+ return PEAR::raiseError(sprintf(_("Bind failed: (%s) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+
+ return $ldapcn;
+ }
+
+ /**
+ * Retrieve current user's scripts.
+ *
+ * @param resource $ldapcn The connection to the LDAP server.
+ * @param string $userDN Set to the user object's real DN.
+ *
+ * @return mixed Array of script sources, or PEAR_Error on failure.
+ */
+ protected function _getScripts($ldapcn, &$userDN)
+ {
+ $attrs = array($this->_params['script_attribute'], 'dn');
+ $filter = $this->_substUser($this->_params['script_filter']);
+
+ /* Find the user object. */
+ $sr = @ldap_search($ldapcn, $this->_params['script_base'], $filter,
+ $attrs);
+ if ($sr === false) {
+ return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+ if (@ldap_count_entries($ldapcn, $sr) != 1) {
+ return PEAR::raiseError(sprintf(_("Expected 1 object, got %d."),
+ ldap_count_entries($ldapcn, $sr)));
+ }
+ $ent = @ldap_first_entry($ldapcn, $sr);
+ if ($ent === false) {
+ return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+
+ /* Retrieve the user's DN. */
+ $v = @ldap_get_dn($ldapcn, $ent);
+ if ($v === false) {
+ @ldap_free_result($sr);
+ return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+ $userDN = $v;
+
+ /* Retrieve the user's scripts. */
+ $attrs = @ldap_get_attributes($ldapcn, $ent);
+ @ldap_free_result($sr);
+ if ($attrs === false) {
+ return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+
+ /* Attribute can be in any case, and can have a ";binary"
+ * specifier. */
+ $regexp = '/^' . preg_quote($this->_params['script_attribute'], '/') .
+ '(?:;.*)?$/i';
+ unset($attrs['count']);
+ foreach ($attrs as $name => $values) {
+ if (preg_match($regexp, $name)) {
+ unset($values['count']);
+ return array_values($values);
+ }
+ }
+
+ return array();
+ }
+
+ /**
+ * Sets a script running on the backend.
+ *
+ * @param string $script The sieve script.
+ *
+ * @return mixed True on success, PEAR_Error on error.
+ */
+ protected function setScriptActive($script)
+ {
+ $ldapcn = $this->_connect();
+ if (is_a($ldapcn, 'PEAR_Error')) {
+ return $ldapcn;
+ }
+
+ $values = $this->_getScripts($ldapcn, $userDN);
+ if (is_a($values, 'PEAR_Error')) {
+ return $values;
+ }
+
+ $found = false;
+ foreach ($values as $i => $value) {
+ if (strpos($value, "# Sieve Filter\n") !== false) {
+ if (empty($script)) {
+ unset($values[$i]);
+ } else {
+ $values[$i] = $script;
+ }
+ $found = true;
+ break;
+ }
+ }
+ if (!$found && !empty($script)) {
+ $values[] = $script;
+ }
+
+ $replace = array(Horde_String::lower($this->_params['script_attribute']) => $values);
+ if (empty($values)) {
+ $r = @ldap_mod_del($ldapcn, $userDN, $replace);
+ } else {
+ $r = @ldap_mod_replace($ldapcn, $userDN, $replace);
+ }
+ if (!$r) {
+ return PEAR::raiseError(sprintf(_("Activating the script for \"%s\" failed: (%d) %s"),
+ $userDN,
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+
+ @ldap_close($ldapcn);
+ return true;
+ }
+
+ /**
+ * Returns the content of the currently active script.
+ *
+ * @return string The complete ruleset of the specified user.
+ */
+ public function getScript()
+ {
+ $ldapcn = $this->_connect();
+ if (is_a($ldapcn, 'PEAR_Error')) {
+ return $ldapcn;
+ }
+
+ $values = $this->_getScripts($ldapcn, $userDN);
+ if (is_a($values, 'PEAR_Error')) {
+ return $values;
+ }
+
+ $script = '';
+ foreach ($values as $value) {
+ if (strpos($value, "# Sieve Filter\n") !== false) {
+ $script = $value;
+ break;
+ }
+ }
+
+ @ldap_close($ldapcn);
+ return $script;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Driver_Null:: implements a null api -- useful for just testing
+ * the UI and storage.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Brent J. Nordquist <bjn@horde.org>
+ * @package Ingo
+ */
+
+class Ingo_Driver_Null extends Ingo_Driver
+{
+ /**
+ * Constructor.
+ */
+ public function __construct($params = array())
+ {
+ $this->_support_shares = true;
+ parent::__construct($params);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Driver_Sivtest:: implements the Sieve_Driver api to allow scripts to
+ * be installed and set active via the Cyrus sivtest command line utility.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ * Copyright 2004-2007 Liam Hoekenga <liamr@umich.edu>
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @author Jan Schneider <jan@horde.org>
+ * @author Liam Hoekenga <liamr@umich.edu>
+ * @package Ingo
+ */
+class Ingo_Driver_Sivtest extends Ingo_Driver
+{
+ /**
+ * The Net_Sieve object.
+ *
+ * @var Net_Sieve
+ */
+ protected $_sieve;
+
+ /**
+ * Constructor.
+ */
+ public function __construct($params = array())
+ {
+ $default_params = array(
+ 'hostspec' => 'localhost',
+ 'logintype' => '',
+ 'port' => 2000,
+ 'scriptname' => 'ingo',
+ 'admin' => '',
+ 'usetls' => true,
+ 'command' => '',
+ 'socket' => '',
+ );
+
+ parent::__construct(array_merge($default_params, $params));
+ }
+
+ /**
+ * Connect to the sieve server.
+ *
+ * @return mixed True on success, PEAR_Error on false.
+ */
+ protected function _connect()
+ {
+ if (!empty($this->_sieve)) {
+ return true;
+ }
+
+ $this->sivtestSocket($this->_params['username'],
+ $this->_params['password'], $this->_params['hostspec']);
+ if (substr(PHP_VERSION, 0, 1) == '5') {
+ $domain_socket = 'unix://' . $this->_params['socket'];
+ } else {
+ $domain_socket = $this->_params['socket'];
+ }
+
+ $this->_sieve = new Net_Sieve($this->_params['username'],
+ $this->_params['password'],
+ $domain_socket,
+ 0,
+ null,
+ null,
+ false,
+ true,
+ $this->_params['usetls']);
+
+ $res = $this->_sieve->getError();
+ if (is_a($res, 'PEAR_Error')) {
+ unset($this->_sieve);
+ return $res;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Sets a script running on the backend.
+ *
+ * @param string $script The sieve script.
+ *
+ * @return mixed True on success.
+ * Returns PEAR_Error on error.
+ */
+ public function setScriptActive($script)
+ {
+ $res = $this->_connect();
+ if (is_a($res, 'PEAR_Error')) {
+ return $res;
+ }
+
+ $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
+ if (is_a($res, 'PEAR_ERROR')) {
+ return $res;
+ }
+
+ return $this->_sieve->installScript($this->_params['scriptname'], $script, true);
+ }
+
+ /**
+ * Returns the content of the currently active script.
+ *
+ * @return string The complete ruleset of the specified user.
+ */
+ public function getScript()
+ {
+ $res = $this->_connect();
+ if (is_a($res, 'PEAR_Error')) {
+ return $res;
+ }
+ return $this->_sieve->getScript($this->_sieve->getActive());
+ }
+
+ /**
+ * Used to figure out which Sieve server the script will be run
+ * on, and then open a GSSAPI authenticated socket to said server.
+ *
+ * @param string $username The username.
+ * @param string $password The password.
+ * @param string $hostspec The hostspec.
+ *
+ * @return TODO
+ */
+ public function sivtestSocket($username, $password, $hostspec)
+ {
+ $command = '';
+ $error_return = '';
+
+ if (strtolower($this->_params['logintype']) == 'gssapi'
+ && isset($_SERVER['KRB5CCNAME'])) {
+ $command .= 'KRB5CCNAME=' . $_SERVER['KRB5CCNAME'];
+ }
+
+ if (substr(PHP_VERSION, 0, 1) == '5') {
+ $domain_socket = 'unix://' . $this->_params['socket'];
+ } else {
+ $domain_socket = $this->_params['socket'];
+ }
+
+ $command .= ' ' . $this->_params['command']
+ . ' -m ' . $this->_params['logintype']
+ . ' -u ' . $username
+ . ' -a ' . $username
+ . ' -w ' . $password
+ . ' -p ' . $this->_params['port']
+ . ' -X ' . $this->_params['socket']
+ . ' ' . $hostspec;
+
+ $conn_attempts = 0;
+ while ($conn_attempts++ < 4) {
+ $attempts = 0;
+ if (!file_exists($this->_params['socket'])) {
+ exec($command . ' > /dev/null 2>&1');
+ sleep(1);
+ while (!file_exists($this->_params['socket'])) {
+ usleep(200000);
+ if ($attempts++ > 5) {
+ $error_return = ': No socket after 10 seconds of trying!';
+ continue 2;
+ }
+ }
+ }
+ $socket = new Net_Socket();
+ $error = $socket->connect($domain_socket, 0, true, 30);
+ if (!is_a($error, 'PEAR_Error')) {
+ break;
+ }
+
+ // We failed, break this connection.
+ unlink($this->_params['socket']);
+ }
+
+ if (!empty($error_return)) {
+ return PEAR::raiseError(_($error_return));
+ }
+
+ $status = $socket->getStatus();
+ if (is_a($status, 'PEAR_Error') || $status['eof']) {
+ return PEAR::raiseError(_('Failed to write to socket: (connection lost!)'));
+ }
+
+ $error = $socket->writeLine("CAPABILITY");
+ if (is_a($error, 'PEAR_Error')) {
+ return PEAR::raiseError(_('Failed to write to socket: ' . $error->getMessage()));
+ }
+
+ $result = $socket->readLine();
+ if (is_a($result, 'PEAR_Error')) {
+ return PEAR::raiseError(_('Failed to read from socket: ' . $error->getMessage()));
+ }
+
+ if (preg_match('|^bye \(referral "(sieve://)?([^"]+)|i',
+ $result, $matches)) {
+ $socket->disconnect();
+
+ $this->sivtestSocket($username, $password, $matches[2]);
+ } else {
+ $socket->disconnect();
+ exec($command . ' > /dev/null 2>&1');
+ sleep(1);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Driver_Timsieved:: implements the Sieve_Driver api to allow scripts to
+ * be installed and set active via a Cyrus timsieved server.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Ingo
+ */
+class Ingo_Driver_Timsieved extends Ingo_Driver
+{
+ /**
+ * The Net_Sieve object.
+ *
+ * @var Net_Sieve
+ */
+ protected $_sieve;
+
+ /**
+ * Constructor.
+ */
+ public function __construct($params = array())
+ {
+ $this->_support_shares = true;
+
+ $default_params = array(
+ 'hostspec' => 'localhost',
+ 'logintype' => 'PLAIN',
+ 'port' => 2000,
+ 'scriptname' => 'ingo',
+ 'admin' => '',
+ 'usetls' => true
+ );
+
+ parent::__construct(array_merge($default_params, $params));
+ }
+
+ /**
+ * Connect to the sieve server.
+ *
+ * @return mixed True on success, PEAR_Error on false.
+ */
+ public function _connect()
+ {
+ if (!empty($this->_sieve)) {
+ return true;
+ }
+
+ if (empty($this->_params['admin'])) {
+ $auth = $this->_params['username'];
+ } else {
+ $auth = $this->_params['admin'];
+ }
+ $this->_sieve = new Net_Sieve($auth,
+ $this->_params['password'],
+ $this->_params['hostspec'],
+ $this->_params['port'],
+ $this->_params['logintype'],
+ Ingo::getUser(false),
+ false,
+ false,
+ $this->_params['usetls']);
+
+ $res = $this->_sieve->getError();
+ if (is_a($res, 'PEAR_Error')) {
+ unset($this->_sieve);
+ return $res;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Sets a script running on the backend.
+ *
+ * @param string $script The sieve script.
+ *
+ * @return mixed True on success, PEAR_Error on error.
+ */
+ public function setScriptActive($script)
+ {
+ $res = $this->_connect();
+ if (is_a($res, 'PEAR_Error')) {
+ return $res;
+ }
+
+ if (!strlen($script)) {
+ return $this->_sieve->setActive('');
+ }
+
+ $res = $this->_sieve->haveSpace($this->_params['scriptname'],
+ strlen($script));
+ if (is_a($res, 'PEAR_ERROR')) {
+ return $res;
+ }
+
+ return $this->_sieve->installScript($this->_params['scriptname'],
+ $script, true);
+ }
+
+ /**
+ * Returns the content of the currently active script.
+ *
+ * @return string The complete ruleset of the specified user.
+ */
+ public function getScript()
+ {
+ $res = $this->_connect();
+ if (is_a($res, 'PEAR_Error')) {
+ return $res;
+ }
+ $active = $this->_sieve->getActive();
+ if (empty($active)) {
+ return '';
+ }
+ return $this->_sieve->getScript($active);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Driver_Vfs:: implements an Ingo storage driver using Horde VFS.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Brent J. Nordquist <bjn@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Ingo
+ */
+class Ingo_Driver_Vfs extends Ingo_Driver
+{
+ /**
+ * Constructs a new VFS-based storage driver.
+ *
+ * @param array $params A hash containing driver parameters.
+ */
+ public function __construct($params = array())
+ {
+ $this->_support_shares = true;
+
+ $default_params = array(
+ 'hostspec' => 'localhost',
+ 'port' => 21,
+ 'filename' => '.ingo_filter',
+ 'vfstype' => 'ftp',
+ 'vfs_path' => '',
+ 'vfs_forward_path' => '',
+ );
+
+ parent::__construct(array_merge($default_params, $params));
+ }
+
+ /**
+ * Sets a script running on the backend.
+ *
+ * @param string $script The filter script
+ *
+ * @return mixed True on success, or PEAR_Error on failure.
+ */
+ public function setScriptActive($script)
+ {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (empty($script)) {
+ $result = $this->_vfs->deleteFile($this->_params['vfs_path'], $this->_params['filename']);
+ } else {
+ $result = $this->_vfs->writeData($this->_params['vfs_path'], $this->_params['filename'], $script, true);
+ }
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (isset($this->_params['file_perms']) && !empty($script)) {
+ $result = $this->_vfs->changePermissions($this->_params['vfs_path'], $this->_params['filename'], $this->_params['file_perms']);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ // Get the backend; necessary if a .forward is needed for
+ // procmail.
+ $backend = Ingo::getBackend();
+ if ($backend['script'] == 'procmail' && isset($backend['params']['forward_file']) && isset($backend['params']['forward_string'])) {
+ if (empty($script)) {
+ $result = $this->_vfs->deleteFile($this->_params['vfs_forward_path'], $backend['params']['forward_file']);
+ } else {
+ $result = $this->_vfs->writeData($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $backend['params']['forward_string'], true);
+ }
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (isset($this->_params['file_perms']) && !empty($script)) {
+ $result = $this->_vfs->changePermissions($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $this->_params['file_perms']);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the content of the currently active script.
+ *
+ * @return string The complete ruleset of the specified user.
+ */
+ public function getScript()
+ {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ return $this->_vfs->read('', $this->_params['vfs_path'] . '/' . $this->_params['filename']);
+ }
+
+ /**
+ * Connect to the VFS server.
+ *
+ * @return boolean True on success, PEAR_Error on false.
+ */
+ protected function _connect()
+ {
+ /* Do variable substitution. */
+ if (!empty($this->_params['vfs_path'])) {
+ $user = Ingo::getUser();
+ $domain = Ingo::getDomain();
+ if ($_SESSION['ingo']['backend']['hordeauth'] !== 'full') {
+ $pos = strpos($user, '@');
+ if ($pos !== false) {
+ $domain = substr($user, $pos + 1);
+ $user = substr($user, 0, $pos);
+ }
+ }
+ $this->_params['vfs_path'] = str_replace(
+ array('%u', '%d', '%U'),
+ array($user, $domain, $this->_params['username']),
+ $this->_params['vfs_path']);
+ }
+
+ if (!empty($this->_vfs)) {
+ return true;
+ }
+
+ $this->_vfs = &VFS::singleton($this->_params['vfstype'], $this->_params);
+ if (is_a($this->_vfs, 'PEAR_Error')) {
+ $error = $this->_vfs;
+ $this->_vfs = null;
+ return $error;
+ } else {
+ return true;
+ }
+ }
+
+}
+++ /dev/null
-<?php
-/**
- * Ingo_Driver_ldap:: Implements the Sieve_Driver api to allow scripts to be
- * installed and set active via an LDAP server.
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Jason M. Felice <jason.m.felice@gmail.com>
- * @package Ingo
- */
-class Ingo_Driver_ldap extends Ingo_Driver
-{
- /**
- * Constructor.
- */
- public function __construct($params = array())
- {
- if (!Horde_Util::extensionExists('ldap')) {
- Horde::fatal(PEAR::raiseError(_("LDAP support is required but the LDAP module is not available or not loaded.")), __FILE__, __LINE__);
- }
-
- $default_params = array(
- 'hostspec' => 'localhost',
- 'port' => 389,
- 'script_attribute' => 'mailSieveRuleSource'
- );
-
- parent::__construct(array_merge($default_params, $params));
- }
-
- /**
- * Create a DN from a DN template.
- * This is done by substituting the username for %u and the 'dc='
- * components for %d.
- *
- * @param string $templ The DN template (from the config).
- *
- * @return string The resulting DN.
- */
- protected function _substUser($templ)
- {
- $domain = '';
- $username = $this->_params['username'];
-
- if (strpos($username, '@') !== false) {
- list($username, $domain) = explode('@', $username);
- }
- $domain = implode(', dc=', explode('.', $domain));
- if (!empty($domain)) {
- $domain = 'dc=' . $domain;
- }
-
- if (preg_match('/^\s|\s$|\s\s|[,+="\r\n<>#;]/', $username)) {
- $username = '"' . str_replace('"', '\\"', $username) . '"';
- }
-
- return str_replace(array('%u', '%d'),
- array($username, $domain),
- $templ);
- }
-
- /**
- * Connect and bind to ldap server.
- */
- protected function _connect()
- {
- if (!($ldapcn = @ldap_connect($this->_params['hostspec'],
- $this->_params['port']))) {
- return PEAR::raiseError(_("Connection failure"));
- }
-
- /* Set the LDAP protocol version. */
- if (!empty($this->_params['version'])) {
- @ldap_set_option($ldapcn,
- LDAP_OPT_PROTOCOL_VERSION,
- $this->_params['version']);
- }
-
- /* Start TLS if we're using it. */
- if (!empty($this->_params['tls'])) {
- if (!@ldap_start_tls($ldapcn)) {
- return PEAR::raiseError(sprintf(_("STARTTLS failed: (%s) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
- }
-
- /* Bind to the server. */
- if (isset($this->_params['bind_dn'])) {
- $bind_dn = $this->_substUser($this->_params['bind_dn']);
- if (is_a($bind_dn, 'PEAR_Error')) {
- return $bind_dn;
- }
-
- if (isset($this->_params['bind_password'])) {
- $password = $this->_params['bind_password'];
- } else {
- $password = $this->_params['password'];
- }
-
- if (!@ldap_bind($ldapcn, $bind_dn, $password)) {
- return PEAR::raiseError(sprintf(_("Bind failed: (%s) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
- } elseif (!(@ldap_bind($ldapcn))) {
- return PEAR::raiseError(sprintf(_("Bind failed: (%s) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
-
- return $ldapcn;
- }
-
- /**
- * Retrieve current user's scripts.
- *
- * @param resource $ldapcn The connection to the LDAP server.
- * @param string $userDN Set to the user object's real DN.
- *
- * @return mixed Array of script sources, or PEAR_Error on failure.
- */
- protected function _getScripts($ldapcn, &$userDN)
- {
- $attrs = array($this->_params['script_attribute'], 'dn');
- $filter = $this->_substUser($this->_params['script_filter']);
-
- /* Find the user object. */
- $sr = @ldap_search($ldapcn, $this->_params['script_base'], $filter,
- $attrs);
- if ($sr === false) {
- return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
- if (@ldap_count_entries($ldapcn, $sr) != 1) {
- return PEAR::raiseError(sprintf(_("Expected 1 object, got %d."),
- ldap_count_entries($ldapcn, $sr)));
- }
- $ent = @ldap_first_entry($ldapcn, $sr);
- if ($ent === false) {
- return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
-
- /* Retrieve the user's DN. */
- $v = @ldap_get_dn($ldapcn, $ent);
- if ($v === false) {
- @ldap_free_result($sr);
- return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
- $userDN = $v;
-
- /* Retrieve the user's scripts. */
- $attrs = @ldap_get_attributes($ldapcn, $ent);
- @ldap_free_result($sr);
- if ($attrs === false) {
- return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
-
- /* Attribute can be in any case, and can have a ";binary"
- * specifier. */
- $regexp = '/^' . preg_quote($this->_params['script_attribute'], '/') .
- '(?:;.*)?$/i';
- unset($attrs['count']);
- foreach ($attrs as $name => $values) {
- if (preg_match($regexp, $name)) {
- unset($values['count']);
- return array_values($values);
- }
- }
-
- return array();
- }
-
- /**
- * Sets a script running on the backend.
- *
- * @param string $script The sieve script.
- *
- * @return mixed True on success, PEAR_Error on error.
- */
- protected function setScriptActive($script)
- {
- $ldapcn = $this->_connect();
- if (is_a($ldapcn, 'PEAR_Error')) {
- return $ldapcn;
- }
-
- $values = $this->_getScripts($ldapcn, $userDN);
- if (is_a($values, 'PEAR_Error')) {
- return $values;
- }
-
- $found = false;
- foreach ($values as $i => $value) {
- if (strpos($value, "# Sieve Filter\n") !== false) {
- if (empty($script)) {
- unset($values[$i]);
- } else {
- $values[$i] = $script;
- }
- $found = true;
- break;
- }
- }
- if (!$found && !empty($script)) {
- $values[] = $script;
- }
-
- $replace = array(Horde_String::lower($this->_params['script_attribute']) => $values);
- if (empty($values)) {
- $r = @ldap_mod_del($ldapcn, $userDN, $replace);
- } else {
- $r = @ldap_mod_replace($ldapcn, $userDN, $replace);
- }
- if (!$r) {
- return PEAR::raiseError(sprintf(_("Activating the script for \"%s\" failed: (%d) %s"),
- $userDN,
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
-
- @ldap_close($ldapcn);
- return true;
- }
-
- /**
- * Returns the content of the currently active script.
- *
- * @return string The complete ruleset of the specified user.
- */
- public function getScript()
- {
- $ldapcn = $this->_connect();
- if (is_a($ldapcn, 'PEAR_Error')) {
- return $ldapcn;
- }
-
- $values = $this->_getScripts($ldapcn, $userDN);
- if (is_a($values, 'PEAR_Error')) {
- return $values;
- }
-
- $script = '';
- foreach ($values as $value) {
- if (strpos($value, "# Sieve Filter\n") !== false) {
- $script = $value;
- break;
- }
- }
-
- @ldap_close($ldapcn);
- return $script;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Ingo_Driver_null:: Implements a null api -- useful for just testing
- * the UI and storage.
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Brent J. Nordquist <bjn@horde.org>
- * @package Ingo
- */
-
-class Ingo_Driver_null extends Ingo_Driver
-{
- /**
- * Constructor.
- */
- public function __construct($params = array())
- {
- $this->_support_shares = true;
- parent::__construct($params);
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Ingo_Driver_sivtest:: Implements the Sieve_Driver api to allow scripts to
- * be installed and set active via the Cyrus sivtest command line utility.
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- * Copyright 2004-2007 Liam Hoekenga <liamr@umich.edu>
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @author Jan Schneider <jan@horde.org>
- * @author Liam Hoekenga <liamr@umich.edu>
- * @package Ingo
- */
-class Ingo_Driver_sivtest extends Ingo_Driver
-{
- /**
- * The Net_Sieve object.
- *
- * @var Net_Sieve
- */
- protected $_sieve;
-
- /**
- * Constructor.
- */
- public function __construct($params = array())
- {
- $default_params = array(
- 'hostspec' => 'localhost',
- 'logintype' => '',
- 'port' => 2000,
- 'scriptname' => 'ingo',
- 'admin' => '',
- 'usetls' => true,
- 'command' => '',
- 'socket' => '',
- );
-
- parent::__construct(array_merge($default_params, $params));
- }
-
- /**
- * Connect to the sieve server.
- *
- * @return mixed True on success, PEAR_Error on false.
- */
- protected function _connect()
- {
- if (!empty($this->_sieve)) {
- return true;
- }
-
- $this->sivtestSocket($this->_params['username'],
- $this->_params['password'], $this->_params['hostspec']);
- if (substr(PHP_VERSION, 0, 1) == '5') {
- $domain_socket = 'unix://' . $this->_params['socket'];
- } else {
- $domain_socket = $this->_params['socket'];
- }
-
- $this->_sieve = new Net_Sieve($this->_params['username'],
- $this->_params['password'],
- $domain_socket,
- 0,
- null,
- null,
- false,
- true,
- $this->_params['usetls']);
-
- $res = $this->_sieve->getError();
- if (is_a($res, 'PEAR_Error')) {
- unset($this->_sieve);
- return $res;
- } else {
- return true;
- }
- }
-
- /**
- * Sets a script running on the backend.
- *
- * @param string $script The sieve script.
- *
- * @return mixed True on success.
- * Returns PEAR_Error on error.
- */
- public function setScriptActive($script)
- {
- $res = $this->_connect();
- if (is_a($res, 'PEAR_Error')) {
- return $res;
- }
-
- $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
- if (is_a($res, 'PEAR_ERROR')) {
- return $res;
- }
-
- return $this->_sieve->installScript($this->_params['scriptname'], $script, true);
- }
-
- /**
- * Returns the content of the currently active script.
- *
- * @return string The complete ruleset of the specified user.
- */
- public function getScript()
- {
- $res = $this->_connect();
- if (is_a($res, 'PEAR_Error')) {
- return $res;
- }
- return $this->_sieve->getScript($this->_sieve->getActive());
- }
-
- /**
- * Used to figure out which Sieve server the script will be run
- * on, and then open a GSSAPI authenticated socket to said server.
- *
- * @param string $username The username.
- * @param string $password The password.
- * @param string $hostspec The hostspec.
- *
- * @return TODO
- */
- public function sivtestSocket($username, $password, $hostspec)
- {
- $command = '';
- $error_return = '';
-
- if (strtolower($this->_params['logintype']) == 'gssapi'
- && isset($_SERVER['KRB5CCNAME'])) {
- $command .= 'KRB5CCNAME=' . $_SERVER['KRB5CCNAME'];
- }
-
- if (substr(PHP_VERSION, 0, 1) == '5') {
- $domain_socket = 'unix://' . $this->_params['socket'];
- } else {
- $domain_socket = $this->_params['socket'];
- }
-
- $command .= ' ' . $this->_params['command']
- . ' -m ' . $this->_params['logintype']
- . ' -u ' . $username
- . ' -a ' . $username
- . ' -w ' . $password
- . ' -p ' . $this->_params['port']
- . ' -X ' . $this->_params['socket']
- . ' ' . $hostspec;
-
- $conn_attempts = 0;
- while ($conn_attempts++ < 4) {
- $attempts = 0;
- if (!file_exists($this->_params['socket'])) {
- exec($command . ' > /dev/null 2>&1');
- sleep(1);
- while (!file_exists($this->_params['socket'])) {
- usleep(200000);
- if ($attempts++ > 5) {
- $error_return = ': No socket after 10 seconds of trying!';
- continue 2;
- }
- }
- }
- $socket = new Net_Socket();
- $error = $socket->connect($domain_socket, 0, true, 30);
- if (!is_a($error, 'PEAR_Error')) {
- break;
- }
-
- // We failed, break this connection.
- unlink($this->_params['socket']);
- }
-
- if (!empty($error_return)) {
- return PEAR::raiseError(_($error_return));
- }
-
- $status = $socket->getStatus();
- if (is_a($status, 'PEAR_Error') || $status['eof']) {
- return PEAR::raiseError(_('Failed to write to socket: (connection lost!)'));
- }
-
- $error = $socket->writeLine("CAPABILITY");
- if (is_a($error, 'PEAR_Error')) {
- return PEAR::raiseError(_('Failed to write to socket: ' . $error->getMessage()));
- }
-
- $result = $socket->readLine();
- if (is_a($result, 'PEAR_Error')) {
- return PEAR::raiseError(_('Failed to read from socket: ' . $error->getMessage()));
- }
-
- if (preg_match('|^bye \(referral "(sieve://)?([^"]+)|i',
- $result, $matches)) {
- $socket->disconnect();
-
- $this->sivtestSocket($username, $password, $matches[2]);
- } else {
- $socket->disconnect();
- exec($command . ' > /dev/null 2>&1');
- sleep(1);
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Ingo_Driver_timsieved:: Implements the Sieve_Driver api to allow scripts to
- * be installed and set active via a Cyrus timsieved server.
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @author Jan Schneider <jan@horde.org>
- * @package Ingo
- */
-class Ingo_Driver_timsieved extends Ingo_Driver
-{
- /**
- * The Net_Sieve object.
- *
- * @var Net_Sieve
- */
- protected $_sieve;
-
- /**
- * Constructor.
- */
- public function __construct($params = array())
- {
- $this->_support_shares = true;
-
- $default_params = array(
- 'hostspec' => 'localhost',
- 'logintype' => 'PLAIN',
- 'port' => 2000,
- 'scriptname' => 'ingo',
- 'admin' => '',
- 'usetls' => true
- );
-
- parent::__construct(array_merge($default_params, $params));
- }
-
- /**
- * Connect to the sieve server.
- *
- * @return mixed True on success, PEAR_Error on false.
- */
- public function _connect()
- {
- if (!empty($this->_sieve)) {
- return true;
- }
-
- if (empty($this->_params['admin'])) {
- $auth = $this->_params['username'];
- } else {
- $auth = $this->_params['admin'];
- }
- $this->_sieve = new Net_Sieve($auth,
- $this->_params['password'],
- $this->_params['hostspec'],
- $this->_params['port'],
- $this->_params['logintype'],
- Ingo::getUser(false),
- false,
- false,
- $this->_params['usetls']);
-
- $res = $this->_sieve->getError();
- if (is_a($res, 'PEAR_Error')) {
- unset($this->_sieve);
- return $res;
- } else {
- return true;
- }
- }
-
- /**
- * Sets a script running on the backend.
- *
- * @param string $script The sieve script.
- *
- * @return mixed True on success, PEAR_Error on error.
- */
- public function setScriptActive($script)
- {
- $res = $this->_connect();
- if (is_a($res, 'PEAR_Error')) {
- return $res;
- }
-
- if (!strlen($script)) {
- return $this->_sieve->setActive('');
- }
-
- $res = $this->_sieve->haveSpace($this->_params['scriptname'],
- strlen($script));
- if (is_a($res, 'PEAR_ERROR')) {
- return $res;
- }
-
- return $this->_sieve->installScript($this->_params['scriptname'],
- $script, true);
- }
-
- /**
- * Returns the content of the currently active script.
- *
- * @return string The complete ruleset of the specified user.
- */
- public function getScript()
- {
- $res = $this->_connect();
- if (is_a($res, 'PEAR_Error')) {
- return $res;
- }
- $active = $this->_sieve->getActive();
- if (empty($active)) {
- return '';
- }
- return $this->_sieve->getScript($active);
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Ingo_Driver_vfs:: Implements an Ingo storage driver using Horde VFS.
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Brent J. Nordquist <bjn@horde.org>
- * @author Jan Schneider <jan@horde.org>
- * @package Ingo
- */
-class Ingo_Driver_vfs extends Ingo_Driver
-{
- /**
- * Constructs a new VFS-based storage driver.
- *
- * @param array $params A hash containing driver parameters.
- */
- public function __construct($params = array())
- {
- $this->_support_shares = true;
-
- $default_params = array(
- 'hostspec' => 'localhost',
- 'port' => 21,
- 'filename' => '.ingo_filter',
- 'vfstype' => 'ftp',
- 'vfs_path' => '',
- 'vfs_forward_path' => '',
- );
-
- parent::__construct(array_merge($default_params, $params));
- }
-
- /**
- * Sets a script running on the backend.
- *
- * @param string $script The filter script
- *
- * @return mixed True on success, or PEAR_Error on failure.
- */
- public function setScriptActive($script)
- {
- $result = $this->_connect();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- if (empty($script)) {
- $result = $this->_vfs->deleteFile($this->_params['vfs_path'], $this->_params['filename']);
- } else {
- $result = $this->_vfs->writeData($this->_params['vfs_path'], $this->_params['filename'], $script, true);
- }
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- if (isset($this->_params['file_perms']) && !empty($script)) {
- $result = $this->_vfs->changePermissions($this->_params['vfs_path'], $this->_params['filename'], $this->_params['file_perms']);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
-
- // Get the backend; necessary if a .forward is needed for
- // procmail.
- $backend = Ingo::getBackend();
- if ($backend['script'] == 'procmail' && isset($backend['params']['forward_file']) && isset($backend['params']['forward_string'])) {
- if (empty($script)) {
- $result = $this->_vfs->deleteFile($this->_params['vfs_forward_path'], $backend['params']['forward_file']);
- } else {
- $result = $this->_vfs->writeData($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $backend['params']['forward_string'], true);
- }
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- if (isset($this->_params['file_perms']) && !empty($script)) {
- $result = $this->_vfs->changePermissions($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $this->_params['file_perms']);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Returns the content of the currently active script.
- *
- * @return string The complete ruleset of the specified user.
- */
- public function getScript()
- {
- $result = $this->_connect();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- return $this->_vfs->read('', $this->_params['vfs_path'] . '/' . $this->_params['filename']);
- }
-
- /**
- * Connect to the VFS server.
- *
- * @return boolean True on success, PEAR_Error on false.
- */
- protected function _connect()
- {
- /* Do variable substitution. */
- if (!empty($this->_params['vfs_path'])) {
- $user = Ingo::getUser();
- $domain = Ingo::getDomain();
- if ($_SESSION['ingo']['backend']['hordeauth'] !== 'full') {
- $pos = strpos($user, '@');
- if ($pos !== false) {
- $domain = substr($user, $pos + 1);
- $user = substr($user, 0, $pos);
- }
- }
- $this->_params['vfs_path'] = str_replace(
- array('%u', '%d', '%U'),
- array($user, $domain, $this->_params['username']),
- $this->_params['vfs_path']);
- }
-
- if (!empty($this->_vfs)) {
- return true;
- }
-
- $this->_vfs = &VFS::singleton($this->_params['vfstype'], $this->_params);
- if (is_a($this->_vfs, 'PEAR_Error')) {
- $error = $this->_vfs;
- $this->_vfs = null;
- return $error;
- } else {
- return true;
- }
- }
-
-}
static public function factory($script, $params = array())
{
$script = basename($script);
- $class = 'Ingo_Script_' . $script;
+ $class = 'Ingo_Script_' . ucfirst($script);
return class_exists($class)
? new $class($params)
--- /dev/null
+<?php
+/**
+ * The Ingo_Script_Imap:: class represents an IMAP client-side script
+ * generator.
+ *
+ * $Horde: ingo/lib/Script/imap.php,v 1.76 2009/01/19 18:10:01 mrubinsk Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Script_Imap extends Ingo_Script
+{
+ /**
+ * The list of actions allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ protected $_actions = array(
+ Ingo_Storage::ACTION_KEEP,
+ Ingo_Storage::ACTION_MOVE,
+ Ingo_Storage::ACTION_DISCARD,
+ Ingo_Storage::ACTION_MOVEKEEP
+ );
+
+ /**
+ * The categories of filtering allowed.
+ *
+ * @var array
+ */
+ protected $_categories = array(
+ Ingo_Storage::ACTION_BLACKLIST,
+ Ingo_Storage::ACTION_WHITELIST
+ );
+
+ /**
+ * The list of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ protected $_tests = array(
+ 'contains', 'not contain'
+ );
+
+ /**
+ * The types of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ protected $_types = array(
+ Ingo_Storage::TYPE_HEADER,
+ Ingo_Storage::TYPE_SIZE,
+ Ingo_Storage::TYPE_BODY
+ );
+
+ /**
+ * Does the driver support setting IMAP flags?
+ *
+ * @var boolean
+ */
+ protected $_supportIMAPFlags = true;
+
+ /**
+ * Does the driver support the stop-script option?
+ *
+ * @var boolean
+ */
+ protected $_supportStopScript = true;
+
+ /**
+ * This driver can perform on demand filtering (in fact, that is all
+ * it can do).
+ *
+ * @var boolean
+ */
+ protected $_ondemand = true;
+
+ /**
+ * The API to use for IMAP functions.
+ *
+ * @var Ingo_Script_Imap_Api
+ */
+ protected $_api;
+
+ /**
+ * Perform the filtering specified in the rules.
+ *
+ * @param array $params The parameter array. It MUST contain:
+ * <pre>
+ * 'mailbox' - The name of the mailbox to filter.
+ * </pre>
+ *
+ * @return boolean True if filtering performed, false if not.
+ */
+ public function perform($params)
+ {
+ if (empty($params['api'])) {
+ $this->_api = Ingo_Script_Imap_Api::factory('live', $params);
+ } else {
+ $this->_api = &$params['api'];
+ }
+
+ /* Indices that will be ignored by subsequent rules. */
+ $ignore_ids = array();
+
+ /* Only do filtering if:
+ 1. We have not done filtering before -or-
+ 2. The mailbox has changed -or-
+ 3. The rules have changed. */
+ $cache = $this->_api->getCache();
+ if (($cache !== false) && ($cache == $_SESSION['ingo']['change'])) {
+ return true;
+ }
+
+ /* Grab the rules list. */
+ $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
+
+ /* Should we filter only [un]seen messages? */
+ $seen_flag = $GLOBALS['prefs']->getValue('filter_seen');
+
+ /* Should we use detailed notification messages? */
+ $detailmsg = $GLOBALS['prefs']->getValue('show_filter_msg');
+
+ /* Parse through the rules, one-by-one. */
+ foreach ($filters->getFilterList() as $rule) {
+ /* Check to make sure this is a valid rule and that the rule is
+ not disabled. */
+ if (!$this->_validRule($rule['action']) ||
+ !empty($rule['disable'])) {
+ continue;
+ }
+
+ $search_array = array();
+
+ switch ($rule['action']) {
+ case Ingo_Storage::ACTION_BLACKLIST:
+ case Ingo_Storage::ACTION_WHITELIST:
+ $bl_folder = null;
+
+ if ($rule['action'] == Ingo_Storage::ACTION_BLACKLIST) {
+ $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+ $addr = $blacklist->getBlacklist();
+ $bl_folder = $blacklist->getBlacklistFolder();
+ } else {
+ $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
+ $addr = $whitelist->getWhitelist();
+ }
+
+ /* If list is empty, move on. */
+ if (empty($addr)) {
+ continue;
+ }
+
+ $query = new Horde_Imap_Client_Search_Query();
+ foreach ($addr as $val) {
+ $ob = new Horde_Imap_Client_Search_Query();
+ $ob->flag('\\deleted', false);
+ if ($seen_flag == Ingo_Script::FILTER_UNSEEN) {
+ $ob->flag('\\seen', false);
+ } elseif ($seen_flag == Ingo_Script::FILTER_SEEN) {
+ $ob->flag('\\seen', true);
+ }
+ $ob->headerText('from', $val);
+ $search_array[] = $ob;
+ }
+ $query->orSearch($search_array);
+ $indices = $this->_api->search($query);
+
+ /* Remove any indices that got in there by way of partial
+ * address match. */
+ $msgs = $this->_api->fetchEnvelope($indices);
+ foreach ($msgs as $k => $v) {
+ $from_addr = Horde_Mime_Address::bareAddress(Horde_Mime_Address::addrArray2String($v['envelope']['from']));
+ $found = false;
+ foreach ($addr as $val) {
+ if (strtolower($from_addr) == strtolower($val)) {
+ $found = true;
+ }
+ }
+ if (!$found) {
+ $indices = array_diff($indices, array($k));
+ }
+ }
+
+ if ($rule['action'] == Ingo_Storage::ACTION_BLACKLIST) {
+ $indices = array_diff($indices, $ignore_ids);
+ if (!empty($indices)) {
+ if (!empty($bl_folder)) {
+ $this->_api->moveMessages($indices, $bl_folder);
+ } else {
+ $this->_api->deleteMessages($indices);
+ }
+ $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) that matched the blacklist were deleted."), count($indices)), 'horde.message');
+ }
+ } else {
+ $ignore_ids = $indices;
+ }
+ break;
+
+ case Ingo_Storage::ACTION_KEEP:
+ case Ingo_Storage::ACTION_MOVE:
+ case Ingo_Storage::ACTION_DISCARD:
+ $query = new Horde_Imap_Client_Search_Query();
+ foreach ($rule['conditions'] as $val) {
+ $ob = new Horde_Imap_Client_Search_Query();
+ $ob->flag('\\deleted', false);
+ if ($seen_flag == Ingo_Script::FILTER_UNSEEN) {
+ $ob->flag('\\seen', false);
+ } elseif ($seen_flag == Ingo_Script::FILTER_SEEN) {
+ $ob->flag('\\seen', true);
+ }
+ if (!empty($val['type']) &&
+ ($val['type'] == Ingo_Storage::TYPE_SIZE)) {
+ $ob->size($val['value'], ($val['match'] == 'greater than'));
+ } elseif (!empty($val['type']) &&
+ ($val['type'] == Ingo_Storage::TYPE_BODY)) {
+ $ob->text($val['value'], true, ($val['match'] == 'not contain'));
+ } else {
+ $ob->headerText($val['field'], $val['value'], ($val['match'] == 'not contain'));
+ }
+ $search_array[] = $ob;
+ }
+
+ if ($rule['combine'] == Ingo_Storage::COMBINE_ALL) {
+ $query->andSearch($search_array);
+ } else {
+ $query->orSearch($search_array);
+ }
+
+ $indices = $this->_api->search($query);
+
+ if (($indices = array_diff($indices, $ignore_ids))) {
+ if ($rule['stop']) {
+ /* If the stop action is set, add these
+ * indices to the list of ids that will be
+ * ignored by subsequent rules. */
+ $ignore_ids = array_unique($indices + $ignore_ids);
+ }
+
+ /* Set the flags. */
+ if (!empty($rule['flags']) &&
+ ($rule['action'] != Ingo_Storage::ACTION_DISCARD)) {
+ $flags = array();
+ if ($rule['flags'] & Ingo_Storage::FLAG_ANSWERED) {
+ $flags[] = '\\answered';
+ }
+ if ($rule['flags'] & Ingo_Storage::FLAG_DELETED) {
+ $flags[] = '\\deleted';
+ }
+ if ($rule['flags'] & Ingo_Storage::FLAG_FLAGGED) {
+ $flags[] = '\\flagged';
+ }
+ if ($rule['flags'] & Ingo_Storage::FLAG_SEEN) {
+ $flags[] = '\\seen';
+ }
+ $this->_api->setMessageFlags($indices, implode(' ', $flags));
+ }
+
+ if ($rule['action'] == Ingo_Storage::ACTION_KEEP) {
+ /* Add these indices to the ignore list. */
+ $ignore_ids = array_unique($indices + $ignore_ids);
+ } elseif ($rule['action'] == Ingo_Storage::ACTION_MOVE) {
+ /* We need to grab the overview first. */
+ if ($detailmsg) {
+ $overview = $this->_api->fetchEnvelope($indices);
+ }
+
+ /* Move the messages to the requested mailbox. */
+ $this->_api->moveMessages($indices, $rule['action-value']);
+
+ /* Display notification message(s). */
+ if ($detailmsg) {
+ foreach ($overview as $msg) {
+ $GLOBALS['notification']->push(
+ sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been moved to the folder \"%s\"."),
+ !empty($msg['envelope']['subject']) ? Horde_Mime::decode($msg['envelope']['subject'], Horde_Nls::getCharset()) : _("[No Subject]"),
+ !empty($msg['envelope']['from']) ? Horde_Mime::decode(Horde_Mime_Address::addrArray2String($msg['envelope']['from']), Horde_Nls::getCharset()) : _("[No Sender]"),
+ Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())),
+ 'horde.message');
+ }
+ } else {
+ $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been moved to the folder \"%s\"."),
+ count($indices),
+ Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())), 'horde.message');
+ }
+ } elseif ($rule['action'] == Ingo_Storage::ACTION_DISCARD) {
+ /* We need to grab the overview first. */
+ if ($detailmsg) {
+ $overview = $this->_api->fetchEnvelope($indices);
+ }
+
+ /* Delete the messages now. */
+ $this->_api->deleteMessages($indices);
+
+ /* Display notification message(s). */
+ if ($detailmsg) {
+ foreach ($overview as $msg) {
+ $GLOBALS['notification']->push(
+ sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been deleted."),
+ !empty($msg['envelope']['subject']) ? Horde_Mime::decode($msg['envelope']['subject'], Horde_Nls::getCharset()) : _("[No Subject]"),
+ !empty($msg['envelope']['from']) ? Horde_Mime::decode($msg['envelope']['from'], Horde_Nls::getCharset()) : _("[No Sender]")),
+ 'horde.message');
+ }
+ } else {
+ $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been deleted."), count($indices)), 'horde.message');
+ }
+ } elseif ($rule['action'] == Ingo_Storage::ACTION_MOVEKEEP) {
+ /* Copy the messages to the requested mailbox. */
+ $this->_api->copyMessages($indices,
+ $rule['action-value']);
+
+ /* Display notification message(s). */
+ if ($detailmsg) {
+ $overview = $this->_api->fetchEnvelope($indices);
+ foreach ($overview as $msg) {
+ $GLOBALS['notification']->push(
+ sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been copied to the folder \"%s\"."),
+ !empty($msg['envelope']['subject']) ? Horde_Mime::decode($msg['envelope']['subject'], Horde_Nls::getCharset()) : _("[No Subject]"),
+ !empty($msg['envelope']['from']) ? Horde_Mime::decode($msg['envelope']['from'], Horde_Nls::getCharset()) : _("[No Sender]"),
+ Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())),
+ 'horde.message');
+ }
+ } else {
+ $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been copied to the folder \"%s\"."), count($indices), Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())), 'horde.message');
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ /* Set cache flag. */
+ $this->_api->storeCache($_SESSION['ingo']['change']);
+
+ return true;
+ }
+
+ /**
+ * Is the apply() function available?
+ *
+ * @return boolean True if apply() is available, false if not.
+ */
+ public function canApply()
+ {
+ if ($this->performAvailable() &&
+ $GLOBALS['registry']->hasMethod('mail/server')) {
+ $server = $GLOBALS['registry']->call('mail/server');
+ return ($server['protocol'] == 'imap');
+ }
+
+ return false;
+ }
+
+ /**
+ * Apply the filters now.
+ *
+ * @return boolean See perform().
+ */
+ public function apply()
+ {
+ return $this->canApply()
+ ? $this->perform(array('mailbox' => 'INBOX'))
+ : false;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Ingo_Script_Imap_Api:: is the base driver class for Ingo_Script_Imap::.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Script_Imap_Api
+{
+ /**
+ * TODO
+ */
+ protected $_params;
+
+ /**
+ * TODO
+ */
+ static public function factory($type, $params)
+ {
+ $class = 'Ingo_Script_Imap_' . ucfirst($type);
+ return new $class($params);
+ }
+
+ /**
+ * TODO
+ */
+ public function __construct($params = array())
+ {
+ $this->_params = $params;
+ }
+
+ /**
+ * TODO
+ */
+ public function deleteMessages($indices)
+ {
+ return PEAR::raiseError('Not implemented.');
+ }
+
+ /**
+ * TODO
+ */
+ public function moveMessages($indices, $folder)
+ {
+ return PEAR::raiseError('Not implemented.');
+ }
+
+ /**
+ * TODO
+ */
+ public function copyMessages($indices, $folder)
+ {
+ return PEAR::raiseError('Not implemented.');
+ }
+
+ /**
+ * TODO
+ */
+ public function setMessageFlags($indices, $flags)
+ {
+ return PEAR::raiseError('Not implemented.');
+ }
+
+ /**
+ * TODO
+ */
+ public function fetchEnvelope($indices)
+ {
+ return PEAR::raiseError('Not implemented.');
+ }
+
+ /**
+ * TODO
+ */
+ public function search($query)
+ {
+ return PEAR::raiseError('Not implemented.');
+ }
+
+ /**
+ * TODO
+ */
+ public function getCache()
+ {
+ return false;
+ }
+
+ /**
+ * TODO
+ */
+ public function storeCache($timestamp)
+ {
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Ingo_Script_Imap_Live:: driver.
+ *
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Jason M. Felice <jason.m.felice@gmail.com>
+ * @author Michael Slusarz <slusarz@curecanti.org>
+ * @package Ingo
+ */
+class Ingo_Script_Imap_Live extends Ingo_Script_Imap_Api
+{
+ /**
+ */
+ public function deleteMessages($indices)
+ {
+ return $GLOBALS['registry']->hasMethod('mail/deleteMessages')
+ ? $GLOBALS['registry']->call('mail/deleteMessages', array($this->_params['mailbox'], $indices))
+ : false;
+ }
+
+ /**
+ */
+ public function moveMessages($indices, $folder)
+ {
+ return $GLOBALS['registry']->hasMethod('mail/moveMessages')
+ ? $GLOBALS['registry']->call('mail/moveMessages', array($this->_params['mailbox'], $indices, $folder))
+ : false;
+ }
+
+ /**
+ */
+ public function copyMessages($indices, $folder)
+ {
+ return $GLOBALS['registry']->hasMethod('mail/copyMessages')
+ ? $GLOBALS['registry']->call('mail/copyMessages', array($this->_params['mailbox'], $indices, $folder))
+ : false;
+ }
+
+ /**
+ */
+ public function setMessageFlags($indices, $flags)
+ {
+ return $GLOBALS['registry']->hasMethod('mail/flagMessages')
+ ? $GLOBALS['registry']->call('mail/flagMessages', array($this->_params['mailbox'], $indices, $flags, true))
+ : false;
+ }
+
+ /**
+ */
+ public function fetchEnvelope($indices)
+ {
+ return $GLOBALS['registry']->hasMethod('mail/msgEnvelope')
+ ? $GLOBALS['registry']->call('mail/msgEnvelope', array($this->_params['mailbox'], $indices))
+ : false;
+ }
+
+ /**
+ */
+ public function search($query)
+ {
+ return $GLOBALS['registry']->hasMethod('mail/searchMailbox')
+ ? $GLOBALS['registry']->call('mail/searchMailbox', array($this->_params['mailbox'], $query))
+ : false;
+ }
+
+ /**
+ */
+ public function getCache()
+ {
+ if (empty($_SESSION['ingo']['imapcache'][$this->_params['mailbox']])) {
+ return false;
+ }
+ $ptr = &$_SESSION['ingo']['imapcache'][$this->_params['mailbox']];
+
+ if ($this->_cacheId() != $ptr['id']) {
+ $ptr = array();
+ return false;
+ }
+
+ return $ptr['ts'];
+ }
+
+ /**
+ */
+ public function storeCache($timestamp)
+ {
+ if (!isset($_SESSION['ingo']['imapcache'])) {
+ $_SESSION['ingo']['imapcache'] = array();
+ }
+
+ $_SESSION['ingo']['imapcache'][$this->_params['mailbox']] = array(
+ 'id' => $this->_cacheId(),
+ 'ts' => $timestamp
+ );
+ }
+
+ /**
+ */
+ protected function _cacheId()
+ {
+ return $GLOBALS['registry']->hasMethod('mail/mailboxCacheId')
+ ? $GLOBALS['registry']->call('mail/mailboxCacheId', array($this->_params['mailbox']))
+ : time();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * TODO
+ */
+class Ingo_Script_Imap_Mock extends Ingo_Script_Imap_Api
+{
+ /**
+ * TODO
+ */
+ protected $_fixtures = array();
+
+ /**
+ * TODO
+ */
+ protected $_folders = array();
+
+ /**
+ * TODO
+ */
+ public function loadFixtures($dir)
+ {
+ $this->_fixtures = array();
+
+ $dh = opendir($dir);
+ while (($dent = readdir($dh)) !== false) {
+ if (!in_array($dent, array('.', '..'))) {
+ $this->_fixtures[$dent] = Horde_Mime_Part::parseHeaders(file_get_contents($dir . '/' . $dent));
+ }
+ }
+ closedir($dh);
+
+ $i = 0;
+ foreach (array_keys($this->_fixtures) as $key) {
+ $this->_folders['INBOX'][] = array('uid' => ++$i,
+ 'fixture' => $key,
+ 'deleted' => false);
+ }
+ }
+
+ /**
+ * TODO
+ */
+ public function hasMessage($fixture, $folder = 'INBOX')
+ {
+ if (empty($this->_folders[$folder])) {
+ return false;
+ }
+ foreach ($this->_folders[$folder] as $message) {
+ if ($message['fixture'] == $fixture) {
+ return !$message['deleted'];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * TODO
+ */
+ public function search(&$query)
+ {
+ $result = array();
+ foreach ($this->_folders['INBOX'] as $message) {
+ if ($message['deleted']) {
+ continue;
+ }
+ if ($query->matches($this->_fixtures[$message['fixture']])) {
+ $result[] = $message['uid'];
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * TODO
+ */
+ public function deleteMessages($indices)
+ {
+ foreach (array_keys($this->_folders['INBOX']) as $i) {
+ if (in_array($this->_folders['INBOX'][$i]['uid'], $indices)) {
+ unset($this->_folders['INBOX'][$i]);
+ }
+ }
+
+ // Force renumbering
+ $this->_folders['INBOX'] = array_merge($this->_folders['INBOX'], array());
+ }
+
+ /**
+ * TODO
+ */
+ public function moveMessages($indices, $folder)
+ {
+ foreach (array_keys($this->_folders['INBOX']) as $i) {
+ if (in_array($this->_folders['INBOX'][$i]['uid'], $indices)) {
+ $this->_folders[$folder][] = $this->_folders['INBOX'][$i];
+ }
+ }
+ return $this->deleteMessages($indices);
+ }
+
+ /**
+ * TODO
+ */
+ public function fetchEnvelope($indices)
+ {
+ $result = array();
+
+ foreach ($indices as $uid) {
+ foreach (array_keys($this->_folders['INBOX']) as $i) {
+ if ($this->_folders['INBOX'][$i]['uid'] == $uid) {
+ $fixture = $this->_fixtures[$this->_folders['INBOX'][$i]['fixture']];
+ $result[] = array(
+ 'envelope' => array(
+ 'from' => $fixture->getValue('from'),
+ 'uid' => $uid
+ )
+ );
+ }
+ }
+ }
+
+ return $result;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Ingo_Script_Maildrop:: class represents a maildrop script generator.
+ *
+ * Copyright 2005-2007 Matt Weyland <mathias@weyland.ch>
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Matt Weyland <mathias@weyland.ch>
+ * @package Ingo
+ */
+
+/**
+ * Additional storage action since maildrop does not support the
+ * "c-flag" as in procmail.
+ */
+define('MAILDROP_STORAGE_ACTION_STOREANDFORWARD', 100);
+
+/**
+ */
+class Ingo_Script_Maildrop extends Ingo_Script {
+
+ /**
+ * The list of actions allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_actions = array(
+ Ingo_Storage::ACTION_KEEP,
+ Ingo_Storage::ACTION_MOVE,
+ Ingo_Storage::ACTION_DISCARD,
+ Ingo_Storage::ACTION_REDIRECT,
+ Ingo_Storage::ACTION_REDIRECTKEEP,
+ Ingo_Storage::ACTION_REJECT,
+ );
+
+ /**
+ * The categories of filtering allowed.
+ *
+ * @var array
+ */
+ var $_categories = array(
+ Ingo_Storage::ACTION_BLACKLIST,
+ Ingo_Storage::ACTION_WHITELIST,
+ Ingo_Storage::ACTION_VACATION,
+ Ingo_Storage::ACTION_FORWARD,
+ Ingo_Storage::ACTION_SPAM,
+ );
+
+ /**
+ * The types of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_types = array(
+ Ingo_Storage::TYPE_HEADER,
+ );
+
+ /**
+ * The list of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_tests = array(
+ 'contains', 'not contain',
+ 'is', 'not is',
+ 'begins with','not begins with',
+ 'ends with', 'not ends with',
+ 'regex',
+ 'matches', 'not matches',
+ 'exists', 'not exist',
+ 'less than', 'less than or equal to',
+ 'equal', 'not equal',
+ 'greater than', 'greater than or equal to',
+ );
+
+ /**
+ * Can tests be case sensitive?
+ *
+ * @var boolean
+ */
+ var $_casesensitive = true;
+
+ /**
+ * Does the driver support the stop-script option?
+ *
+ * @var boolean
+ */
+ var $_supportStopScript = false;
+
+ /**
+ * Does the driver require a script file to be generated?
+ *
+ * @var boolean
+ */
+ var $_scriptfile = true;
+
+ /**
+ * The recipes that make up the code.
+ *
+ * @var array
+ */
+ var $_recipes = array();
+
+ /**
+ * Returns a script previously generated with generate().
+ *
+ * @return string The maildrop script.
+ */
+ function toCode()
+ {
+ $code = '';
+ foreach ($this->_recipes as $item) {
+ $code .= $item->generate() . "\n";
+ }
+ return rtrim($code);
+ }
+
+ /**
+ * Generates the maildrop script to do the filtering specified in
+ * the rules.
+ *
+ * @return string The maildrop script.
+ */
+ function generate()
+ {
+ $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
+
+ $this->addItem(new Maildrop_Comment(_("maildrop script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
+
+ /* Add variable information, if present. */
+ if (!empty($this->_params['variables']) &&
+ is_array($this->_params['variables'])) {
+ foreach ($this->_params['variables'] as $key => $val) {
+ $this->addItem(new Maildrop_Variable(array('name' => $key, 'value' => $val)));
+ }
+ }
+
+ foreach ($filters->getFilterList() as $filter) {
+ switch ($filter['action']) {
+ case Ingo_Storage::ACTION_BLACKLIST:
+ $this->generateBlacklist(!empty($filter['disable']));
+ break;
+
+ case Ingo_Storage::ACTION_WHITELIST:
+ $this->generateWhitelist(!empty($filter['disable']));
+ break;
+
+ case Ingo_Storage::ACTION_FORWARD:
+ $this->generateForward(!empty($filter['disable']));
+ break;
+
+ case Ingo_Storage::ACTION_VACATION:
+ $this->generateVacation(!empty($filter['disable']));
+ break;
+
+ case Ingo_Storage::ACTION_SPAM:
+ $this->generateSpamfilter(!empty($filter['disable']));
+ break;
+
+ default:
+ if (in_array($filter['action'], $this->_actions)) {
+ /* Create filter if using AND. */
+ $recipe = new Maildrop_Recipe($filter, $this->_params);
+ foreach ($filter['conditions'] as $condition) {
+ $recipe->addCondition($condition);
+ }
+ $this->addItem(new Maildrop_Comment($filter['name'], !empty($filter['disable']), true));
+ $this->addItem($recipe);
+ }
+ }
+ }
+
+ return $this->toCode();
+ }
+
+ /**
+ * Generates the maildrop script to handle the blacklist specified in
+ * the rules.
+ *
+ * @param boolean $disable Disable the blacklist?
+ */
+ function generateBlacklist($disable = false)
+ {
+ $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+ $bl_addr = $blacklist->getBlacklist();
+ $bl_folder = $blacklist->getBlacklistFolder();
+
+ $bl_type = (empty($bl_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
+
+ if (!empty($bl_addr)) {
+ $this->addItem(new Maildrop_Comment(_("Blacklisted Addresses"), $disable, true));
+ $params = array('action-value' => $bl_folder,
+ 'action' => $bl_type,
+ 'disable' => $disable);
+
+ foreach ($bl_addr as $address) {
+ if (!empty($address)) {
+ $recipe = new Maildrop_Recipe($params, $this->_params);
+ $recipe->addCondition(array('field' => 'From', 'value' => $address));
+ $this->addItem($recipe);
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates the maildrop script to handle the whitelist specified in
+ * the rules.
+ *
+ * @param boolean $disable Disable the whitelist?
+ */
+ function generateWhitelist($disable = false)
+ {
+ $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
+ $wl_addr = $whitelist->getWhitelist();
+
+ if (!empty($wl_addr)) {
+ $this->addItem(new Maildrop_Comment(_("Whitelisted Addresses"), $disable, true));
+ foreach ($wl_addr as $address) {
+ if (!empty($address)) {
+ $recipe = new Maildrop_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
+ $recipe->addCondition(array('field' => 'From', 'value' => $address));
+ $this->addItem($recipe);
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates the maildrop script to handle mail forwards.
+ *
+ * @param boolean $disable Disable forwarding?
+ */
+ function generateForward($disable = false)
+ {
+ $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
+ $addresses = $forward->getForwardAddresses();
+
+ if (!empty($addresses)) {
+ $this->addItem(new Maildrop_Comment(_("Forwards"), $disable, true));
+ $params = array('action' => Ingo_Storage::ACTION_FORWARD,
+ 'action-value' => $addresses,
+ 'disable' => $disable);
+ if ($forward->getForwardKeep()) {
+ $params['action'] = MAILDROP_STORAGE_ACTION_STOREANDFORWARD;
+ }
+ $recipe = new Maildrop_Recipe($params, $this->_params);
+ $recipe->addCondition(array('field' => 'From', 'value' => ''));
+ $this->addItem($recipe);
+ }
+ }
+
+ /**
+ * Generates the maildrop script to handle vacation messages.
+ *
+ * @param boolean $disable Disable forwarding?
+ */
+ function generateVacation($disable = false)
+ {
+ $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
+ $addresses = $vacation->getVacationAddresses();
+ $actionval = array('addresses' => $addresses,
+ 'subject' => $vacation->getVacationSubject(),
+ 'days' => $vacation->getVacationDays(),
+ 'reason' => $vacation->getVacationReason(),
+ 'ignorelist' => $vacation->getVacationIgnorelist(),
+ 'excludes' => $vacation->getVacationExcludes(),
+ 'start' => $vacation->getVacationStart(),
+ 'end' => $vacation->getVacationEnd());
+
+ if (!empty($addresses)) {
+ $this->addItem(new Maildrop_Comment(_("Vacation"), $disable, true));
+ $params = array('action' => Ingo_Storage::ACTION_VACATION,
+ 'action-value' => $actionval,
+ 'disable' => $disable);
+ $recipe = new Maildrop_Recipe($params, $this->_params);
+ $this->addItem($recipe);
+ }
+ }
+
+ /**
+ * Generates the maildrop script to handle spam as identified by SpamAssassin
+ *
+ * @param boolean $disable Disable the spam-filter?
+ */
+ function generateSpamfilter($disable = false)
+ {
+ $spam = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
+ if ($spam == false) {
+ return;
+ }
+
+ $spam_folder = $spam->getSpamFolder();
+ $spam_action = (empty($spam_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
+
+ $this->addItem(new Maildrop_Comment(_("Spam Filter"), $disable, true));
+
+ $params = array('action-value' => $spam_folder,
+ 'action' => $spam_action,
+ 'disable' => $disable);
+ $recipe = new Maildrop_Recipe($params, $this->_params);
+ if ($GLOBALS['conf']['spam']['compare'] == 'numeric') {
+ $recipe->addCondition(array('match' => 'greater than or equal to',
+ 'field' => $GLOBALS['conf']['spam']['header'],
+ 'value' => $spam->getSpamLevel()));
+ } elseif ($GLOBALS['conf']['spam']['compare'] == 'string') {
+ $recipe->addCondition(array('match' => 'contains',
+ 'field' => $GLOBALS['conf']['spam']['header'],
+ 'value' => str_repeat($GLOBALS['conf']['spam']['char'], $spam->getSpamLevel())));
+ }
+
+ $this->addItem($recipe);
+ }
+
+ /**
+ * Adds an item to the recipe list.
+ *
+ * @param object $item The item to add to the recipe list.
+ * The object should have a generate() function.
+ */
+ function addItem($item)
+ {
+ $this->_recipes[] = $item;
+ }
+
+}
+
+/**
+ * The Maildrop_Comment:: class represents a maildrop comment. This is
+ * a pretty simple class, but it makes the code in Ingo_Script_Maildrop::
+ * cleaner as it provides a generate() function and can be added to the
+ * recipe list the same way as a recipe can be.
+ *
+ * @author Matt Weyland <mathias@weyland.ch>
+ * @since Ingo 1.1
+ * @package Ingo
+ */
+class Maildrop_Comment {
+
+ /**
+ * The comment text.
+ *
+ * @var string
+ */
+ var $_comment = '';
+
+ /**
+ * Constructs a new maildrop comment.
+ *
+ * @param string $comment Comment to be generated.
+ * @param boolean $disable Output 'DISABLED' comment?
+ * @param boolean $header Output a 'header' comment?
+ */
+ function Maildrop_Comment($comment, $disable = false, $header = false)
+ {
+ if ($disable) {
+ $comment = _("DISABLED: ") . $comment;
+ }
+
+ if ($header) {
+ $this->_comment .= "##### $comment #####";
+ } else {
+ $this->_comment .= "# $comment";
+ }
+ }
+
+ /**
+ * Returns the comment stored by this object.
+ *
+ * @return string The comment stored by this object.
+ */
+ function generate()
+ {
+ return $this->_comment;
+ }
+
+}
+
+/**
+ * The Maildrop_Recipe:: class represents a maildrop recipe.
+ *
+ * @author Matt Weyland <mathias@weyland.ch>
+ * @since Ingo 1.1
+ * @package Ingo
+ */
+class Maildrop_Recipe {
+
+ var $_action = array();
+ var $_conditions = array();
+ var $_disable = '';
+ var $_flags = '';
+ var $_params = array();
+ var $_combine = '';
+ var $_valid = true;
+
+ var $_operators = array(
+ 'less than' => '<',
+ 'less than or equal to' => '<=',
+ 'equal' => '==',
+ 'not equal' => '!=',
+ 'greater than' => '>',
+ 'greater than or equal to' => '>=',
+ );
+
+ /**
+ * Constructs a new maildrop recipe.
+ *
+ * @param array $params Array of parameters.
+ * REQUIRED FIELDS:
+ * 'action'
+ * OPTIONAL FIELDS:
+ * 'action-value' (only used if the
+ * 'action' requires it)
+ * @param array $scriptparams Array of parameters passed to
+ * Ingo_Script_Maildrop::.
+ */
+ function Maildrop_Recipe($params = array(), $scriptparams = array())
+ {
+ $this->_disable = !empty($params['disable']);
+ $this->_params = $scriptparams;
+ $this->_action[] = 'exception {';
+
+ switch ($params['action']) {
+ case Ingo_Storage::ACTION_KEEP:
+ $this->_action[] = ' to "${DEFAULT}"';
+ break;
+
+ case Ingo_Storage::ACTION_MOVE:
+ $this->_action[] = ' to ' . $this->maildropPath($params['action-value']);
+ break;
+
+ case Ingo_Storage::ACTION_DISCARD:
+ $this->_action[] = ' exit';
+ break;
+
+ case Ingo_Storage::ACTION_REDIRECT:
+ $this->_action[] = ' to "! ' . $params['action-value'] . '"';
+ break;
+
+ case Ingo_Storage::ACTION_REDIRECTKEEP:
+ $this->_action[] = ' cc "! ' . $params['action-value'] . '"';
+ $this->_action[] = ' to "${DEFAULT}"';
+ break;
+
+ case Ingo_Storage::ACTION_REJECT:
+ $this->_action[] = ' EXITCODE=77'; # EX_NOPERM (permanent failure)
+ $this->_action[] = ' echo "5.7.1 ' . $params['action-value'] . '"';
+ $this->_action[] = ' exit';
+ break;
+
+ case Ingo_Storage::ACTION_VACATION:
+ $from = '';
+ foreach ($params['action-value']['addresses'] as $address) {
+ $from = $address;
+ }
+
+ /**
+ * @TODO
+ *
+ * Exclusion and listfilter
+ */
+ $exclude = '';
+ foreach ($params['action-value']['excludes'] as $address) {
+ $exclude .= $address . ' ';
+ }
+
+ $start = strftime($params['action-value']['start']);
+ if ($start === false) {
+ $start = 0;
+ }
+ $end = strftime($params['action-value']['end']);
+ if ($end === false) {
+ $end = 0;
+ }
+ $days = strftime($params['action-value']['days']);
+ if ($days === false) {
+ // Set to same value as $_days in ingo/lib/Storage.php
+ $days = 7;
+ }
+
+ // Writing vacation.msg file
+ $reason = Horde_Mime::encode($params['action-value']['reason'], Horde_Nls::getCharset());
+ $driver = Ingo::getDriver();
+ $driver->_connect();
+ $result = $driver->_vfs->writeData($driver->_params['vfs_path'], 'vacation.msg', $reason, true);
+
+ // Rule : Do not send responses to bulk or list messages
+ if ($params['action-value']['ignorelist'] == 1) {
+ $params['combine'] = Ingo_Storage::COMBINE_ALL;
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Precedence: (bulk|list|junk)/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<#@\[\]>/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<>/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^From:.*MAILER-DAEMON/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-ClamAV-Notice-Flag: *YES/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Content-Type:.*message\/delivery-status/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery Status Notification/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Undelivered Mail Returned to Sender/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery failure/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Message delay/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail Delivery Subsystem/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail System Error.*Returned Mail/'));
+ $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-Spam-Flag: YES/ '));
+ } else {
+ $this->addCondition(array('field' => 'From', 'value' => ''));
+ }
+
+ // Rule : Start/End of vacation
+ if (($start != 0) && ($end !== 0)) {
+ $this->_action[] = ' flock "vacation.lock" {';
+ $this->_action[] = ' current_time=time';
+ $this->_action[] = ' if ( \ ';
+ $this->_action[] = ' ($current_time >= ' . $start . ') && \ ';
+ $this->_action[] = ' ($current_time <= ' . $end . ')) ';
+ $this->_action[] = ' {';
+ }
+ $this->_action[] = " cc \"| mailbot -D " . $params['action-value']['days'] . " -c '" . Horde_Nls::getCharset() . "' -t \$HOME/vacation.msg -d \$HOME/vacation -A 'From: $from' -s '" . Horde_Mime::encode($params['action-value']['subject'], Horde_Nls::getCharset()) . "' /usr/sbin/sendmail -t \"";
+ if (($start != 0) && ($end !== 0)) {
+ $this->_action[] = ' }';
+ $this->_action[] = ' }';
+ }
+
+ break;
+
+ case Ingo_Storage::ACTION_FORWARD:
+ case MAILDROP_STORAGE_ACTION_STOREANDFORWARD:
+ foreach ($params['action-value'] as $address) {
+ if (!empty($address)) {
+ $this->_action[] = ' cc "! ' . $address . '"';
+ }
+ }
+
+ /* The 'to' must be the last action, because maildrop
+ * stops processing after it. */
+ if ($params['action'] == MAILDROP_STORAGE_ACTION_STOREANDFORWARD) {
+ $this->_action[] = ' to "${DEFAULT}"';
+ } else {
+ $this->_action[] = ' exit';
+ }
+ break;
+
+ default:
+ $this->_valid = false;
+ break;
+ }
+
+ $this->_action[] = '}';
+
+ if (isset($params['combine']) &&
+ ($params['combine'] == Ingo_Storage::COMBINE_ALL)) {
+ $this->_combine = '&& ';
+ } else {
+ $this->_combine = '|| ';
+ }
+ }
+
+ /**
+ * Adds a flag to the recipe.
+ *
+ * @param string $flag String of flags to append to the current flags.
+ */
+ function addFlag($flag)
+ {
+ $this->_flags .= $flag;
+ }
+
+ /**
+ * Adds a condition to the recipe.
+ *
+ * @param optonal array $condition Array of parameters. Required keys
+ * are 'field' and 'value'. 'case' is
+ * an optional keys.
+ */
+ function addCondition($condition = array())
+ {
+ $flag = (!empty($condition['case'])) ? 'D' : '';
+ if (empty($this->_conditions)) {
+ $this->addFlag($flag);
+ }
+
+ $string = '';
+ $extra = '';
+
+ $match = (isset($condition['match'])) ? $condition['match'] : null;
+ // negate tests starting with 'not ', except 'not equals', which simply uses the != operator
+ if ($match != 'not equal' && substr($match, 0, 4) == 'not ') {
+ $string .= '! ';
+ }
+
+ // convert 'field' to PCRE pattern matching
+ if (strpos($condition['field'], ',') == false) {
+ $string .= '/^' . $condition['field'] . ':\\s*';
+ } else {
+ $string .= '/^(' . str_replace(',', '|', $condition['field']) . '):\\s*';
+ }
+
+ switch ($match) {
+ case 'not regex':
+ case 'regex':
+ $string .= $condition['value'] . '/:h';
+ break;
+
+ case 'filter':
+ $string = $condition['value'];
+ break;
+
+ case 'exists':
+ case 'not exist':
+ // Just run a match for the header name
+ $string .= '/:h';
+ break;
+
+ case 'less than or equal to':
+ case 'less than':
+ case 'equal':
+ case 'not equal':
+ case 'greater than or equal to':
+ case 'greater than':
+ $string .= '(\d+(\.\d+)?)/:h';
+ $extra = ' && $MATCH1 ' . $this->_operators[$match] . ' ' . (int)$condition['value'];
+ break;
+
+ case 'begins with':
+ case 'not begins with':
+ $string .= preg_quote($condition['value'], '/') . '/:h';
+ break;
+
+ case 'ends with':
+ case 'not ends with':
+ $string .= '.*' . preg_quote($condition['value'], '/') . '$/:h';
+ break;
+
+ case 'is':
+ case 'not is':
+ $string .= preg_quote($condition['value'], '/') . '$/:h';
+ break;
+
+ case 'matches':
+ case 'not matches':
+ $string .= str_replace(array('\\*', '\\?'), array('.*', '.'), preg_quote($condition['value'], '/') . '$') . '/:h';
+ break;
+
+ case 'contains':
+ case 'not contain':
+ default:
+ $string .= '.*' . preg_quote($condition['value'], '/') . '/:h';
+ break;
+ }
+
+ $this->_conditions[] = array('condition' => $string, 'flags' => $flag, 'extra' => $extra);
+ }
+
+ /**
+ * Generates maildrop code to represent the recipe.
+ *
+ * @return string maildrop code to represent the recipe.
+ */
+ function generate()
+ {
+ $text = array();
+
+ if (!$this->_valid) {
+ return '';
+ }
+
+ if (count($this->_conditions) > 0) {
+
+ $text[] = "if( \\";
+
+ $nest = false;
+ foreach ($this->_conditions as $condition) {
+ $cond = $nest ? $this->_combine : ' ';
+ $text[] = $cond . $condition['condition'] . $condition['flags'] . $condition['extra'] . " \\";
+ $nest = true;
+ }
+
+ $text[] = ')';
+ }
+
+ foreach ($this->_action as $val) {
+ $text[] = $val;
+ }
+
+ if ($this->_disable) {
+ $code = '';
+ foreach ($text as $val) {
+ $comment = new Maildrop_Comment($val);
+ $code .= $comment->generate() . "\n";
+ }
+ return $code . "\n";
+ } else {
+ return implode("\n", $text) . "\n";
+ }
+ }
+
+ /**
+ * Returns a maildrop-ready mailbox path, converting IMAP folder pathname
+ * conventions as necessary.
+ *
+ * @param string $folder The IMAP folder name.
+ *
+ * @return string The maildrop mailbox path.
+ */
+ function maildropPath($folder)
+ {
+ /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
+ if (isset($this->_params) &&
+ ($this->_params['path_style'] == 'maildir')) {
+ if (empty($folder) || ($folder == 'INBOX')) {
+ return '"${DEFAULT}"';
+ }
+ if ($this->_params['strip_inbox'] &&
+ substr($folder, 0, 6) == 'INBOX.') {
+ $folder = substr($folder, 6);
+ }
+ return '"${DEFAULT}/.' . $folder . '/"';
+ } else {
+ if (empty($folder) || ($folder == 'INBOX')) {
+ return '${DEFAULT}';
+ }
+ return str_replace(' ', '\ ', $folder);
+ }
+ }
+
+}
+
+/**
+ * The Maildrop_Variable:: class represents a Maildrop variable.
+ *
+ * @author Matt Weyland <mathias@weyland.ch>
+ * @since Ingo 1.1
+ * @package Ingo
+ */
+class Maildrop_Variable {
+
+ var $_name;
+ var $_value;
+
+ /**
+ * Constructs a new maildrop variable.
+ *
+ * @param array $params Array of parameters. Expected fields are 'name'
+ * and 'value'.
+ */
+ function Maildrop_Variable($params = array())
+ {
+ $this->_name = $params['name'];
+ $this->_value = $params['value'];
+ }
+
+ /**
+ * Generates maildrop code to represent the variable.
+ *
+ * @return string maildrop code to represent the variable.
+ */
+ function generate()
+ {
+ return $this->_name . '=' . $this->_value . "\n";
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Ingo_Script_Procmail:: class represents a Procmail script generator.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Brent J. Nordquist <bjn@horde.org>
+ * @author Ben Chavet <ben@horde.org>
+ * @package Ingo
+ */
+class Ingo_Script_Procmail extends Ingo_Script {
+
+ /**
+ * The list of actions allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_actions = array(
+ Ingo_Storage::ACTION_KEEP,
+ Ingo_Storage::ACTION_MOVE,
+ Ingo_Storage::ACTION_DISCARD,
+ Ingo_Storage::ACTION_REDIRECT,
+ Ingo_Storage::ACTION_REDIRECTKEEP,
+ Ingo_Storage::ACTION_REJECT
+ );
+
+ /**
+ * The categories of filtering allowed.
+ *
+ * @var array
+ */
+ var $_categories = array(
+ Ingo_Storage::ACTION_BLACKLIST,
+ Ingo_Storage::ACTION_WHITELIST,
+ Ingo_Storage::ACTION_VACATION,
+ Ingo_Storage::ACTION_FORWARD
+ );
+
+ /**
+ * The types of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_types = array(
+ Ingo_Storage::TYPE_HEADER,
+ Ingo_Storage::TYPE_BODY
+ );
+
+ /**
+ * A list of any special types that this driver supports.
+ *
+ * @var array
+ */
+ var $_special_types = array(
+ 'Destination',
+ );
+
+ /**
+ * The list of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_tests = array(
+ 'contains',
+ 'not contain',
+ 'begins with',
+ 'not begins with',
+ 'ends with',
+ 'not ends with',
+ 'regex'
+ );
+
+ /**
+ * Can tests be case sensitive?
+ *
+ * @var boolean
+ */
+ var $_casesensitive = true;
+
+ /**
+ * Does the driver support the stop-script option?
+ *
+ * @var boolean
+ */
+ var $_supportStopScript = true;
+
+ /**
+ * Does the driver require a script file to be generated?
+ *
+ * @var boolean
+ */
+ var $_scriptfile = true;
+
+ /**
+ * The recipes that make up the code.
+ *
+ * @var array
+ */
+ var $_recipes = array();
+
+ /**
+ * Returns a script previously generated with generate().
+ *
+ * @return string The procmail script.
+ */
+ function toCode()
+ {
+ $code = '';
+ foreach ($this->_recipes as $item) {
+ $code .= $item->generate() . "\n";
+ }
+
+ // If an external delivery program is used, add final rule
+ // to deliver to $DEFAULT
+ if (isset($this->_params['delivery_agent'])) {
+ $code .= ":0 w\n";
+ $code .= isset($this->_params['delivery_mailbox_prefix']) ?
+ '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' :
+ '| ' . $this->_params['delivery_agent'] . ' $DEFAULT';
+ }
+
+ return rtrim($code) . "\n";
+ }
+
+ /**
+ * Generates the procmail script to do the filtering specified in the
+ * rules.
+ *
+ * @return string The procmail script.
+ */
+ function generate()
+ {
+ $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
+
+ $this->addItem(new Procmail_Comment(_("procmail script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
+
+ /* Add variable information, if present. */
+ if (!empty($this->_params['variables']) &&
+ is_array($this->_params['variables'])) {
+ foreach ($this->_params['variables'] as $key => $val) {
+ $this->addItem(new Procmail_Variable(array('name' => $key, 'value' => $val)));
+ }
+ }
+
+ foreach ($filters->getFilterList() as $filter) {
+ switch ($filter['action']) {
+ case Ingo_Storage::ACTION_BLACKLIST:
+ $this->generateBlacklist(!empty($filter['disable']));
+ break;
+
+ case Ingo_Storage::ACTION_WHITELIST:
+ $this->generateWhitelist(!empty($filter['disable']));
+ break;
+
+ case Ingo_Storage::ACTION_VACATION:
+ $this->generateVacation(!empty($filter['disable']));
+ break;
+
+ case Ingo_Storage::ACTION_FORWARD:
+ $this->generateForward(!empty($filter['disable']));
+ break;
+
+ default:
+ if (in_array($filter['action'], $this->_actions)) {
+ /* Create filter if using AND. */
+ if ($filter['combine'] == Ingo_Storage::COMBINE_ALL) {
+ $recipe = new Procmail_Recipe($filter, $this->_params);
+ if (!$filter['stop']) {
+ $recipe->addFlag('c');
+ }
+ foreach ($filter['conditions'] as $condition) {
+ $recipe->addCondition($condition);
+ }
+ $this->addItem(new Procmail_Comment($filter['name'], !empty($filter['disable']), true));
+ $this->addItem($recipe);
+ } else {
+ /* Create filter if using OR */
+ $this->addItem(new Procmail_Comment($filter['name'], !empty($filter['disable']), true));
+ $loop = 0;
+ foreach ($filter['conditions'] as $condition) {
+ $recipe = new Procmail_Recipe($filter, $this->_params);
+ if ($loop++) {
+ $recipe->addFlag('E');
+ }
+ if (!$filter['stop']) {
+ $recipe->addFlag('c');
+ }
+ $recipe->addCondition($condition);
+ $this->addItem($recipe);
+ }
+ }
+ }
+ }
+ }
+
+ return $this->toCode();
+ }
+
+ /**
+ * Generates the procmail script to handle the blacklist specified in
+ * the rules.
+ *
+ * @param boolean $disable Disable the blacklist?
+ */
+ function generateBlacklist($disable = false)
+ {
+ $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+ $bl_addr = $blacklist->getBlacklist();
+ $bl_folder = $blacklist->getBlacklistFolder();
+
+ $bl_type = (empty($bl_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
+
+ if (!empty($bl_addr)) {
+ $this->addItem(new Procmail_Comment(_("Blacklisted Addresses"), $disable, true));
+ $params = array('action-value' => $bl_folder,
+ 'action' => $bl_type,
+ 'disable' => $disable);
+
+ foreach ($bl_addr as $address) {
+ if (!empty($address)) {
+ $recipe = new Procmail_Recipe($params, $this->_params);
+ $recipe->addCondition(array('field' => 'From', 'value' => $address, 'match' => 'address'));
+ $this->addItem($recipe);
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates the procmail script to handle the whitelist specified in
+ * the rules.
+ *
+ * @param boolean $disable Disable the whitelist?
+ */
+ function generateWhitelist($disable = false)
+ {
+ $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
+ $wl_addr = $whitelist->getWhitelist();
+
+ if (!empty($wl_addr)) {
+ $this->addItem(new Procmail_Comment(_("Whitelisted Addresses"), $disable, true));
+ foreach ($wl_addr as $address) {
+ if (!empty($address)) {
+ $recipe = new Procmail_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
+ $recipe->addCondition(array('field' => 'From', 'value' => $address, 'match' => 'address'));
+ $this->addItem($recipe);
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates the procmail script to handle vacation.
+ *
+ * @param boolean $disable Disable vacation?
+ */
+ function generateVacation($disable = false)
+ {
+ $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
+ $addresses = $vacation->getVacationAddresses();
+ $actionval = array(
+ 'addresses' => $addresses,
+ 'subject' => $vacation->getVacationSubject(),
+ 'days' => $vacation->getVacationDays(),
+ 'reason' => $vacation->getVacationReason(),
+ 'ignorelist' => $vacation->getVacationIgnorelist(),
+ 'excludes' => $vacation->getVacationExcludes(),
+ 'start' => $vacation->getVacationStart(),
+ 'end' => $vacation->getVacationEnd(),
+ );
+
+ if (!empty($addresses)) {
+ $this->addItem(new Procmail_Comment(_("Vacation"), $disable, true));
+ $params = array('action' => Ingo_Storage::ACTION_VACATION,
+ 'action-value' => $actionval,
+ 'disable' => $disable);
+ $recipe = new Procmail_Recipe($params, $this->_params);
+ $this->addItem($recipe);
+ }
+ }
+
+ /**
+ * Generates the procmail script to handle mail forwards.
+ *
+ * @param boolean $disable Disable forwarding?
+ */
+ function generateForward($disable = false)
+ {
+ $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
+ $addresses = $forward->getForwardAddresses();
+
+ if (!empty($addresses)) {
+ $this->addItem(new Procmail_Comment(_("Forwards"), $disable, true));
+ $params = array('action' => Ingo_Storage::ACTION_FORWARD,
+ 'action-value' => $addresses,
+ 'disable' => $disable);
+ $recipe = new Procmail_Recipe($params, $this->_params);
+ if ($forward->getForwardKeep()) {
+ $recipe->addFlag('c');
+ }
+ $this->addItem($recipe);
+ }
+ }
+
+ /**
+ * Adds an item to the recipe list.
+ *
+ * @param object $item The item to add to the recipe list.
+ * The object should have a generate() function.
+ */
+ function addItem($item)
+ {
+ $this->_recipes[] = $item;
+ }
+
+}
+
+/**
+ * The Procmail_Comment:: class represents a Procmail comment. This is
+ * a pretty simple class, but it makes the code in Ingo_Script_Procmail::
+ * cleaner as it provides a generate() function and can be added to the
+ * recipe list the same way as a recipe can be.
+ *
+ * @author Ben Chavet <ben@chavet.net>
+ * @since Ingo 1.0
+ * @package Ingo
+ */
+class Procmail_Comment {
+
+ /**
+ * The comment text.
+ *
+ * @var string
+ */
+ var $_comment = '';
+
+ /**
+ * Constructs a new procmail comment.
+ *
+ * @param string $comment Comment to be generated.
+ * @param boolean $disable Output 'DISABLED' comment?
+ * @param boolean $header Output a 'header' comment?
+ */
+ function Procmail_Comment($comment, $disable = false, $header = false)
+ {
+ if ($disable) {
+ $comment = _("DISABLED: ") . $comment;
+ }
+
+ if ($header) {
+ $this->_comment .= "##### $comment #####";
+ } else {
+ $this->_comment .= "# $comment";
+ }
+ }
+
+ /**
+ * Returns the comment stored by this object.
+ *
+ * @return string The comment stored by this object.
+ */
+ function generate()
+ {
+ return $this->_comment;
+ }
+
+}
+
+/**
+ * The Procmail_Recipe:: class represents a Procmail recipe.
+ *
+ * @author Ben Chavet <ben@chavet.net>
+ * @since Ingo 1.0
+ * @package Ingo
+ */
+class Procmail_Recipe {
+
+ var $_action = array();
+ var $_conditions = array();
+ var $_disable = '';
+ var $_flags = '';
+ var $_params = array(
+ 'date' => 'date',
+ 'echo' => 'echo',
+ 'ls' => 'ls'
+ );
+ var $_valid = true;
+
+ /**
+ * Constructs a new procmail recipe.
+ *
+ * @param array $params Array of parameters.
+ * REQUIRED FIELDS:
+ * 'action'
+ * OPTIONAL FIELDS:
+ * 'action-value' (only used if the
+ * 'action' requires it)
+ * @param array $scriptparams Array of parameters passed to
+ * Ingo_Script_Procmail::.
+ */
+ function Procmail_Recipe($params = array(), $scriptparams = array())
+ {
+ $this->_disable = !empty($params['disable']);
+ $this->_params = array_merge($this->_params, $scriptparams);
+
+ switch ($params['action']) {
+ case Ingo_Storage::ACTION_KEEP:
+ // Note: you may have to set the DEFAULT variable in your
+ // backend configuration.
+ if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
+ $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
+ } elseif (isset($this->_params['delivery_agent'])) {
+ $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' $DEFAULT';
+ } else {
+ $this->_action[] = '$DEFAULT';
+ }
+ break;
+
+ case Ingo_Storage::ACTION_MOVE:
+ if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
+ $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . $this->procmailPath($params['action-value']);
+ } elseif (isset($this->_params['delivery_agent'])) {
+ $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->procmailPath($params['action-value']);
+ } else {
+ $this->_action[] = $this->procmailPath($params['action-value']);
+ }
+ break;
+
+ case Ingo_Storage::ACTION_DISCARD:
+ $this->_action[] = '/dev/null';
+ break;
+
+ case Ingo_Storage::ACTION_REDIRECT:
+ $this->_action[] = '! ' . $params['action-value'];
+ break;
+
+ case Ingo_Storage::ACTION_REDIRECTKEEP:
+ $this->_action[] = '{';
+ $this->_action[] = ' :0 c';
+ $this->_action[] = ' ! ' . $params['action-value'];
+ $this->_action[] = '';
+ $this->_action[] = ' :0' . (isset($this->_params['delivery_agent']) ? ' w' : '');
+ if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
+ $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
+ } elseif (isset($this->_params['delivery_agent'])) {
+ $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
+ } else {
+ $this->_action[] = ' $DEFAULT';
+ }
+ $this->_action[] = '}';
+ break;
+
+ case Ingo_Storage::ACTION_REJECT:
+ $this->_action[] = '{';
+ $this->_action[] = ' EXITCODE=' . $params['action-value'];
+ $this->_action[] = ' HOST="no.address.here"';
+ $this->_action[] = '}';
+ break;
+
+ case Ingo_Storage::ACTION_VACATION:
+ $days = $params['action-value']['days'];
+ $timed = !empty($params['action-value']['start']) &&
+ !empty($params['action-value']['end']);
+ $this->_action[] = '{';
+ foreach ($params['action-value']['addresses'] as $address) {
+ if (!empty($address)) {
+ $this->_action[] = ' FILEDATE=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
+ . $this->_params['ls'] . ' -lcn --time-style=+%s ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' | '
+ . 'awk \'{ print $6 + (' . $days * 86400 . ') }\'`';
+ $this->_action[] = ' DATE=`' . $this->_params['date'] . ' +%s`';
+ $this->_action[] = ' DUMMY=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
+ . 'test $FILEDATE -le $DATE && '
+ . 'rm ${VACATION_DIR:-.}/\'.vacation.' . $address . '\'`';
+ if ($timed) {
+ $this->_action[] = ' START=' . $params['action-value']['start'];
+ $this->_action[] = ' END=' . $params['action-value']['end'];
+ }
+ $this->_action[] = '';
+ $this->_action[] = ' :0 h';
+ $this->_action[] = ' SUBJECT=| formail -xSubject:';
+ $this->_action[] = '';
+ $this->_action[] = ' :0 Whc: ${VACATION_DIR:-.}/vacation.lock';
+ if ($timed) {
+ $this->_action[] = ' * ? test $DATE -gt $START && test $END -gt $DATE';
+ $this->_action[] = ' {';
+ $this->_action[] = ' :0 Whc';
+ }
+ $this->_action[] = ' * ^TO_' . $address;
+ $this->_action[] = ' * !^X-Loop: ' . $address;
+ $this->_action[] = ' * !^X-Spam-Flag: YES';
+ if (count($params['action-value']['excludes']) > 0) {
+ foreach ($params['action-value']['excludes'] as $exclude) {
+ if (!empty($exclude)) {
+ $this->_action[] = ' * !^From.*' . $exclude;
+ }
+ }
+ }
+ if ($params['action-value']['ignorelist']) {
+ $this->_action[] = ' * !^FROM_DAEMON';
+ }
+ $this->_action[] = ' | formail -rD 8192 ${VACATION_DIR:-.}/.vacation.' . $address;
+ $this->_action[] = ' :0 ehc';
+ $this->_action[] = ' | (formail -rI"Precedence: junk" \\';
+ $this->_action[] = ' -a"From: <' . $address . '>" \\';
+ $this->_action[] = ' -A"X-Loop: ' . $address . '" \\';
+ if (Horde_Mime::is8bit($params['action-value']['reason'])) {
+ $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', Horde_Nls::getCharset()) . '" \\';
+ $this->_action[] = ' -i"Content-Transfer-Encoding: quoted-printable" \\';
+ $this->_action[] = ' -i"Content-Type: text/plain; charset=' . Horde_Nls::getCharset() . '" ; \\';
+ $reason = Horde_Mime::quotedPrintableEncode($params['action-value']['reason'], "\n");
+ } else {
+ $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', Horde_Nls::getCharset()) . '" ; \\';
+ $reason = $params['action-value']['reason'];
+ }
+ $reason = addcslashes($reason, "\\\n\r\t\"`");
+ $this->_action[] = ' ' . $this->_params['echo'] . ' -e "' . $reason . '" \\';
+ $this->_action[] = ' ) | $SENDMAIL -f' . $address . ' -oi -t';
+ $this->_action[] = '';
+ $this->_action[] = ' :0';
+ $this->_action[] = ' /dev/null';
+ if ($timed) {
+ $this->_action[] = ' }';
+ }
+ }
+ }
+ $this->_action[] = '}';
+ break;
+
+ case Ingo_Storage::ACTION_FORWARD:
+ /* Make sure that we prevent mail loops using 3 methods.
+ *
+ * First, we call sendmail -f to set the envelope sender to be the
+ * same as the original sender, so bounces will go to the original
+ * sender rather than to us. This unfortunately triggers lots of
+ * Authentication-Warning: messages in sendmail's logs.
+ *
+ * Second, add an X-Loop header, to handle the case where the
+ * address we forward to forwards back to us.
+ *
+ * Third, don't forward mailer daemon messages (i.e., bounces).
+ * Method 1 above should make this redundant, unless we're sending
+ * mail from this account and have a bad forward-to account.
+ *
+ * Get the from address, saving a call to formail if possible.
+ * The procmail code for doing this is borrowed from the
+ * Procmail Library Project, http://pm-lib.sourceforge.net/.
+ * The Ingo project has the permission to use Procmail Library code
+ * under Apache licence v 1.x or any later version.
+ * Permission obtained 2006-04-04 from Author Jari Aalto. */
+ $this->_action[] = '{';
+ $this->_action[] = ' :0 ';
+ $this->_action[] = ' *$ ! ^From *\/[^ ]+';
+ $this->_action[] = ' *$ ! ^Sender: *\/[^ ]+';
+ $this->_action[] = ' *$ ! ^From: *\/[^ ]+';
+ $this->_action[] = ' *$ ! ^Reply-to: *\/[^ ]+';
+ $this->_action[] = ' {';
+ $this->_action[] = ' OUTPUT = `formail -zxFrom:`';
+ $this->_action[] = ' }';
+ $this->_action[] = ' :0 E';
+ $this->_action[] = ' {';
+ $this->_action[] = ' OUTPUT = $MATCH';
+ $this->_action[] = ' }';
+ $this->_action[] = '';
+
+ /* Forward to each address on our list. */
+ foreach ($params['action-value'] as $address) {
+ if (!empty($address)) {
+ $this->_action[] = ' :0 c';
+ $this->_action[] = ' * !^FROM_MAILER';
+ $this->_action[] = ' * !^X-Loop: to-' . $address;
+ $this->_action[] = ' | formail -A"X-Loop: to-' . $address . '" | $SENDMAIL -oi -f $OUTPUT ' . $address;
+ }
+ }
+
+ /* In case of mail loop or bounce, store a copy locally. Note
+ * that if we forward to more than one address, only a mail loop
+ * on the last address will cause a local copy to be saved. TODO:
+ * The next two lines are redundant (and create an extra copy of
+ * the message) if "Keep a copy of messages in this account" is
+ * checked. */
+ $this->_action[] = ' :0 E' . (isset($this->_params['delivery_agent']) ? 'w' : '');
+ if (isset($this->_params['delivery_agent'])) {
+ $this->_action[] = isset($this->_params['delivery_mailbox_prefix']) ?
+ ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' :
+ ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
+ } else {
+ $this->_action[] = ' $DEFAULT';
+ }
+ $this->_action[] = ' :0 ';
+ $this->_action[] = ' /dev/null';
+ $this->_action[] = '}';
+ break;
+
+ default:
+ $this->_valid = false;
+ break;
+ }
+ }
+
+ /**
+ * Adds a flag to the recipe.
+ *
+ * @param string $flag String of flags to append to the current flags.
+ */
+ function addFlag($flag)
+ {
+ $this->_flags .= $flag;
+ }
+
+ /**
+ * Adds a condition to the recipe.
+ *
+ * @param array $condition Array of parameters. Required keys are 'field'
+ * and 'value'. 'case' is an optional key.
+ */
+ function addCondition($condition = array())
+ {
+ $flag = !empty($condition['case']) ? 'D' : '';
+ $match = isset($condition['match']) ? $condition['match'] : null;
+ $string = '';
+ $prefix = '';
+
+ switch ($condition['field']) {
+ case 'Destination':
+ $string = '^TO_';
+ break;
+
+ case 'Body':
+ $flag .= 'B';
+ break;
+
+ default:
+ // convert 'field' to PCRE pattern matching
+ if (strpos($condition['field'], ',') == false) {
+ $string = '^' . $condition['field'] . ':';
+ } else {
+ $string .= '/^(' . str_replace(',', '|', $condition['field']) . '):';
+ }
+ $prefix = ' ';
+ }
+
+ $reverseCondition = false;
+ switch ($match) {
+ case 'regex':
+ $string .= $prefix . $condition['value'];
+ break;
+
+ case 'address':
+ $string .= '(.*\<)?' . quotemeta($condition['value']);
+ break;
+
+ case 'not begins with':
+ $reverseCondition = true;
+ // fall through
+ case 'begins with':
+ $string .= $prefix . quotemeta($condition['value']);
+ break;
+
+ case 'not ends with':
+ $reverseCondition = true;
+ // fall through
+ case 'ends with':
+ $string .= '.*' . quotemeta($condition['value']) . '$';
+ break;
+
+ case 'not contain':
+ $reverseCondition = true;
+ // fall through
+ case 'contains':
+ default:
+ $string .= '.*' . quotemeta($condition['value']);
+ break;
+ }
+
+ $this->_conditions[] = array('condition' => ($reverseCondition ? '* !' : '* ') . $string,
+ 'flags' => $flag);
+ }
+
+ /**
+ * Generates procmail code to represent the recipe.
+ *
+ * @return string Procmail code to represent the recipe.
+ */
+ function generate()
+ {
+ $nest = 0;
+ $prefix = '';
+ $text = array();
+
+ if (!$this->_valid) {
+ return '';
+ }
+
+ // Set the global flags for the whole rule, each condition
+ // will add its own (such as Body or Case Sensitive)
+ $global = $this->_flags;
+ if (isset($this->_conditions[0])) {
+ $global .= $this->_conditions[0]['flags'];
+ }
+ $text[] = ':0 ' . $global . (isset($this->_params['delivery_agent']) ? 'w' : '');
+ foreach ($this->_conditions as $condition) {
+ if ($nest > 0) {
+ $text[] = str_repeat(' ', $nest - 1) . '{';
+ $text[] = str_repeat(' ', $nest) . ':0 ' . $condition['flags'];
+ $text[] = str_repeat(' ', $nest) . $condition['condition'];
+ } else {
+ $text[] = $condition['condition'];
+ }
+ $nest++;
+ }
+
+ if (--$nest > 0) {
+ $prefix = str_repeat(' ', $nest);
+ }
+ foreach ($this->_action as $val) {
+ $text[] = $prefix . $val;
+ }
+
+ for ($i = $nest; $i > 0; $i--) {
+ $text[] = str_repeat(' ', $i - 1) . '}';
+ }
+
+ if ($this->_disable) {
+ $code = '';
+ foreach ($text as $val) {
+ $comment = new Procmail_Comment($val);
+ $code .= $comment->generate() . "\n";
+ }
+ return $code . "\n";
+ } else {
+ return implode("\n", $text) . "\n";
+ }
+ }
+
+ /**
+ * Returns a procmail-ready mailbox path, converting IMAP folder
+ * pathname conventions as necessary.
+ *
+ * @param string $folder The IMAP folder name.
+ *
+ * @return string The procmail mailbox path.
+ */
+ function procmailPath($folder)
+ {
+ /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
+ if (isset($this->_params) &&
+ ($this->_params['path_style'] == 'maildir')) {
+ if (empty($folder) || ($folder == 'INBOX')) {
+ return '$DEFAULT';
+ }
+ if (substr($folder, 0, 6) == 'INBOX.') {
+ $folder = substr($folder, 6);
+ }
+ return '"$DEFAULT/.' . escapeshellcmd($folder) . '/"';
+ } else {
+ if (empty($folder) || ($folder == 'INBOX')) {
+ return '$DEFAULT';
+ }
+ return str_replace(' ', '\ ', escapeshellcmd($folder));
+ }
+ }
+
+}
+
+/**
+ * The Procmail_Variable:: class represents a Procmail variable.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @since Ingo 1.0
+ * @package Ingo
+ */
+class Procmail_Variable {
+
+ var $_name;
+ var $_value;
+
+ /**
+ * Constructs a new procmail variable.
+ *
+ * @param array $params Array of parameters. Expected fields are 'name'
+ * and 'value'.
+ */
+ function Procmail_Variable($params = array())
+ {
+ $this->_name = $params['name'];
+ $this->_value = $params['value'];
+ }
+
+ /**
+ * Generates procmail code to represent the variable.
+ *
+ * @return string Procmail code to represent the variable.
+ */
+ function generate()
+ {
+ return $this->_name . '=' . $this->_value . "\n";
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Ingo_Script_Sieve class represents a Sieve Script.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve extends Ingo_Script {
+
+ /**
+ * The list of actions allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_actions = array(
+ Ingo_Storage::ACTION_KEEP,
+ Ingo_Storage::ACTION_MOVE,
+ Ingo_Storage::ACTION_DISCARD,
+ Ingo_Storage::ACTION_REDIRECT,
+ Ingo_Storage::ACTION_REDIRECTKEEP,
+ Ingo_Storage::ACTION_MOVEKEEP,
+ Ingo_Storage::ACTION_REJECT,
+ Ingo_Storage::ACTION_FLAGONLY,
+ Ingo_Storage::ACTION_NOTIFY
+ );
+
+ /**
+ * The categories of filtering allowed.
+ *
+ * @var array
+ */
+ var $_categories = array(
+ Ingo_Storage::ACTION_BLACKLIST,
+ Ingo_Storage::ACTION_WHITELIST,
+ Ingo_Storage::ACTION_VACATION,
+ Ingo_Storage::ACTION_FORWARD,
+ Ingo_Storage::ACTION_SPAM
+ );
+
+ /**
+ * The list of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_tests = array(
+ 'contains', 'not contain', 'is', 'not is', 'begins with',
+ 'not begins with', 'ends with', 'not ends with', 'exists', 'not exist',
+ 'less than', 'less than or equal to', 'equal', 'not equal',
+ 'greater than', 'greater than or equal to', 'regex', 'matches',
+ 'not matches'
+ );
+
+ /**
+ * The types of tests allowed (implemented) for this driver.
+ *
+ * @var array
+ */
+ var $_types = array(
+ Ingo_Storage::TYPE_HEADER,
+ Ingo_Storage::TYPE_SIZE,
+ Ingo_Storage::TYPE_BODY
+ );
+
+ /**
+ * Can tests be case sensitive?
+ *
+ * @var boolean
+ */
+ var $_casesensitive = true;
+
+ /**
+ * Does the driver support setting IMAP flags?
+ *
+ * @var boolean
+ */
+ var $_supportIMAPFlags = true;
+
+ /**
+ * Does the driver support the stop-script option?
+ *
+ * @var boolean
+ */
+ var $_supportStopScript = true;
+
+ /**
+ * Does the driver require a script file to be generated?
+ *
+ * @var boolean
+ */
+ var $_scriptfile = true;
+
+ /**
+ * The blocks that make up the code.
+ *
+ * @var array
+ */
+ var $_blocks = array();
+
+ /**
+ * The blocks that have to appear at the end of the code.
+ *
+ * @var array
+ */
+ var $_endBlocks = array();
+
+ /**
+ * Returns a script previously generated with generate().
+ *
+ * @return string The Sieve script.
+ */
+ function toCode()
+ {
+ $date_format = $GLOBALS['prefs']->getValue('date_format');
+ // %R and %r don't work on Windows, but who runs a Sieve backend on a
+ // Windows server?
+ $time_format = $GLOBALS['prefs']->getValue('twentyFour') ? '%R' : '%r';
+ $code = "# Sieve Filter\n# "
+ . _("Generated by Ingo (http://www.horde.org/ingo/)") . ' ('
+ . trim(strftime($date_format . ', ' . $time_format))
+ . ")\n\n";
+ $code = Horde_String::convertCharset($code, Horde_Nls::getCharset(), 'UTF-8');
+ $requires = $this->requires();
+
+ if (count($requires) > 1) {
+ $stringlist = '';
+ foreach ($this->requires() as $require) {
+ $stringlist .= (empty($stringlist)) ? '"' : ', "';
+ $stringlist .= $require . '"';
+ }
+ $code .= 'require [' . $stringlist . '];' . "\n\n";
+ } elseif (count($requires) == 1) {
+ foreach ($this->requires() as $require) {
+ $code .= 'require "' . $require . '";' . "\n\n";
+ }
+ }
+
+ foreach ($this->_blocks as $block) {
+ $code .= $block->toCode() . "\n";
+ }
+
+ return rtrim($code) . "\n";
+ }
+
+ /**
+ * Escape a string according to Sieve RFC 3028 [2.4.2].
+ *
+ * @param string $string The string to escape.
+ * @param boolean $regexmode Is the escaped string a regex value?
+ * Defaults to no.
+ *
+ * @return string The escaped string.
+ */
+ function escapeString($string, $regexmode = false)
+ {
+ /* Remove any backslashes in front of commas. */
+ $string = str_replace('\,', ',', $string);
+
+ if ($regexmode) {
+ return str_replace('"', addslashes('"'), $string);
+ } else {
+ return str_replace(array('\\', '"'), array(addslashes('\\'), addslashes('"')), $string);
+ }
+ }
+
+ /**
+ * Checks if all rules are valid.
+ *
+ * @return boolean|string True if all rules are valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ foreach ($this->_blocks as $block) {
+ $res = $block->check();
+ if ($res !== true) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ $requires = array();
+ foreach ($this->_blocks as $block) {
+ $requires = array_merge($requires, $block->requires());
+ }
+
+ return array_unique($requires);
+ }
+
+ /**
+ * Adds all blocks necessary for the forward rule.
+ */
+ function _addForwardBlocks()
+ {
+ if (!$this->_validRule(Ingo_Storage::ACTION_FORWARD)) {
+ return;
+ }
+
+ $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
+ $fwd_addr = $forward->getForwardAddresses();
+ if (empty($fwd_addr)) {
+ return;
+ }
+
+ $action = array();
+ foreach ($fwd_addr as $addr) {
+ $addr = trim($addr);
+ if (!empty($addr)) {
+ $action[] = new Sieve_Action_Redirect(array('address' => $addr));
+ }
+ }
+
+ if (count($action)) {
+ if($forward->getForwardKeep()) {
+ $this->_endBlocks[] = new Sieve_Comment(_("Forward Keep Action"));
+ $if = new Sieve_If(new Sieve_Test_True());
+ $if->setActions(array(new Sieve_Action_Keep(),
+ new Sieve_Action_Stop()));
+ $this->_endBlocks[] = $if;
+ } else {
+ $action[] = new Sieve_Action_Stop();
+ }
+ }
+
+ $this->_blocks[] = new Sieve_Comment(_("Forwards"));
+
+ $test = new Sieve_Test_True();
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ }
+
+ /**
+ * Adds all blocks necessary for the blacklist rule.
+ */
+ function _addBlacklistBlocks()
+ {
+ if (!$this->_validRule(Ingo_Storage::ACTION_BLACKLIST)) {
+ return;
+ }
+
+ $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+ $bl_addr = $blacklist->getBlacklist();
+ $folder = $blacklist->getBlacklistFolder();
+ if (empty($bl_addr)) {
+ return;
+ }
+
+ $action = array();
+ if (empty($folder)) {
+ $action[] = new Sieve_Action_Discard();
+ } elseif ($folder == INGO_BLACKLIST_MARKER) {
+ $action[] = new Sieve_Action_Addflag(array('flags' => Ingo_Storage::FLAG_DELETED));
+ $action[] = new Sieve_Action_Keep();
+ $action[] = new Sieve_Action_Removeflag(array('flags' => Ingo_Storage::FLAG_DELETED));
+ } else {
+ $action[] = new Sieve_Action_Fileinto(array('folder' => $folder));
+ }
+
+ $action[] = new Sieve_Action_Stop();
+
+ $this->_blocks[] = new Sieve_Comment(_("Blacklisted Addresses"));
+
+ /* Split the test up to only do 5 addresses at a time. */
+ $temp = array();
+ $wildcards = array();
+ foreach ($bl_addr as $address) {
+ if (!empty($address)) {
+ if ((strstr($address, '*') !== false) ||
+ (strstr($address, '?') !== false)) {
+ $wildcards[] = $address;
+ } else {
+ $temp[] = $address;
+ }
+ }
+ if (count($temp) == 5) {
+ $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ $temp = array();
+ }
+ if (count($wildcards) == 5) {
+ $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ $wildcards = array();
+ }
+ }
+
+ if ($temp) {
+ $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ }
+
+ if ($wildcards) {
+ $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ }
+ }
+
+ /**
+ * Adds all blocks necessary for the whitelist rule.
+ */
+ function _addWhitelistBlocks()
+ {
+ if (!$this->_validRule(Ingo_Storage::ACTION_WHITELIST)) {
+ return;
+ }
+
+ $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
+ $wl_addr = $whitelist->getWhitelist();
+ if (empty($wl_addr)) {
+ return;
+ }
+
+ $this->_blocks[] = new Sieve_Comment(_("Whitelisted Addresses"));
+
+ $action = array(new Sieve_Action_Keep(), new Sieve_Action_Stop());
+ $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $wl_addr)));
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ }
+
+ /**
+ * Adds all blocks necessary for the vacation rule.
+ */
+ function _addVacationBlocks()
+ {
+ if (!$this->_validRule(Ingo_Storage::ACTION_VACATION)) {
+ return;
+ }
+
+ $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
+ $vacation_addr = $vacation->getVacationAddresses();
+ if (!count($vacation_addr)) {
+ return;
+ }
+
+ $vals = array(
+ 'subject' => Horde_String::convertCharset($vacation->getVacationSubject(), Horde_Nls::getCharset(), 'UTF-8'),
+ 'days' => $vacation->getVacationDays(),
+ 'addresses' => $vacation_addr,
+ 'start' => $vacation->getVacationStart(),
+ 'start_year' => $vacation->getVacationStartYear(),
+ 'start_month' => $vacation->getVacationStartMonth(),
+ 'start_day' => $vacation->getVacationStartDay(),
+ 'end' => $vacation->getVacationEnd(),
+ 'end_year' => $vacation->getVacationEndYear(),
+ 'end_month' => $vacation->getVacationEndMonth(),
+ 'end_day' => $vacation->getVacationEndDay(),
+ 'reason' => Horde_String::convertCharset($vacation->getVacationReason(), Horde_Nls::getCharset(), 'UTF-8'),
+ );
+
+ $action = $tests = array();
+ $action[] = new Sieve_Action_Vacation($vals);
+
+ if ($vacation->getVacationIgnorelist()) {
+ $mime_headers = new Horde_Mime_Headers();
+ $headers = $mime_headers->listHeaders();
+ $headers['Mailing-List'] = null;
+ $tmp = new Sieve_Test_Exists(array('headers' => implode("\n", array_keys($headers))));
+ $tests[] = new Sieve_Test_Not($tmp);
+ $vals = array('headers' => 'Precedence',
+ 'match-type' => ':is',
+ 'strings' => "list\nbulk\njunk",
+ 'comparator' => 'i;ascii-casemap');
+ $tmp = new Sieve_Test_Header($vals);
+ $tests[] = new Sieve_Test_Not($tmp);
+ $vals = array('headers' => 'To',
+ 'match-type' => ':matches',
+ 'strings' => 'Multiple recipients of*',
+ 'comparator' => 'i;ascii-casemap');
+ $tmp = new Sieve_Test_Header($vals);
+ $tests[] = new Sieve_Test_Not($tmp);
+ }
+
+ $addrs = array();
+ foreach ($vacation->getVacationExcludes() as $addr) {
+ $addr = trim($addr);
+ if (!empty($addr)) {
+ $addrs[] = $addr;
+ }
+ }
+
+ if ($addrs) {
+ $tmp = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $addrs)));
+ $tests[] = new Sieve_Test_Not($tmp);
+ }
+
+ $this->_blocks[] = new Sieve_Comment(_("Vacation"));
+
+ if ($tests) {
+ $test = new Sieve_Test_Allof($tests);
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ } else {
+ $this->_blocks[] = $action[0];
+ }
+ }
+
+ /**
+ * Adds all blocks necessary for the spam rule.
+ */
+ function _addSpamBlocks()
+ {
+ if (!$this->_validRule(Ingo_Storage::ACTION_SPAM)) {
+ return;
+ }
+
+ $spam = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
+ if ($spam === false) {
+ return;
+ }
+
+ $this->_blocks[] = new Sieve_Comment(_("Spam Filter"));
+
+ $actions = array();
+ $actions[] = new Sieve_Action_Fileinto(array(
+ 'folder' => $spam->getSpamFolder()
+ ));
+
+ if ($GLOBALS['conf']['spam']['compare'] == 'numeric') {
+ $vals = array(
+ 'headers' => $GLOBALS['conf']['spam']['header'],
+ 'comparison' => 'ge',
+ 'value' => $spam->getSpamLevel(),
+ );
+ $test = new Sieve_Test_Relational($vals);
+ } elseif ($GLOBALS['conf']['spam']['compare'] == 'string') {
+ $vals = array(
+ 'headers' => $GLOBALS['conf']['spam']['header'],
+ 'match-type' => ':contains',
+ 'strings' => str_repeat($GLOBALS['conf']['spam']['char'],
+ $spam->getSpamLevel()),
+ 'comparator' => 'i;ascii-casemap',
+ );
+ $test = new Sieve_Test_Header($vals);
+ }
+
+ $actions[] = new Sieve_Action_Stop();
+
+ $if = new Sieve_If($test);
+ $if->setActions($actions);
+ $this->_blocks[] = $if;
+ }
+
+ /**
+ * Generates the Sieve script to do the filtering specified in
+ * the rules.
+ *
+ * @return string The Sieve script.
+ */
+ function generate()
+ {
+ global $ingo_storage;
+
+ $filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
+ foreach ($filters->getFilterList() as $filter) {
+ /* Check to make sure this is a valid rule and that the rule
+ is not disabled. */
+ if (!$this->_validRule($filter['action']) ||
+ !empty($filter['disable'])) {
+ continue;
+ }
+
+ $action = array();
+ switch ($filter['action']) {
+ case Ingo_Storage::ACTION_KEEP:
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+ }
+
+ $action[] = new Sieve_Action_Keep();
+
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+ }
+ break;
+
+ case Ingo_Storage::ACTION_DISCARD:
+ $action[] = new Sieve_Action_Discard();
+ break;
+
+ case Ingo_Storage::ACTION_MOVE:
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+ }
+
+ $action[] = new Sieve_Action_Fileinto(array('folder' => $filter['action-value']));
+
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+ }
+ break;
+
+ case Ingo_Storage::ACTION_REJECT:
+ $action[] = new Sieve_Action_Reject(array('reason' => $filter['action-value']));
+ break;
+
+ case Ingo_Storage::ACTION_REDIRECT:
+ $action[] = new Sieve_Action_Redirect(array('address' => $filter['action-value']));
+ break;
+
+ case Ingo_Storage::ACTION_REDIRECTKEEP:
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+ }
+
+ $action[] = new Sieve_Action_Redirect(array('address' => $filter['action-value']));
+ $action[] = new Sieve_Action_Keep();
+
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+ }
+ break;
+
+ case Ingo_Storage::ACTION_MOVEKEEP:
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+ }
+
+ $action[] = new Sieve_Action_Keep();
+ $action[] = new Sieve_Action_Fileinto(array('folder' => $filter['action-value']));
+
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+ }
+ break;
+
+ case Ingo_Storage::ACTION_FLAGONLY:
+ if (!empty($filter['flags'])) {
+ $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+ }
+ break;
+
+ case Ingo_Storage::ACTION_NOTIFY:
+ $action[] = new Sieve_Action_Notify(array('address' => $filter['action-value'], 'name' => $filter['name']));
+ break;
+
+ case Ingo_Storage::ACTION_WHITELIST:
+ $this->_addWhitelistBlocks();
+ continue 2;
+
+ case Ingo_Storage::ACTION_BLACKLIST:
+ $this->_addBlacklistBlocks();
+ continue 2;
+
+ case Ingo_Storage::ACTION_VACATION:
+ $this->_addVacationBlocks();
+ continue 2;
+
+ case Ingo_Storage::ACTION_FORWARD:
+ $this->_addForwardBlocks();
+ continue 2;
+
+ case Ingo_Storage::ACTION_SPAM:
+ $this->_addSpamBlocks();
+ continue 2;
+ }
+
+ $this->_blocks[] = new Sieve_Comment($filter['name']);
+
+ if ($filter['stop']) {
+ $action[] = new Sieve_Action_Stop();
+ }
+
+ $test = new Sieve_Test();
+ if ($filter['combine'] == Ingo_Storage::COMBINE_ANY) {
+ $test = new Sieve_Test_Anyof();
+ } else {
+ $test = new Sieve_Test_Allof();
+ }
+
+ foreach ($filter['conditions'] as $condition) {
+ $tmp = '';
+ switch ($condition['match']) {
+ case 'equal':
+ $tmp = new Sieve_Test_Relational(array('comparison' => 'eq', 'headers' => $condition['field'], 'value' => $condition['value']));
+ $test->addTest($tmp);
+ break;
+
+ case 'not equal':
+ $tmp = new Sieve_Test_Relational(array('comparison' => 'ne', 'headers' => $condition['field'], 'value' => $condition['value']));
+ $test->addTest($tmp);
+ break;
+
+ case 'less than':
+ if ($condition['field'] == 'Size') {
+ /* Message Size Test. */
+ $tmp = new Sieve_Test_Size(array('comparison' => ':under', 'size' => $condition['value']));
+ } else {
+ /* Relational Test. */
+ $tmp = new Sieve_Test_Relational(array('comparison' => 'lt', 'headers' => $condition['field'], 'value' => $condition['value']));
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'less than or equal to':
+ $tmp = new Sieve_Test_Relational(array('comparison' => 'le', 'headers' => $condition['field'], 'value' => $condition['value']));
+ $test->addTest($tmp);
+ break;
+
+ case 'greater than':
+ if ($condition['field'] == 'Size') {
+ /* Message Size Test. */
+ $tmp = new Sieve_Test_Size(array('comparison' => ':over', 'size' => $condition['value']));
+ } else {
+ /* Relational Test. */
+ $tmp = new Sieve_Test_Relational(array('comparison' => 'gt', 'headers' => $condition['field'], 'value' => $condition['value']));
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'greater than or equal to':
+ $tmp = new Sieve_Test_Relational(array('comparison' => 'ge', 'headers' => $condition['field'], 'value' => $condition['value']));
+ $test->addTest($tmp);
+ break;
+
+ case 'exists':
+ $tmp = new Sieve_Test_Exists(array('headers' => $condition['field']));
+ $test->addTest($tmp);
+ break;
+
+ case 'not exist':
+ $tmp = new Sieve_Test_Exists(array('headers' => $condition['field']));
+ $test->addTest(new Sieve_Test_Not($tmp));
+ break;
+
+ case 'contains':
+ case 'not contain':
+ case 'is':
+ case 'not is':
+ case 'begins with':
+ case 'not begins with':
+ case 'ends with':
+ case 'not ends with':
+ case 'regex':
+ case 'matches':
+ case 'not matches':
+ $comparator = (isset($condition['case']) &&
+ $condition['case'])
+ ? 'i;octet'
+ : 'i;ascii-casemap';
+ $vals = array('headers' => preg_replace('/(.)(?<!\\\)\,(.)/',
+ "$1\n$2",
+ $condition['field']),
+ 'comparator' => $comparator);
+ $use_address_test = false;
+
+ if ($condition['match'] != 'regex') {
+ $condition['value'] = preg_replace('/(.)(?<!\\\)\,(.)/',
+ "$1\n$2",
+ $condition['value']);
+ }
+
+ /* Do 'smarter' searching for fields where we know we have
+ * e-mail addresses. */
+ if (preg_match('/^(From|To|Cc|Bcc)/', $condition['field'])) {
+ $vals['addresses'] = $condition['value'];
+ $use_address_test = true;
+ } else {
+ $vals['strings'] = $condition['value'];
+ }
+
+ switch ($condition['match']) {
+ case 'contains':
+ $vals['match-type'] = ':contains';
+ if ($use_address_test) {
+ $tmp = new Sieve_Test_Address($vals);
+ } elseif ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'not contain':
+ $vals['match-type'] = ':contains';
+ if ($use_address_test) {
+ $tmp = new Sieve_Test_Address($vals);
+ } elseif ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ $test->addTest(new Sieve_Test_Not($tmp));
+ break;
+
+ case 'is':
+ $vals['match-type'] = ':is';
+ if ($use_address_test) {
+ $tmp = new Sieve_Test_Address($vals);
+ } elseif ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'not is':
+ $vals['match-type'] = ':is';
+ if ($use_address_test) {
+ $tmp = new Sieve_Test_Address($vals);
+ } elseif ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ $test->addTest(new Sieve_Test_Not($tmp));
+ break;
+
+ case 'begins with':
+ $vals['match-type'] = ':matches';
+ if ($use_address_test) {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = $v . '*';
+ }
+ $vals['addresses'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['addresses'] .= '*';
+ }
+ $tmp = new Sieve_Test_Address($vals);
+ } else {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = $v . '*';
+ }
+ $vals['strings'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['strings'] .= '*';
+ }
+ if ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'not begins with':
+ $vals['match-type'] = ':matches';
+ if ($use_address_test) {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = $v . '*';
+ }
+ $vals['addresses'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['addresses'] .= '*';
+ }
+ $tmp = new Sieve_Test_Address($vals);
+ } else {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = $v . '*';
+ }
+ $vals['strings'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['strings'] .= '*';
+ }
+ if ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ }
+ $test->addTest(new Sieve_Test_Not($tmp));
+ break;
+
+ case 'ends with':
+ $vals['match-type'] = ':matches';
+ if ($use_address_test) {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = '*' . $v;
+ }
+ $vals['addresses'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['addresses'] = '*' . $vals['addresses'];
+ }
+ $tmp = new Sieve_Test_Address($vals);
+ } else {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = '*' . $v;
+ }
+ $vals['strings'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['strings'] = '*' . $vals['strings'];
+ }
+ if ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'not ends with':
+ $vals['match-type'] = ':matches';
+ if ($use_address_test) {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = '*' . $v;
+ }
+ $vals['addresses'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['addresses'] = '*' . $vals['addresses'];
+ }
+ $tmp = new Sieve_Test_Address($vals);
+ } else {
+ $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
+ if (count($add_arr) > 1) {
+ foreach ($add_arr as $k => $v) {
+ $add_arr[$k] = '*' . $v;
+ }
+ $vals['strings'] = implode("\r\n", $add_arr);
+ } else {
+ $vals['strings'] = '*' . $vals['strings'];
+ }
+ if ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ }
+ $test->addTest(new Sieve_Test_Not($tmp));
+ break;
+
+ case 'regex':
+ $vals['match-type'] = ':regex';
+ if ($use_address_test) {
+ $tmp = new Sieve_Test_Address($vals);
+ } elseif ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'matches':
+ $vals['match-type'] = ':matches';
+ if ($use_address_test) {
+ $tmp = new Sieve_Test_Address($vals);
+ } elseif ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ $test->addTest($tmp);
+ break;
+
+ case 'not matches':
+ $vals['match-type'] = ':matches';
+ if ($use_address_test) {
+ $tmp = new Sieve_Test_Address($vals);
+ } elseif ($condition['field'] == 'Body') {
+ $tmp = new Sieve_Test_Body($vals);
+ } else {
+ $tmp = new Sieve_Test_Header($vals);
+ }
+ $test->addTest(new Sieve_Test_Not($tmp));
+ break;
+ }
+ }
+ }
+
+ $if = new Sieve_If($test);
+ $if->setActions($action);
+ $this->_blocks[] = $if;
+ }
+
+ /* Add blocks that have to go to the end. */
+ foreach ($this->_endBlocks as $block) {
+ $this->_blocks[] = $block;
+ }
+
+ return $this->toCode();
+ }
+
+}
+
+/**
+ * The Sieve_If class represents a Sieve If Statement
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_If {
+
+ /**
+ * The Sieve_Test object for the if test.
+ *
+ * @var Sieve_Test
+ */
+ var $_test;
+
+ /**
+ * A list of Sieve_Action objects that go into the if clause.
+ *
+ * @var array
+ */
+ var $_actions = array();
+
+ /**
+ * A list of Sieve_Elseif objects that create optional elsif clauses.
+ *
+ * @var array
+ */
+ var $_elsifs = array();
+
+ /**
+ * A Sieve_Else object that creates an optional else clause.
+ *
+ * @var Sieve_Else
+ */
+ var $_else;
+
+ /**
+ * Constructor.
+ *
+ * @params Sieve_Test $test A Sieve_Test object.
+ */
+ function Sieve_If($test = null)
+ {
+ if (is_null($test)) {
+ $this->_test = new Sieve_Test_False();
+ } else {
+ $this->_test = $test;
+ }
+
+ $this->_actions[] = new Sieve_Action_Keep();
+ $this->_else = new Sieve_Else();
+ }
+
+ function getTest()
+ {
+ return $this->_test;
+ }
+
+ function setTest($test)
+ {
+ $this->_test = $test;
+ }
+
+ function getActions()
+ {
+ return $this->_actions;
+ }
+
+ function setActions($actions)
+ {
+ $this->_actions = $actions;
+ }
+
+ function getElsifs()
+ {
+ return $this->_elsifs;
+ }
+
+ function setElsifs($elsifs)
+ {
+ $this->_elsifs = $elsifs;
+ }
+
+ function addElsif($elsif)
+ {
+ $this->_elsifs[] = $elsif;
+ }
+
+ function getElse()
+ {
+ return $this->_else;
+ }
+
+ function setElse($else)
+ {
+ $this->_else = $else;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = 'if ' . $this->_test->toCode() . " { \n";
+ foreach ($this->_actions as $action) {
+ $code .= ' ' . $action->toCode() . "\n";
+ }
+ $code .= "} ";
+
+ foreach ($this->_elsifs as $elsif) {
+ $code .= $elsif->toCode();
+ }
+
+ $code .= $this->_else->toCode();
+
+ return $code . "\n";
+ }
+
+ /**
+ * Checks if all sub-rules are valid.
+ *
+ * @return boolean|string True if all rules are valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ $res = $this->_test->check();
+ if ($res !== true) {
+ return $res;
+ }
+
+ foreach ($this->_elsifs as $elsif) {
+ $res = $elsif->check();
+ if ($res !== true) {
+ return $res;
+ }
+ }
+
+ $res = $this->_else->check();
+ if ($res !== true) {
+ return $res;
+ }
+
+ foreach ($this->_actions as $action) {
+ $res = $action->check();
+ if ($res !== true) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ $requires = array();
+
+ foreach ($this->_actions as $action) {
+ $requires = array_merge($requires, $action->requires());
+ }
+
+ foreach ($this->_elsifs as $elsif) {
+ $requires = array_merge($requires, $elsif->requires());
+ }
+
+ $requires = array_merge($requires, $this->_test->requires(), $this->_else->requires());
+
+ return $requires;
+ }
+
+}
+
+/**
+ * The Sieve_Else class represents a Sieve Else Statement
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Else {
+
+ /**
+ * A list of Sieve_Action objects that go into the else clause.
+ *
+ * @var array
+ */
+ var $_actions = array();
+
+ /**
+ * Constructor.
+ *
+ * @params Sieve_Action|array $actions A Sieve_Action object or a list of
+ * Sieve_Action objects.
+ */
+ function Sieve_Else($actions = null)
+ {
+ if (is_array($actions)) {
+ $this->_actions = $actions;
+ } elseif (!is_null($actions)) {
+ $this->_actions[] = $actions;
+ }
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ if (count($this->_actions) == 0) {
+ return '';
+ }
+
+ $code = 'else' . " { \n";
+ foreach ($this->_actions as $action) {
+ $code .= ' ' . $action->toCode() . "\n";
+ }
+ $code .= "} ";
+
+ return $code;
+ }
+
+ function setActions($actions)
+ {
+ $this->_actions = $actions;
+ }
+
+ function getActions()
+ {
+ return $this->_actions;
+ }
+
+ /**
+ * Checks if all sub-rules are valid.
+ *
+ * @return boolean|string True if all rules are valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ foreach ($this->_actions as $action) {
+ $res = $action->check();
+ if ($res !== true) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ $requires = array();
+
+ foreach ($this->_actions as $action) {
+ $requires = array_merge($requires, $action->requires());
+ }
+
+ return $requires;
+ }
+
+}
+
+/**
+ * The Sieve_Elsif class represents a Sieve Elsif Statement
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Elsif {
+
+ /**
+ * The Sieve_Test object for the if test.
+ *
+ * @var Sieve_Test
+ */
+ var $_test;
+
+ /**
+ * A list of Sieve_Action objects that go into the if clause.
+ *
+ * @var array
+ */
+ var $_actions = array();
+
+ /**
+ * Constructor.
+ *
+ * @params Sieve_Test $test A Sieve_Test object.
+ */
+ function Sieve_Elsif($test = null)
+ {
+ if (is_null($test)) {
+ $this->_test = new Sieve_Test_False();
+ } else {
+ $this->_test = $test;
+ }
+ $this->_actions[] = new Sieve_Action_Keep();
+ }
+
+ function getTest()
+ {
+ return $this->_test;
+ }
+
+ function setTest($test)
+ {
+ $this->_test = $test;
+ }
+
+ function getActions()
+ {
+ return $this->_actions;
+ }
+
+ function setActions($actions)
+ {
+ $this->_actions = $actions;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = 'elsif ' . $this->_test->toCode() . " { \n";
+ foreach ($this->_actions as $action) {
+ $code .= ' ' . $action->toCode() . "\n";
+ }
+ $code .= "} ";
+
+ return $code;
+ }
+
+ /**
+ * Checks if all sub-rules are valid.
+ *
+ * @return boolean|string True if all rules are valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ $res = $this->_test->check();
+ if ($res !== true) {
+ return $res;
+ }
+
+ foreach ($this->_actions as $action) {
+ $res = $action->check();
+ if ($res !== true) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ $requires = array();
+
+ foreach ($this->_actions as $action) {
+ $requires = array_merge($requires, $action->requires());
+ }
+
+ $requires = array_merge($requires, $this->_test->requires());
+
+ return $requires;
+ }
+
+}
+
+/**
+ * The Sieve_Test class represents a Sieve Test.
+ *
+ * A test is a piece of code that evaluates to true or false.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test {
+
+ /**
+ * Any necessary test parameters.
+ *
+ * @var array
+ */
+ var $_vars = array();
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'toCode() Function Not Implemented in class ' . get_class($this);
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return 'check() Function Not Implemented in class ' . get_class($this);
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array();
+ }
+
+}
+
+/**
+ * The Sieve_Test_True class represents a test that always evaluates to true.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_True extends Sieve_Test {
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'true';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return true;
+ }
+
+}
+
+/**
+ * The Sieve_Test_False class represents a test that always evaluates to
+ * false.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_False extends Sieve_Test {
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'false';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return true;
+ }
+
+}
+
+/**
+ * The Sieve_Test_Allof class represents a Allof test structure.
+ *
+ * Equivalent to a logical AND of all the tests it contains.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_Allof extends Sieve_Test {
+
+ var $_tests = array();
+
+ /**
+ * Constructor.
+ *
+ * @params Sieve_Test|array $test A Sieve_Test object or a list of
+ * Sieve_Test objects.
+ */
+ function Sieve_Test_Allof($test = null)
+ {
+ if (is_array($test)) {
+ $this->_tests = $test;
+ } elseif (!is_null($test)) {
+ $this->_tests[] = $test;
+ }
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = '';
+ if (count($this->_tests) > 1) {
+ $testlist = '';
+ foreach ($this->_tests as $test) {
+ $testlist .= (empty($testlist)) ? '' : ', ';
+ $testlist .= trim($test->toCode());
+ }
+
+ $code = "allof ( $testlist )";
+ } elseif (count($this->_tests) == 1) {
+ $code = $this->_tests[0]->toCode();
+ } else {
+ return 'true';
+ }
+ return $code;
+ }
+
+ /**
+ * Checks if all sub-rules are valid.
+ *
+ * @return boolean|string True if all rules are valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ foreach ($this->_tests as $test) {
+ $res = $test->check();
+ if ($res !== true) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ function addTest($test)
+ {
+ $this->_tests[] = $test;
+ }
+
+ function getTests()
+ {
+ return $this->_tests;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ $requires = array();
+
+ foreach ($this->_tests as $test) {
+ $requires = array_merge($requires, $test->requires());
+ }
+
+ return $requires;
+ }
+
+}
+
+/**
+ * The Sieve_Test_Anyof class represents a Anyof test structure.
+ *
+ * Equivalent to a logical OR of all the tests it contains.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_Anyof extends Sieve_Test {
+
+ var $_tests = array();
+
+ /**
+ * Constructor.
+ *
+ * @params Sieve_Test|array $test A Sieve_Test object or a list of
+ * Sieve_Test objects.
+ */
+ function Sieve_Test_Anyof($test = null)
+ {
+ if (is_array($test)) {
+ $this->_tests = $test;
+ } elseif (!is_null($test)) {
+ $this->_tests[] = $test;
+ }
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $testlist = '';
+ if (count($this->_tests) > 1) {
+ $testlist = '';
+ foreach ($this->_tests as $test) {
+ $testlist .= (empty($testlist)) ? '' : ', ';
+ $testlist .= trim($test->toCode());
+ }
+
+ $code = "anyof ( $testlist )";
+ } elseif (count($this->_tests) == 1) {
+ $code = $this->_tests[0]->toCode();
+ } else {
+ return 'true';
+ }
+ return $code;
+ }
+
+ function addTest($test)
+ {
+ $this->_tests[] = $test;
+ }
+
+ function getTests()
+ {
+ return $this->_tests;
+ }
+
+ /**
+ * Checks if all sub-rules are valid.
+ *
+ * @return boolean|string True if all rules are valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ foreach ($this->_tests as $test) {
+ $res = $test->check();
+ if ($res !== true) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ $requires = array();
+
+ foreach ($this->_tests as $test) {
+ $requires = array_merge($requires, $test->requires());
+ }
+
+ return $requires;
+ }
+
+}
+
+/**
+ * The Sieve_Test_Relational class represents a relational test.
+ *
+ * @author Todd Merritt <tmerritt@email.arizona.edu>
+ * @since Ingo 1.0
+ * @package Ingo
+ */
+class Sieve_Test_Relational extends Sieve_Test {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Test_Relational($vars = array())
+ {
+ $this->_vars['comparison'] = (isset($vars['comparison'])) ? $vars['comparison'] : '';
+ $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
+ $this->_vars['value'] = (isset($vars['value'])) ? $vars['value'] : 0;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = 'header :value "' .
+ $this->_vars['comparison'] . '" ' .
+ ':comparator "i;ascii-numeric" ';
+
+ $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+ $header_count = count($headers);
+
+ if ($header_count > 1) {
+ $code .= "[";
+ $headerstr = '';
+
+ foreach ($headers as $val) {
+ $headerstr .= (empty($headerstr) ? '"' : ', "') .
+ Ingo_Script_Sieve::escapeString($val) . '"';
+ }
+
+ $code .= $headerstr . "] ";
+ } elseif ($header_count == 1) {
+ $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0]) . '" ';
+ }
+
+ return $code . '["' . $this->_vars['value'] . '"]';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+ return $headers ? true : _("No headers specified");
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array('relational', 'comparator-i;ascii-numeric');
+ }
+
+}
+
+/**
+ * The Sieve_Test_Size class represents a message size test.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_Size extends Sieve_Test {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Test_Size($vars = array())
+ {
+ $this->_vars['comparison'] = (isset($vars['comparison'])) ? $vars['comparison'] : '';
+ $this->_vars['size'] = (isset($vars['size'])) ? $vars['size'] : '';
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'size ' . $this->_vars['comparison'] . ' ' . $this->_vars['size'];
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ if (!(isset($this->_vars['comparison']) &&
+ isset($this->_vars['size']))) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
+
+/**
+ * The Sieve_Test_Not class represents the inverse of a given test.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_Not extends Sieve_Test {
+
+ var $_test = array();
+
+ /**
+ * Constructor.
+ *
+ * @params Sieve_Test $test A Sieve_Test object.
+ */
+ function Sieve_Test_Not($test)
+ {
+ $this->_test = $test;
+ }
+
+ /**
+ * Checks if the sub-rule is valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return $this->_test->check();
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'not ' . $this->_test->toCode();
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return $this->_test->requires();
+ }
+
+}
+
+/**
+ * The Sieve_Test_Exists class represents a test for the existsance of one or
+ * more headers in a message.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_Exists extends Sieve_Test {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Test_Exists($vars = array())
+ {
+ $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+ if (!$headers) {
+ return _("No headers specified");
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = 'exists ';
+ $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+ if (count($headers) > 1) {
+ $code .= "[";
+ $headerstr = '';
+ foreach ($headers as $header) {
+ $headerstr .= (empty($headerstr) ? '"' : ', "') .
+ Ingo_Script_Sieve::escapeString($header) . '"';
+ }
+ $code .= $headerstr . "] ";
+ } elseif (count($headers) == 1) {
+ $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0]) . '" ';
+ } else {
+ return "**error** No Headers Specified";
+ }
+
+ return $code;
+ }
+
+}
+
+/**
+ * The Sieve_Test_Address class represents a test on parts or all of the
+ * addresses in the given fields.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_Address extends Sieve_Test {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Test_Address($vars)
+ {
+ $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
+ $this->_vars['comparator'] = (isset($vars['comparator'])) ? $vars['comparator'] : 'i;ascii-casemap';
+ $this->_vars['match-type'] = (isset($vars['match-type'])) ? $vars['match-type'] : ':is';
+ $this->_vars['address-part'] = (isset($vars['address-part'])) ? $vars['address-part'] : ':all';
+ $this->_vars['addresses'] = (isset($vars['addresses'])) ? $vars['addresses'] : '';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+ if (!$headers) {
+ return false;
+ }
+
+ $addresses = preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
+ if (!$addresses) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = 'address ' .
+ $this->_vars['address-part'] . ' ' .
+ ':comparator "' . $this->_vars['comparator'] . '" ' .
+ $this->_vars['match-type'] . ' ';
+
+ $headers = preg_split('(\r\n|\n|\r|,)', $this->_vars['headers']);
+ $headers = array_filter($headers);
+ if (count($headers) > 1) {
+ $code .= "[";
+ $headerstr = '';
+ foreach ($headers as $header) {
+ $header = trim($header);
+ if (!empty($header)) {
+ $headerstr .= empty($headerstr) ? '"' : ', "';
+ $headerstr .= Ingo_Script_Sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
+ }
+ }
+ $code .= $headerstr . "] ";
+ } elseif (count($headers) == 1) {
+ $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0], $this->_vars['match-type'] == ':regex') . '" ';
+ } else {
+ return "No Headers Specified";
+ }
+
+ $addresses = preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
+ $addresses = array_filter($addresses);
+ if (count($addresses) > 1) {
+ $code .= "[";
+ $addressstr = '';
+ foreach ($addresses as $addr) {
+ $addr = trim($addr);
+ if (!empty($addr)) {
+ $addressstr .= empty($addressstr) ? '"' : ', "';
+ $addressstr .= Ingo_Script_Sieve::escapeString($addr, $this->_vars['match-type'] == ':regex') . '"';
+ }
+ }
+ $code .= $addressstr . "] ";
+ } elseif (count($addresses) == 1) {
+ $code .= '"' . Ingo_Script_Sieve::escapeString($addresses[0], $this->_vars['match-type'] == ':regex') . '" ';
+ } else {
+ return "No Addresses Specified";
+ }
+
+ return $code;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ if ($this->_vars['match-type'] == ':regex') {
+ return array('regex');
+ }
+ return array();
+ }
+
+}
+
+/**
+ * The Sieve_Test_Header class represents a test on the contents of one or
+ * more headers in a message.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Test_Header extends Sieve_Test {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Test_Header($vars = array())
+ {
+ $this->_vars['headers'] = isset($vars['headers'])
+ ? $vars['headers']
+ : 'Subject';
+ $this->_vars['comparator'] = isset($vars['comparator'])
+ ? $vars['comparator']
+ : 'i;ascii-casemap';
+ $this->_vars['match-type'] = isset($vars['match-type'])
+ ? $vars['match-type']
+ : ':is';
+ $this->_vars['strings'] = isset($vars['strings'])
+ ? $vars['strings']
+ : '';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ $headers = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['headers']);
+ if (!$headers) {
+ return false;
+ }
+
+ $strings = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
+ if (!$strings) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = 'header ' .
+ ':comparator "' . $this->_vars['comparator'] . '" ' .
+ $this->_vars['match-type'] . ' ';
+
+ $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+ $headers = array_filter($headers);
+ if (count($headers) > 1) {
+ $code .= "[";
+ $headerstr = '';
+ foreach ($headers as $header) {
+ $headerstr .= empty($headerstr) ? '"' : ', "';
+ $headerstr .= Ingo_Script_Sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
+ }
+ $code .= $headerstr . "] ";
+ } elseif (count($headers) == 1) {
+ $code .= '"' . $headers[0] . '" ';
+ } else {
+ return _("No headers specified");
+ }
+
+ $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
+ $strings = array_filter($strings);
+ if (count($strings) > 1) {
+ $code .= "[";
+ $stringlist = '';
+ foreach ($strings as $str) {
+ $stringlist .= empty($stringlist) ? '"' : ', "';
+ $stringlist .= Ingo_Script_Sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
+ }
+ $code .= $stringlist . "] ";
+ } elseif (count($strings) == 1) {
+ $code .= '"' . Ingo_Script_Sieve::escapeString(reset($strings), $this->_vars['match-type'] == ':regex') . '" ';
+ } else {
+ return _("No strings specified");
+ }
+
+ return $code;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ if ($this->_vars['match-type'] == ':regex') {
+ return array('regex');
+ }
+ return array();
+ }
+
+}
+
+/**
+ * The Sieve_Test_Body class represents a test on the contents of the body in
+ * a message.
+ *
+ * @author Michael Menge <michael.menge@zdv.uni-tuebingen.de>
+ * @since Ingo 1.2
+ * @package Ingo
+ */
+class Sieve_Test_Body extends Sieve_Test {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Test_Body($vars = array())
+ {
+ $this->_vars['comparator'] = (isset($vars['comparator'])) ? $vars['comparator'] : 'i;ascii-casemap';
+ $this->_vars['match-type'] = (isset($vars['match-type'])) ? $vars['match-type'] : ':is';
+ $this->_vars['strings'] = (isset($vars['strings'])) ? $vars['strings'] : '';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ $strings = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
+ if (!$strings) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = 'body ' .
+ ':comparator "' . $this->_vars['comparator'] . '" ' .
+ $this->_vars['match-type'] . ' ';
+
+ $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
+ $strings = array_filter($strings);
+ if (count($strings) > 1) {
+ $code .= "[";
+ $stringlist = '';
+ foreach ($strings as $str) {
+ $stringlist .= empty($stringlist) ? '"' : ', "';
+ $stringlist .= Ingo_Script_Sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
+ }
+ $code .= $stringlist . "] ";
+ } elseif (count($strings) == 1) {
+ $code .= '"' . Ingo_Script_Sieve::escapeString($strings[0], $this->_vars['match-type'] == ':regex') . '" ';
+ } else {
+ return _("No strings specified");
+ }
+
+ return $code;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ if ($this->_vars['match-type'] == ':regex') {
+ return array('regex', 'body');
+ }
+
+ return array('body');
+ }
+
+}
+
+/**
+ * A Comment.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ * @todo This and Sieve_If should really extends a Sieve_Block eventually.
+ */
+class Sieve_Comment {
+
+ var $_comment;
+
+ /**
+ * Constructor.
+ *
+ * @params string $comment The comment text.
+ */
+ function Sieve_Comment($comment)
+ {
+ $this->_comment = $comment;
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $code = '';
+ $lines = preg_split('(\r\n|\n|\r)', $this->_comment);
+ foreach ($lines as $line) {
+ $line = trim($line);
+ if (strlen($line)) {
+ $code .= (empty($code) ? '' : "\n") . '# ' . $line;
+ }
+ }
+ return Horde_String::convertCharset($code, Horde_Nls::getCharset(), 'UTF-8');
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array();
+ }
+
+}
+
+/**
+ * The Sieve_Action class represents an action in a Sieve script.
+ *
+ * An action is anything that has a side effect eg: discard, redirect.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action {
+
+ /**
+ * Any necessary action parameters.
+ *
+ * @var array
+ */
+ var $_vars = array();
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'toCode() Function Not Implemented in class ' . get_class($this) ;
+ }
+
+ function toString()
+ {
+ return $this->toCode();
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return 'check() Function Not Implemented in class ' . get_class($this) ;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array();
+ }
+
+}
+
+/**
+ * The Sieve_Action_Redirect class represents a redirect action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Redirect extends Sieve_Action {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Action_Redirect($vars = array())
+ {
+ $this->_vars['address'] = (isset($vars['address'])) ? $vars['address'] : '';
+ }
+
+ function toCode($depth = 0)
+ {
+ return str_repeat(' ', $depth * 4) . 'redirect ' .
+ '"' . Ingo_Script_Sieve::escapeString($this->_vars['address']) . '";';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ if (empty($this->_vars['address'])) {
+ return _("Missing address to redirect message to");
+ }
+
+ return true;
+ }
+
+}
+
+/**
+ * The Sieve_Action_Reject class represents a reject action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Reject extends Sieve_Action {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Action_Reject($vars = array())
+ {
+ $this->_vars['reason'] = (isset($vars['reason'])) ? $vars['reason'] : '';
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'reject "' . Ingo_Script_Sieve::escapeString($this->_vars['reason']) . '";';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ if (empty($this->_vars['reason'])) {
+ return _("Missing reason for reject");
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array('reject');
+ }
+
+}
+
+/**
+ * The Sieve_Action_Keep class represents a keep action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Keep extends Sieve_Action {
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'keep;';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return true;
+ }
+
+}
+
+/**
+ * The Sieve_Action_Discard class represents a discard action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Discard extends Sieve_Action {
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'discard;';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return true;
+ }
+
+}
+
+/**
+ * The Sieve_Action_Stop class represents a stop action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Stop extends Sieve_Action {
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'stop;';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return true;
+ }
+
+}
+
+/**
+ * The Sieve_Action_Fileinto class represents a fileinto action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Fileinto extends Sieve_Action {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Action_Fileinto($vars = array())
+ {
+ $this->_vars['folder'] = (isset($vars['folder'])) ? $vars['folder'] : '';
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'fileinto "' . Ingo_Script_Sieve::escapeString($this->_vars['folder']) . '";';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ if (empty($this->_vars['folder'])) {
+ return _("Inexistant mailbox specified for message delivery.");
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array('fileinto');
+ }
+
+}
+
+/**
+ * The Sieve_Action_Vacation class represents a vacation action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Vacation extends Sieve_Action {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Action_Vacation($vars = array())
+ {
+ $this->_vars['days'] = isset($vars['days']) ? intval($vars['days']) : '';
+ $this->_vars['addresses'] = isset($vars['addresses']) ? $vars['addresses'] : '';
+ $this->_vars['subject'] = isset($vars['subject']) ? $vars['subject'] : '';
+ $this->_vars['reason'] = isset($vars['reason']) ? $vars['reason'] : '';
+ $this->_vars['start'] = isset($vars['start']) ? $vars['start'] : '';
+ $this->_vars['start_year'] = isset($vars['start_year']) ? $vars['start_year'] : '';
+ $this->_vars['start_month'] = isset($vars['start_month']) ? $vars['start_month'] : '';
+ $this->_vars['start_day'] = isset($vars['start_day']) ? $vars['start_day'] : '';
+ $this->_vars['end'] = isset($vars['end']) ? $vars['end'] : '';
+ $this->_vars['end_year'] = isset($vars['end_year']) ? $vars['end_year'] : '';
+ $this->_vars['end_month'] = isset($vars['end_month']) ? $vars['end_month'] : '';
+ $this->_vars['end_day'] = isset($vars['end_day']) ? $vars['end_day'] : '';
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ $start_year = $this->_vars['start_year'];
+ $start_month = $this->_vars['start_month'];
+ $start_day = $this->_vars['start_day'];
+
+ $end_year = $this->_vars['end_year'];
+ $end_month = $this->_vars['end_month'];
+ $end_day = $this->_vars['end_day'];
+
+ $code = '';
+
+ if (empty($this->_vars['start']) || empty($this->_vars['end'])) {
+ return $this->_vacationCode();
+ } elseif ($end_year > $start_year + 1) {
+ $code .= $this->_yearCheck($start_year + 1, $end_year - 1)
+ . $this->_vacationCode()
+ . "\n}\n"
+ . $this->_yearCheck($start_year, $start_year);
+ if ($start_month < 12) {
+ $code .= $this->_monthCheck($start_month + 1, 12)
+ . $this->_vacationCode()
+ . "\n}\n";
+ }
+ $code .= $this->_monthCheck($start_month, $start_month)
+ . $this->_dayCheck($start_day, 31)
+ . $this->_vacationCode()
+ . "\n}\n}\n}\n"
+ . $this->_yearCheck($end_year, $end_year);
+ if ($end_month > 1) {
+ $code .= $this->_monthCheck(1, $end_month - 1)
+ . $this->_vacationCode()
+ . "\n}\n";
+ }
+ $code .= $this->_monthCheck($end_month, $end_month)
+ . $this->_dayCheck(1, $end_day)
+ . $this->_vacationCode()
+ . "\n}\n}\n}\n";
+ } elseif ($end_year == $start_year + 1) {
+ $code .= $this->_yearCheck($start_year, $start_year);
+ if ($start_month < 12) {
+ $code .= $this->_monthCheck($start_month + 1, 12)
+ . $this->_vacationCode()
+ . "\n}\n";
+ }
+ $code .= $this->_monthCheck($start_month, $start_month)
+ . $this->_dayCheck($start_day, 31)
+ . $this->_vacationCode()
+ . "\n}\n}\n}\n"
+ . $this->_yearCheck($end_year, $end_year);
+ if ($end_month > 1) {
+ $code .= $this->_monthCheck(1, $end_month - 1)
+ . $this->_vacationCode()
+ . "\n}\n";
+ }
+ $code .= $this->_monthCheck($end_month, $end_month)
+ . $this->_dayCheck(1, $end_day)
+ . $this->_vacationCode()
+ . "\n}\n}\n}\n";
+ } elseif ($end_year == $start_year) {
+ $code .= $this->_yearCheck($start_year, $start_year);
+ if ($end_month > $start_month) {
+ if ($end_month > $start_month + 1) {
+ $code .= $this->_monthCheck($start_month + 1, $end_month - 1)
+ . $this->_vacationCode()
+ . "\n}\n";
+ }
+ $code .= $this->_monthCheck($start_month, $start_month)
+ . $this->_dayCheck($start_day, 31)
+ . $this->_vacationCode()
+ . "\n}\n}\n"
+ . $this->_monthCheck($end_month, $end_month)
+ . $this->_dayCheck(1, $end_day)
+ . $this->_vacationCode()
+ . "\n}\n}\n";
+ } elseif ($end_month == $start_month) {
+ $code .= $this->_monthCheck($start_month, $start_month)
+ . $this->_dayCheck($start_day, $end_day)
+ . $this->_vacationCode()
+ . "\n}\n}\n";
+ }
+ $code .= "}\n";
+ }
+
+ return $code;
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ if (empty($this->_vars['reason'])) {
+ return _("Missing reason in vacation.");
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array('vacation', 'regex');
+ }
+
+ /**
+ */
+ function _vacationCode()
+ {
+ $code = 'vacation :days ' . $this->_vars['days'] . ' ';
+ $addresses = $this->_vars['addresses'];
+ $stringlist = '';
+ if (count($addresses) > 1) {
+ foreach ($addresses as $address) {
+ $address = trim($address);
+ if (!empty($address)) {
+ $stringlist .= empty($stringlist) ? '"' : ', "';
+ $stringlist .= Ingo_Script_Sieve::escapeString($address) . '"';
+ }
+ }
+ $stringlist = "[" . $stringlist . "] ";
+ } elseif (count($addresses) == 1) {
+ $stringlist = '"' . Ingo_Script_Sieve::escapeString($addresses[0]) . '" ';
+ }
+
+ if (!empty($stringlist)) {
+ $code .= ':addresses ' . $stringlist;
+ }
+
+ if (!empty($this->_vars['subject'])) {
+ $code .= ':subject "' . Horde_Mime::encode(Ingo_Script_Sieve::escapeString($this->_vars['subject']), 'UTF-8') . '" ';
+ }
+ return $code
+ . '"' . Ingo_Script_Sieve::escapeString($this->_vars['reason'])
+ . '";';
+ }
+
+ /**
+ */
+ function _yearCheck($begin, $end)
+ {
+ $code = 'if header :regex "Received" "^.*(' . $begin;
+ for ($i = $begin + 1; $i <= $end; $i++) {
+ $code .= '|' . $i;
+ }
+ return $code
+ . ') (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?(\\\\+|\\\\-)....( \\\\(.*\\\\))?$" {'
+ . "\n ";
+ }
+
+ /**
+ */
+ function _monthCheck($begin, $end)
+ {
+ $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
+ $code = 'if header :regex "Received" "^.*(' . $months[$begin - 1];
+ for ($i = $begin + 1; $i <= $end; $i++) {
+ $code .= '|' . $months[$i - 1];
+ }
+ return $code
+ . ') (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?(\\\\+|\\\\-)....( \\\\(.*\\\\))?$" {'
+ . "\n ";
+ }
+
+ /**
+ */
+ function _dayCheck($begin, $end)
+ {
+ $code = 'if header :regex "Received" "^.*(' . str_repeat('[0 ]', 2 - strlen($begin)) . $begin;
+ for ($i = $begin + 1; $i <= $end; $i++) {
+ $code .= '|' . str_repeat('[0 ]', 2 - strlen($i)) . $i;
+ }
+ return $code
+ . ') (\\\\(.*\\\\) )?... (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?(\\\\+|\\\\-)....( \\\\(.*\\\\))?$" {'
+ . "\n ";
+ }
+
+}
+
+/**
+ * The Sieve_Action_Flag class is the base class for flag actions.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Flag extends Sieve_Action {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Action_Flag($vars = array())
+ {
+ if (isset($vars['flags'])) {
+ if ($vars['flags'] & Ingo_Storage::FLAG_ANSWERED) {
+ $this->_vars['flags'][] = '\Answered';
+ }
+ if ($vars['flags'] & Ingo_Storage::FLAG_DELETED) {
+ $this->_vars['flags'][] = '\Deleted';
+ }
+ if ($vars['flags'] & Ingo_Storage::FLAG_FLAGGED) {
+ $this->_vars['flags'][] = '\Flagged';
+ }
+ if ($vars['flags'] & Ingo_Storage::FLAG_SEEN) {
+ $this->_vars['flags'][] = '\Seen';
+ }
+ } else {
+ $this->_vars['flags'] = '';
+ }
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @param string $mode The sieve flag command to use. Either 'removeflag'
+ * or 'addflag'.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function _toCode($mode)
+ {
+ $code = '';
+
+ if (is_array($this->_vars['flags']) && !empty($this->_vars['flags'])) {
+ $code .= $mode . ' ';
+ if (count($this->_vars['flags']) > 1) {
+ $stringlist = '';
+ foreach ($this->_vars['flags'] as $flag) {
+ $flag = trim($flag);
+ if (!empty($flag)) {
+ $stringlist .= empty($stringlist) ? '"' : ', "';
+ $stringlist .= Ingo_Script_Sieve::escapeString($flag) . '"';
+ }
+ }
+ $stringlist = '[' . $stringlist . ']';
+ $code .= $stringlist . ';';
+ } else {
+ $code .= '"' . Ingo_Script_Sieve::escapeString($this->_vars['flags'][0]) . '";';
+ }
+ }
+ return $code;
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array('imapflags');
+ }
+
+}
+
+/**
+ * The Sieve_Action_Addflag class represents an add flag action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Addflag extends Sieve_Action_Flag {
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return $this->_toCode('addflag');
+ }
+
+}
+
+/**
+ * The Sieve_Action_Removeflag class represents a remove flag action.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @since Ingo 0.1
+ * @package Ingo
+ */
+class Sieve_Action_Removeflag extends Sieve_Action_Flag {
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return $this->_toCode('removeflag');
+ }
+
+}
+
+/**
+ * The Sieve_Action_Notify class represents a notify action.
+ *
+ * @author Paul Wolstenholme <wolstena@sfu.ca>
+ * @since Ingo 1.1
+ * @package Ingo
+ */
+class Sieve_Action_Notify extends Sieve_Action {
+
+ /**
+ * Constructor.
+ *
+ * @params array $vars Any required parameters.
+ */
+ function Sieve_Action_Notify($vars = array())
+ {
+ $this->_vars['address'] = isset($vars['address']) ? $vars['address'] : '';
+ $this->_vars['name'] = isset($vars['name']) ? $vars['name'] : '';
+ }
+
+ /**
+ * Returns a script snippet representing this rule and any sub-rules.
+ *
+ * @return string A Sieve script snippet.
+ */
+ function toCode()
+ {
+ return 'notify :method "mailto" :options "' .
+ Ingo_Script_Sieve::escapeString($this->_vars['address']) .
+ '" :message "' .
+ _("You have received a new message") . "\n" .
+ _("From:") . " \$from\$ \n" .
+ _("Subject:") . " \$subject\$ \n" .
+ _("Rule:") . ' ' . $this->_vars['name'] . '";';
+ }
+
+ /**
+ * Checks if the rule parameters are valid.
+ *
+ * @return boolean|string True if this rule is valid, an error message
+ * otherwise.
+ */
+ function check()
+ {
+ if (empty($this->_vars['address'])) {
+ return _("Missing address to notify");
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of sieve extensions required for this rule and any
+ * sub-rules.
+ *
+ * @return array A Sieve extension list.
+ */
+ function requires()
+ {
+ return array('notify');
+ }
+
+}
+++ /dev/null
-<?php
-/**
- * The Ingo_Script_imap:: class represents an IMAP client-side script
- * generator.
- *
- * $Horde: ingo/lib/Script/imap.php,v 1.76 2009/01/19 18:10:01 mrubinsk Exp $
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Script_imap extends Ingo_Script
-{
- /**
- * The list of actions allowed (implemented) for this driver.
- *
- * @var array
- */
- protected $_actions = array(
- Ingo_Storage::ACTION_KEEP,
- Ingo_Storage::ACTION_MOVE,
- Ingo_Storage::ACTION_DISCARD,
- Ingo_Storage::ACTION_MOVEKEEP
- );
-
- /**
- * The categories of filtering allowed.
- *
- * @var array
- */
- protected $_categories = array(
- Ingo_Storage::ACTION_BLACKLIST,
- Ingo_Storage::ACTION_WHITELIST
- );
-
- /**
- * The list of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- protected $_tests = array(
- 'contains', 'not contain'
- );
-
- /**
- * The types of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- protected $_types = array(
- Ingo_Storage::TYPE_HEADER,
- Ingo_Storage::TYPE_SIZE,
- Ingo_Storage::TYPE_BODY
- );
-
- /**
- * Does the driver support setting IMAP flags?
- *
- * @var boolean
- */
- protected $_supportIMAPFlags = true;
-
- /**
- * Does the driver support the stop-script option?
- *
- * @var boolean
- */
- protected $_supportStopScript = true;
-
- /**
- * This driver can perform on demand filtering (in fact, that is all
- * it can do).
- *
- * @var boolean
- */
- protected $_ondemand = true;
-
- /**
- * The API to use for IMAP functions.
- *
- * @var Ingo_Script_imap_api
- */
- protected $_api;
-
- /**
- * Perform the filtering specified in the rules.
- *
- * @param array $params The parameter array. It MUST contain:
- * <pre>
- * 'mailbox' - The name of the mailbox to filter.
- * </pre>
- *
- * @return boolean True if filtering performed, false if not.
- */
- public function perform($params)
- {
- if (empty($params['api'])) {
- $this->_api = Ingo_Script_imap_api::factory('live', $params);
- } else {
- $this->_api = &$params['api'];
- }
-
- /* Indices that will be ignored by subsequent rules. */
- $ignore_ids = array();
-
- /* Only do filtering if:
- 1. We have not done filtering before -or-
- 2. The mailbox has changed -or-
- 3. The rules have changed. */
- $cache = $this->_api->getCache();
- if (($cache !== false) && ($cache == $_SESSION['ingo']['change'])) {
- return true;
- }
-
- /* Grab the rules list. */
- $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
-
- /* Should we filter only [un]seen messages? */
- $seen_flag = $GLOBALS['prefs']->getValue('filter_seen');
-
- /* Should we use detailed notification messages? */
- $detailmsg = $GLOBALS['prefs']->getValue('show_filter_msg');
-
- /* Parse through the rules, one-by-one. */
- foreach ($filters->getFilterList() as $rule) {
- /* Check to make sure this is a valid rule and that the rule is
- not disabled. */
- if (!$this->_validRule($rule['action']) ||
- !empty($rule['disable'])) {
- continue;
- }
-
- $search_array = array();
-
- switch ($rule['action']) {
- case Ingo_Storage::ACTION_BLACKLIST:
- case Ingo_Storage::ACTION_WHITELIST:
- $bl_folder = null;
-
- if ($rule['action'] == Ingo_Storage::ACTION_BLACKLIST) {
- $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
- $addr = $blacklist->getBlacklist();
- $bl_folder = $blacklist->getBlacklistFolder();
- } else {
- $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
- $addr = $whitelist->getWhitelist();
- }
-
- /* If list is empty, move on. */
- if (empty($addr)) {
- continue;
- }
-
- $query = new Horde_Imap_Client_Search_Query();
- foreach ($addr as $val) {
- $ob = new Horde_Imap_Client_Search_Query();
- $ob->flag('\\deleted', false);
- if ($seen_flag == Ingo_Script::FILTER_UNSEEN) {
- $ob->flag('\\seen', false);
- } elseif ($seen_flag == Ingo_Script::FILTER_SEEN) {
- $ob->flag('\\seen', true);
- }
- $ob->headerText('from', $val);
- $search_array[] = $ob;
- }
- $query->orSearch($search_array);
- $indices = $this->_api->search($query);
-
- /* Remove any indices that got in there by way of partial
- * address match. */
- $msgs = $this->_api->fetchEnvelope($indices);
- foreach ($msgs as $k => $v) {
- $from_addr = Horde_Mime_Address::bareAddress(Horde_Mime_Address::addrArray2String($v['envelope']['from']));
- $found = false;
- foreach ($addr as $val) {
- if (strtolower($from_addr) == strtolower($val)) {
- $found = true;
- }
- }
- if (!$found) {
- $indices = array_diff($indices, array($k));
- }
- }
-
- if ($rule['action'] == Ingo_Storage::ACTION_BLACKLIST) {
- $indices = array_diff($indices, $ignore_ids);
- if (!empty($indices)) {
- if (!empty($bl_folder)) {
- $this->_api->moveMessages($indices, $bl_folder);
- } else {
- $this->_api->deleteMessages($indices);
- }
- $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) that matched the blacklist were deleted."), count($indices)), 'horde.message');
- }
- } else {
- $ignore_ids = $indices;
- }
- break;
-
- case Ingo_Storage::ACTION_KEEP:
- case Ingo_Storage::ACTION_MOVE:
- case Ingo_Storage::ACTION_DISCARD:
- $query = new Horde_Imap_Client_Search_Query();
- foreach ($rule['conditions'] as $val) {
- $ob = new Horde_Imap_Client_Search_Query();
- $ob->flag('\\deleted', false);
- if ($seen_flag == Ingo_Script::FILTER_UNSEEN) {
- $ob->flag('\\seen', false);
- } elseif ($seen_flag == Ingo_Script::FILTER_SEEN) {
- $ob->flag('\\seen', true);
- }
- if (!empty($val['type']) &&
- ($val['type'] == Ingo_Storage::TYPE_SIZE)) {
- $ob->size($val['value'], ($val['match'] == 'greater than'));
- } elseif (!empty($val['type']) &&
- ($val['type'] == Ingo_Storage::TYPE_BODY)) {
- $ob->text($val['value'], true, ($val['match'] == 'not contain'));
- } else {
- $ob->headerText($val['field'], $val['value'], ($val['match'] == 'not contain'));
- }
- $search_array[] = $ob;
- }
-
- if ($rule['combine'] == Ingo_Storage::COMBINE_ALL) {
- $query->andSearch($search_array);
- } else {
- $query->orSearch($search_array);
- }
-
- $indices = $this->_api->search($query);
-
- if (($indices = array_diff($indices, $ignore_ids))) {
- if ($rule['stop']) {
- /* If the stop action is set, add these
- * indices to the list of ids that will be
- * ignored by subsequent rules. */
- $ignore_ids = array_unique($indices + $ignore_ids);
- }
-
- /* Set the flags. */
- if (!empty($rule['flags']) &&
- ($rule['action'] != Ingo_Storage::ACTION_DISCARD)) {
- $flags = array();
- if ($rule['flags'] & Ingo_Storage::FLAG_ANSWERED) {
- $flags[] = '\\answered';
- }
- if ($rule['flags'] & Ingo_Storage::FLAG_DELETED) {
- $flags[] = '\\deleted';
- }
- if ($rule['flags'] & Ingo_Storage::FLAG_FLAGGED) {
- $flags[] = '\\flagged';
- }
- if ($rule['flags'] & Ingo_Storage::FLAG_SEEN) {
- $flags[] = '\\seen';
- }
- $this->_api->setMessageFlags($indices, implode(' ', $flags));
- }
-
- if ($rule['action'] == Ingo_Storage::ACTION_KEEP) {
- /* Add these indices to the ignore list. */
- $ignore_ids = array_unique($indices + $ignore_ids);
- } elseif ($rule['action'] == Ingo_Storage::ACTION_MOVE) {
- /* We need to grab the overview first. */
- if ($detailmsg) {
- $overview = $this->_api->fetchEnvelope($indices);
- }
-
- /* Move the messages to the requested mailbox. */
- $this->_api->moveMessages($indices, $rule['action-value']);
-
- /* Display notification message(s). */
- if ($detailmsg) {
- foreach ($overview as $msg) {
- $GLOBALS['notification']->push(
- sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been moved to the folder \"%s\"."),
- !empty($msg['envelope']['subject']) ? Horde_Mime::decode($msg['envelope']['subject'], Horde_Nls::getCharset()) : _("[No Subject]"),
- !empty($msg['envelope']['from']) ? Horde_Mime::decode(Horde_Mime_Address::addrArray2String($msg['envelope']['from']), Horde_Nls::getCharset()) : _("[No Sender]"),
- Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())),
- 'horde.message');
- }
- } else {
- $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been moved to the folder \"%s\"."),
- count($indices),
- Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())), 'horde.message');
- }
- } elseif ($rule['action'] == Ingo_Storage::ACTION_DISCARD) {
- /* We need to grab the overview first. */
- if ($detailmsg) {
- $overview = $this->_api->fetchEnvelope($indices);
- }
-
- /* Delete the messages now. */
- $this->_api->deleteMessages($indices);
-
- /* Display notification message(s). */
- if ($detailmsg) {
- foreach ($overview as $msg) {
- $GLOBALS['notification']->push(
- sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been deleted."),
- !empty($msg['envelope']['subject']) ? Horde_Mime::decode($msg['envelope']['subject'], Horde_Nls::getCharset()) : _("[No Subject]"),
- !empty($msg['envelope']['from']) ? Horde_Mime::decode($msg['envelope']['from'], Horde_Nls::getCharset()) : _("[No Sender]")),
- 'horde.message');
- }
- } else {
- $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been deleted."), count($indices)), 'horde.message');
- }
- } elseif ($rule['action'] == Ingo_Storage::ACTION_MOVEKEEP) {
- /* Copy the messages to the requested mailbox. */
- $this->_api->copyMessages($indices,
- $rule['action-value']);
-
- /* Display notification message(s). */
- if ($detailmsg) {
- $overview = $this->_api->fetchEnvelope($indices);
- foreach ($overview as $msg) {
- $GLOBALS['notification']->push(
- sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been copied to the folder \"%s\"."),
- !empty($msg['envelope']['subject']) ? Horde_Mime::decode($msg['envelope']['subject'], Horde_Nls::getCharset()) : _("[No Subject]"),
- !empty($msg['envelope']['from']) ? Horde_Mime::decode($msg['envelope']['from'], Horde_Nls::getCharset()) : _("[No Sender]"),
- Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())),
- 'horde.message');
- }
- } else {
- $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been copied to the folder \"%s\"."), count($indices), Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', Horde_Nls::getCharset())), 'horde.message');
- }
- }
- }
- break;
- }
- }
-
- /* Set cache flag. */
- $this->_api->storeCache($_SESSION['ingo']['change']);
-
- return true;
- }
-
- /**
- * Is the apply() function available?
- *
- * @return boolean True if apply() is available, false if not.
- */
- public function canApply()
- {
- if ($this->performAvailable() &&
- $GLOBALS['registry']->hasMethod('mail/server')) {
- $server = $GLOBALS['registry']->call('mail/server');
- return ($server['protocol'] == 'imap');
- }
-
- return false;
- }
-
- /**
- * Apply the filters now.
- *
- * @return boolean See perform().
- */
- public function apply()
- {
- return $this->canApply()
- ? $this->perform(array('mailbox' => 'INBOX'))
- : false;
- }
-
-}
-
-class Ingo_Script_imap_api
-{
- /**
- * TODO
- */
- protected $_params;
-
- /**
- * TODO
- */
- static public function factory($type, $params)
- {
- $class = 'Ingo_Script_imap_' . $type;
- return new $class($params);
- }
-
- /**
- * TODO
- */
- public function __construct($params = array())
- {
- $this->_params = $params;
- }
-
- /**
- * TODO
- */
- public function deleteMessages($indices)
- {
- return PEAR::raiseError('Not implemented.');
- }
-
- /**
- * TODO
- */
- public function moveMessages($indices, $folder)
- {
- return PEAR::raiseError('Not implemented.');
- }
-
- /**
- * TODO
- */
- public function copyMessages($indices, $folder)
- {
- return PEAR::raiseError('Not implemented.');
- }
-
- /**
- * TODO
- */
- public function setMessageFlags($indices, $flags)
- {
- return PEAR::raiseError('Not implemented.');
- }
-
- /**
- * TODO
- */
- public function fetchEnvelope($indices)
- {
- return PEAR::raiseError('Not implemented.');
- }
-
- /**
- * TODO
- */
- public function search($query)
- {
- return PEAR::raiseError('Not implemented.');
- }
-
- /**
- * TODO
- */
- public function getCache()
- {
- return false;
- }
-
- /**
- * TODO
- */
- public function storeCache($timestamp)
- {
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Ingo_Script_imap_live:: driver.
- *
- * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Jason M. Felice <jason.m.felice@gmail.com>
- * @author Michael Slusarz <slusarz@curecanti.org>
- * @package Ingo
- */
-class Ingo_Script_imap_live extends Ingo_Script_imap_api
-{
- /**
- */
- public function deleteMessages($indices)
- {
- return $GLOBALS['registry']->hasMethod('mail/deleteMessages')
- ? $GLOBALS['registry']->call('mail/deleteMessages', array($this->_params['mailbox'], $indices))
- : false;
- }
-
- /**
- */
- public function moveMessages($indices, $folder)
- {
- return $GLOBALS['registry']->hasMethod('mail/moveMessages')
- ? $GLOBALS['registry']->call('mail/moveMessages', array($this->_params['mailbox'], $indices, $folder))
- : false;
- }
-
- /**
- */
- public function copyMessages($indices, $folder)
- {
- return $GLOBALS['registry']->hasMethod('mail/copyMessages')
- ? $GLOBALS['registry']->call('mail/copyMessages', array($this->_params['mailbox'], $indices, $folder))
- : false;
- }
-
- /**
- */
- public function setMessageFlags($indices, $flags)
- {
- return $GLOBALS['registry']->hasMethod('mail/flagMessages')
- ? $GLOBALS['registry']->call('mail/flagMessages', array($this->_params['mailbox'], $indices, $flags, true))
- : false;
- }
-
- /**
- */
- public function fetchEnvelope($indices)
- {
- return $GLOBALS['registry']->hasMethod('mail/msgEnvelope')
- ? $GLOBALS['registry']->call('mail/msgEnvelope', array($this->_params['mailbox'], $indices))
- : false;
- }
-
- /**
- */
- public function search($query)
- {
- return $GLOBALS['registry']->hasMethod('mail/searchMailbox')
- ? $GLOBALS['registry']->call('mail/searchMailbox', array($this->_params['mailbox'], $query))
- : false;
- }
-
- /**
- */
- public function getCache()
- {
- if (empty($_SESSION['ingo']['imapcache'][$this->_params['mailbox']])) {
- return false;
- }
- $ptr = &$_SESSION['ingo']['imapcache'][$this->_params['mailbox']];
-
- if ($this->_cacheId() != $ptr['id']) {
- $ptr = array();
- return false;
- }
-
- return $ptr['ts'];
- }
-
- /**
- */
- public function storeCache($timestamp)
- {
- if (!isset($_SESSION['ingo']['imapcache'])) {
- $_SESSION['ingo']['imapcache'] = array();
- }
-
- $_SESSION['ingo']['imapcache'][$this->_params['mailbox']] = array(
- 'id' => $this->_cacheId(),
- 'ts' => $timestamp
- );
- }
-
- /**
- */
- protected function _cacheId()
- {
- return $GLOBALS['registry']->hasMethod('mail/mailboxCacheId')
- ? $GLOBALS['registry']->call('mail/mailboxCacheId', array($this->_params['mailbox']))
- : time();
- }
-
-}
+++ /dev/null
-<?php
-/**
- * TODO
- */
-class Ingo_Script_imap_mock extends Ingo_Script_imap_api
-{
- /**
- * TODO
- */
- protected $_fixtures = array();
-
- /**
- * TODO
- */
- protected $_folders = array();
-
- /**
- * TODO
- */
- public function loadFixtures($dir)
- {
- $this->_fixtures = array();
-
- $dh = opendir($dir);
- while (($dent = readdir($dh)) !== false) {
- if (!in_array($dent, array('.', '..'))) {
- $this->_fixtures[$dent] = Horde_Mime_Part::parseHeaders(file_get_contents($dir . '/' . $dent));
- }
- }
- closedir($dh);
-
- $i = 0;
- foreach (array_keys($this->_fixtures) as $key) {
- $this->_folders['INBOX'][] = array('uid' => ++$i,
- 'fixture' => $key,
- 'deleted' => false);
- }
- }
-
- /**
- * TODO
- */
- public function hasMessage($fixture, $folder = 'INBOX')
- {
- if (empty($this->_folders[$folder])) {
- return false;
- }
- foreach ($this->_folders[$folder] as $message) {
- if ($message['fixture'] == $fixture) {
- return !$message['deleted'];
- }
- }
- return false;
- }
-
- /**
- * TODO
- */
- public function search(&$query)
- {
- $result = array();
- foreach ($this->_folders['INBOX'] as $message) {
- if ($message['deleted']) {
- continue;
- }
- if ($query->matches($this->_fixtures[$message['fixture']])) {
- $result[] = $message['uid'];
- }
- }
- return $result;
- }
-
- /**
- * TODO
- */
- public function deleteMessages($indices)
- {
- foreach (array_keys($this->_folders['INBOX']) as $i) {
- if (in_array($this->_folders['INBOX'][$i]['uid'], $indices)) {
- unset($this->_folders['INBOX'][$i]);
- }
- }
-
- // Force renumbering
- $this->_folders['INBOX'] = array_merge($this->_folders['INBOX'], array());
- }
-
- /**
- * TODO
- */
- public function moveMessages($indices, $folder)
- {
- foreach (array_keys($this->_folders['INBOX']) as $i) {
- if (in_array($this->_folders['INBOX'][$i]['uid'], $indices)) {
- $this->_folders[$folder][] = $this->_folders['INBOX'][$i];
- }
- }
- return $this->deleteMessages($indices);
- }
-
- /**
- * TODO
- */
- public function fetchEnvelope($indices)
- {
- $result = array();
-
- foreach ($indices as $uid) {
- foreach (array_keys($this->_folders['INBOX']) as $i) {
- if ($this->_folders['INBOX'][$i]['uid'] == $uid) {
- $fixture = $this->_fixtures[$this->_folders['INBOX'][$i]['fixture']];
- $result[] = array(
- 'envelope' => array(
- 'from' => $fixture->getValue('from'),
- 'uid' => $uid
- )
- );
- }
- }
- }
-
- return $result;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Ingo_Script_maildrop:: class represents a maildrop script generator.
- *
- * Copyright 2005-2007 Matt Weyland <mathias@weyland.ch>
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Matt Weyland <mathias@weyland.ch>
- * @package Ingo
- */
-
-/**
- * Additional storage action since maildrop does not support the
- * "c-flag" as in procmail.
- */
-define('MAILDROP_STORAGE_ACTION_STOREANDFORWARD', 100);
-
-/**
- */
-class Ingo_Script_maildrop extends Ingo_Script {
-
- /**
- * The list of actions allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_actions = array(
- Ingo_Storage::ACTION_KEEP,
- Ingo_Storage::ACTION_MOVE,
- Ingo_Storage::ACTION_DISCARD,
- Ingo_Storage::ACTION_REDIRECT,
- Ingo_Storage::ACTION_REDIRECTKEEP,
- Ingo_Storage::ACTION_REJECT,
- );
-
- /**
- * The categories of filtering allowed.
- *
- * @var array
- */
- var $_categories = array(
- Ingo_Storage::ACTION_BLACKLIST,
- Ingo_Storage::ACTION_WHITELIST,
- Ingo_Storage::ACTION_VACATION,
- Ingo_Storage::ACTION_FORWARD,
- Ingo_Storage::ACTION_SPAM,
- );
-
- /**
- * The types of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_types = array(
- Ingo_Storage::TYPE_HEADER,
- );
-
- /**
- * The list of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_tests = array(
- 'contains', 'not contain',
- 'is', 'not is',
- 'begins with','not begins with',
- 'ends with', 'not ends with',
- 'regex',
- 'matches', 'not matches',
- 'exists', 'not exist',
- 'less than', 'less than or equal to',
- 'equal', 'not equal',
- 'greater than', 'greater than or equal to',
- );
-
- /**
- * Can tests be case sensitive?
- *
- * @var boolean
- */
- var $_casesensitive = true;
-
- /**
- * Does the driver support the stop-script option?
- *
- * @var boolean
- */
- var $_supportStopScript = false;
-
- /**
- * Does the driver require a script file to be generated?
- *
- * @var boolean
- */
- var $_scriptfile = true;
-
- /**
- * The recipes that make up the code.
- *
- * @var array
- */
- var $_recipes = array();
-
- /**
- * Returns a script previously generated with generate().
- *
- * @return string The maildrop script.
- */
- function toCode()
- {
- $code = '';
- foreach ($this->_recipes as $item) {
- $code .= $item->generate() . "\n";
- }
- return rtrim($code);
- }
-
- /**
- * Generates the maildrop script to do the filtering specified in
- * the rules.
- *
- * @return string The maildrop script.
- */
- function generate()
- {
- $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
-
- $this->addItem(new Maildrop_Comment(_("maildrop script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
-
- /* Add variable information, if present. */
- if (!empty($this->_params['variables']) &&
- is_array($this->_params['variables'])) {
- foreach ($this->_params['variables'] as $key => $val) {
- $this->addItem(new Maildrop_Variable(array('name' => $key, 'value' => $val)));
- }
- }
-
- foreach ($filters->getFilterList() as $filter) {
- switch ($filter['action']) {
- case Ingo_Storage::ACTION_BLACKLIST:
- $this->generateBlacklist(!empty($filter['disable']));
- break;
-
- case Ingo_Storage::ACTION_WHITELIST:
- $this->generateWhitelist(!empty($filter['disable']));
- break;
-
- case Ingo_Storage::ACTION_FORWARD:
- $this->generateForward(!empty($filter['disable']));
- break;
-
- case Ingo_Storage::ACTION_VACATION:
- $this->generateVacation(!empty($filter['disable']));
- break;
-
- case Ingo_Storage::ACTION_SPAM:
- $this->generateSpamfilter(!empty($filter['disable']));
- break;
-
- default:
- if (in_array($filter['action'], $this->_actions)) {
- /* Create filter if using AND. */
- $recipe = new Maildrop_Recipe($filter, $this->_params);
- foreach ($filter['conditions'] as $condition) {
- $recipe->addCondition($condition);
- }
- $this->addItem(new Maildrop_Comment($filter['name'], !empty($filter['disable']), true));
- $this->addItem($recipe);
- }
- }
- }
-
- return $this->toCode();
- }
-
- /**
- * Generates the maildrop script to handle the blacklist specified in
- * the rules.
- *
- * @param boolean $disable Disable the blacklist?
- */
- function generateBlacklist($disable = false)
- {
- $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
- $bl_addr = $blacklist->getBlacklist();
- $bl_folder = $blacklist->getBlacklistFolder();
-
- $bl_type = (empty($bl_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
-
- if (!empty($bl_addr)) {
- $this->addItem(new Maildrop_Comment(_("Blacklisted Addresses"), $disable, true));
- $params = array('action-value' => $bl_folder,
- 'action' => $bl_type,
- 'disable' => $disable);
-
- foreach ($bl_addr as $address) {
- if (!empty($address)) {
- $recipe = new Maildrop_Recipe($params, $this->_params);
- $recipe->addCondition(array('field' => 'From', 'value' => $address));
- $this->addItem($recipe);
- }
- }
- }
- }
-
- /**
- * Generates the maildrop script to handle the whitelist specified in
- * the rules.
- *
- * @param boolean $disable Disable the whitelist?
- */
- function generateWhitelist($disable = false)
- {
- $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
- $wl_addr = $whitelist->getWhitelist();
-
- if (!empty($wl_addr)) {
- $this->addItem(new Maildrop_Comment(_("Whitelisted Addresses"), $disable, true));
- foreach ($wl_addr as $address) {
- if (!empty($address)) {
- $recipe = new Maildrop_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
- $recipe->addCondition(array('field' => 'From', 'value' => $address));
- $this->addItem($recipe);
- }
- }
- }
- }
-
- /**
- * Generates the maildrop script to handle mail forwards.
- *
- * @param boolean $disable Disable forwarding?
- */
- function generateForward($disable = false)
- {
- $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
- $addresses = $forward->getForwardAddresses();
-
- if (!empty($addresses)) {
- $this->addItem(new Maildrop_Comment(_("Forwards"), $disable, true));
- $params = array('action' => Ingo_Storage::ACTION_FORWARD,
- 'action-value' => $addresses,
- 'disable' => $disable);
- if ($forward->getForwardKeep()) {
- $params['action'] = MAILDROP_STORAGE_ACTION_STOREANDFORWARD;
- }
- $recipe = new Maildrop_Recipe($params, $this->_params);
- $recipe->addCondition(array('field' => 'From', 'value' => ''));
- $this->addItem($recipe);
- }
- }
-
- /**
- * Generates the maildrop script to handle vacation messages.
- *
- * @param boolean $disable Disable forwarding?
- */
- function generateVacation($disable = false)
- {
- $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
- $addresses = $vacation->getVacationAddresses();
- $actionval = array('addresses' => $addresses,
- 'subject' => $vacation->getVacationSubject(),
- 'days' => $vacation->getVacationDays(),
- 'reason' => $vacation->getVacationReason(),
- 'ignorelist' => $vacation->getVacationIgnorelist(),
- 'excludes' => $vacation->getVacationExcludes(),
- 'start' => $vacation->getVacationStart(),
- 'end' => $vacation->getVacationEnd());
-
- if (!empty($addresses)) {
- $this->addItem(new Maildrop_Comment(_("Vacation"), $disable, true));
- $params = array('action' => Ingo_Storage::ACTION_VACATION,
- 'action-value' => $actionval,
- 'disable' => $disable);
- $recipe = new Maildrop_Recipe($params, $this->_params);
- $this->addItem($recipe);
- }
- }
-
- /**
- * Generates the maildrop script to handle spam as identified by SpamAssassin
- *
- * @param boolean $disable Disable the spam-filter?
- */
- function generateSpamfilter($disable = false)
- {
- $spam = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
- if ($spam == false) {
- return;
- }
-
- $spam_folder = $spam->getSpamFolder();
- $spam_action = (empty($spam_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
-
- $this->addItem(new Maildrop_Comment(_("Spam Filter"), $disable, true));
-
- $params = array('action-value' => $spam_folder,
- 'action' => $spam_action,
- 'disable' => $disable);
- $recipe = new Maildrop_Recipe($params, $this->_params);
- if ($GLOBALS['conf']['spam']['compare'] == 'numeric') {
- $recipe->addCondition(array('match' => 'greater than or equal to',
- 'field' => $GLOBALS['conf']['spam']['header'],
- 'value' => $spam->getSpamLevel()));
- } elseif ($GLOBALS['conf']['spam']['compare'] == 'string') {
- $recipe->addCondition(array('match' => 'contains',
- 'field' => $GLOBALS['conf']['spam']['header'],
- 'value' => str_repeat($GLOBALS['conf']['spam']['char'], $spam->getSpamLevel())));
- }
-
- $this->addItem($recipe);
- }
-
- /**
- * Adds an item to the recipe list.
- *
- * @param object $item The item to add to the recipe list.
- * The object should have a generate() function.
- */
- function addItem($item)
- {
- $this->_recipes[] = $item;
- }
-
-}
-
-/**
- * The Maildrop_Comment:: class represents a maildrop comment. This is
- * a pretty simple class, but it makes the code in Ingo_Script_maildrop::
- * cleaner as it provides a generate() function and can be added to the
- * recipe list the same way as a recipe can be.
- *
- * @author Matt Weyland <mathias@weyland.ch>
- * @since Ingo 1.1
- * @package Ingo
- */
-class Maildrop_Comment {
-
- /**
- * The comment text.
- *
- * @var string
- */
- var $_comment = '';
-
- /**
- * Constructs a new maildrop comment.
- *
- * @param string $comment Comment to be generated.
- * @param boolean $disable Output 'DISABLED' comment?
- * @param boolean $header Output a 'header' comment?
- */
- function Maildrop_Comment($comment, $disable = false, $header = false)
- {
- if ($disable) {
- $comment = _("DISABLED: ") . $comment;
- }
-
- if ($header) {
- $this->_comment .= "##### $comment #####";
- } else {
- $this->_comment .= "# $comment";
- }
- }
-
- /**
- * Returns the comment stored by this object.
- *
- * @return string The comment stored by this object.
- */
- function generate()
- {
- return $this->_comment;
- }
-
-}
-
-/**
- * The Maildrop_Recipe:: class represents a maildrop recipe.
- *
- * @author Matt Weyland <mathias@weyland.ch>
- * @since Ingo 1.1
- * @package Ingo
- */
-class Maildrop_Recipe {
-
- var $_action = array();
- var $_conditions = array();
- var $_disable = '';
- var $_flags = '';
- var $_params = array();
- var $_combine = '';
- var $_valid = true;
-
- var $_operators = array(
- 'less than' => '<',
- 'less than or equal to' => '<=',
- 'equal' => '==',
- 'not equal' => '!=',
- 'greater than' => '>',
- 'greater than or equal to' => '>=',
- );
-
- /**
- * Constructs a new maildrop recipe.
- *
- * @param array $params Array of parameters.
- * REQUIRED FIELDS:
- * 'action'
- * OPTIONAL FIELDS:
- * 'action-value' (only used if the
- * 'action' requires it)
- * @param array $scriptparams Array of parameters passed to
- * Ingo_Script_maildrop::.
- */
- function Maildrop_Recipe($params = array(), $scriptparams = array())
- {
- $this->_disable = !empty($params['disable']);
- $this->_params = $scriptparams;
- $this->_action[] = 'exception {';
-
- switch ($params['action']) {
- case Ingo_Storage::ACTION_KEEP:
- $this->_action[] = ' to "${DEFAULT}"';
- break;
-
- case Ingo_Storage::ACTION_MOVE:
- $this->_action[] = ' to ' . $this->maildropPath($params['action-value']);
- break;
-
- case Ingo_Storage::ACTION_DISCARD:
- $this->_action[] = ' exit';
- break;
-
- case Ingo_Storage::ACTION_REDIRECT:
- $this->_action[] = ' to "! ' . $params['action-value'] . '"';
- break;
-
- case Ingo_Storage::ACTION_REDIRECTKEEP:
- $this->_action[] = ' cc "! ' . $params['action-value'] . '"';
- $this->_action[] = ' to "${DEFAULT}"';
- break;
-
- case Ingo_Storage::ACTION_REJECT:
- $this->_action[] = ' EXITCODE=77'; # EX_NOPERM (permanent failure)
- $this->_action[] = ' echo "5.7.1 ' . $params['action-value'] . '"';
- $this->_action[] = ' exit';
- break;
-
- case Ingo_Storage::ACTION_VACATION:
- $from = '';
- foreach ($params['action-value']['addresses'] as $address) {
- $from = $address;
- }
-
- /**
- * @TODO
- *
- * Exclusion and listfilter
- */
- $exclude = '';
- foreach ($params['action-value']['excludes'] as $address) {
- $exclude .= $address . ' ';
- }
-
- $start = strftime($params['action-value']['start']);
- if ($start === false) {
- $start = 0;
- }
- $end = strftime($params['action-value']['end']);
- if ($end === false) {
- $end = 0;
- }
- $days = strftime($params['action-value']['days']);
- if ($days === false) {
- // Set to same value as $_days in ingo/lib/Storage.php
- $days = 7;
- }
-
- // Writing vacation.msg file
- $reason = Horde_Mime::encode($params['action-value']['reason'], Horde_Nls::getCharset());
- $driver = Ingo::getDriver();
- $driver->_connect();
- $result = $driver->_vfs->writeData($driver->_params['vfs_path'], 'vacation.msg', $reason, true);
-
- // Rule : Do not send responses to bulk or list messages
- if ($params['action-value']['ignorelist'] == 1) {
- $params['combine'] = Ingo_Storage::COMBINE_ALL;
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Precedence: (bulk|list|junk)/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<#@\[\]>/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<>/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^From:.*MAILER-DAEMON/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-ClamAV-Notice-Flag: *YES/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Content-Type:.*message\/delivery-status/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery Status Notification/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Undelivered Mail Returned to Sender/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery failure/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Message delay/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail Delivery Subsystem/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail System Error.*Returned Mail/'));
- $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-Spam-Flag: YES/ '));
- } else {
- $this->addCondition(array('field' => 'From', 'value' => ''));
- }
-
- // Rule : Start/End of vacation
- if (($start != 0) && ($end !== 0)) {
- $this->_action[] = ' flock "vacation.lock" {';
- $this->_action[] = ' current_time=time';
- $this->_action[] = ' if ( \ ';
- $this->_action[] = ' ($current_time >= ' . $start . ') && \ ';
- $this->_action[] = ' ($current_time <= ' . $end . ')) ';
- $this->_action[] = ' {';
- }
- $this->_action[] = " cc \"| mailbot -D " . $params['action-value']['days'] . " -c '" . Horde_Nls::getCharset() . "' -t \$HOME/vacation.msg -d \$HOME/vacation -A 'From: $from' -s '" . Horde_Mime::encode($params['action-value']['subject'], Horde_Nls::getCharset()) . "' /usr/sbin/sendmail -t \"";
- if (($start != 0) && ($end !== 0)) {
- $this->_action[] = ' }';
- $this->_action[] = ' }';
- }
-
- break;
-
- case Ingo_Storage::ACTION_FORWARD:
- case MAILDROP_STORAGE_ACTION_STOREANDFORWARD:
- foreach ($params['action-value'] as $address) {
- if (!empty($address)) {
- $this->_action[] = ' cc "! ' . $address . '"';
- }
- }
-
- /* The 'to' must be the last action, because maildrop
- * stops processing after it. */
- if ($params['action'] == MAILDROP_STORAGE_ACTION_STOREANDFORWARD) {
- $this->_action[] = ' to "${DEFAULT}"';
- } else {
- $this->_action[] = ' exit';
- }
- break;
-
- default:
- $this->_valid = false;
- break;
- }
-
- $this->_action[] = '}';
-
- if (isset($params['combine']) &&
- ($params['combine'] == Ingo_Storage::COMBINE_ALL)) {
- $this->_combine = '&& ';
- } else {
- $this->_combine = '|| ';
- }
- }
-
- /**
- * Adds a flag to the recipe.
- *
- * @param string $flag String of flags to append to the current flags.
- */
- function addFlag($flag)
- {
- $this->_flags .= $flag;
- }
-
- /**
- * Adds a condition to the recipe.
- *
- * @param optonal array $condition Array of parameters. Required keys
- * are 'field' and 'value'. 'case' is
- * an optional keys.
- */
- function addCondition($condition = array())
- {
- $flag = (!empty($condition['case'])) ? 'D' : '';
- if (empty($this->_conditions)) {
- $this->addFlag($flag);
- }
-
- $string = '';
- $extra = '';
-
- $match = (isset($condition['match'])) ? $condition['match'] : null;
- // negate tests starting with 'not ', except 'not equals', which simply uses the != operator
- if ($match != 'not equal' && substr($match, 0, 4) == 'not ') {
- $string .= '! ';
- }
-
- // convert 'field' to PCRE pattern matching
- if (strpos($condition['field'], ',') == false) {
- $string .= '/^' . $condition['field'] . ':\\s*';
- } else {
- $string .= '/^(' . str_replace(',', '|', $condition['field']) . '):\\s*';
- }
-
- switch ($match) {
- case 'not regex':
- case 'regex':
- $string .= $condition['value'] . '/:h';
- break;
-
- case 'filter':
- $string = $condition['value'];
- break;
-
- case 'exists':
- case 'not exist':
- // Just run a match for the header name
- $string .= '/:h';
- break;
-
- case 'less than or equal to':
- case 'less than':
- case 'equal':
- case 'not equal':
- case 'greater than or equal to':
- case 'greater than':
- $string .= '(\d+(\.\d+)?)/:h';
- $extra = ' && $MATCH1 ' . $this->_operators[$match] . ' ' . (int)$condition['value'];
- break;
-
- case 'begins with':
- case 'not begins with':
- $string .= preg_quote($condition['value'], '/') . '/:h';
- break;
-
- case 'ends with':
- case 'not ends with':
- $string .= '.*' . preg_quote($condition['value'], '/') . '$/:h';
- break;
-
- case 'is':
- case 'not is':
- $string .= preg_quote($condition['value'], '/') . '$/:h';
- break;
-
- case 'matches':
- case 'not matches':
- $string .= str_replace(array('\\*', '\\?'), array('.*', '.'), preg_quote($condition['value'], '/') . '$') . '/:h';
- break;
-
- case 'contains':
- case 'not contain':
- default:
- $string .= '.*' . preg_quote($condition['value'], '/') . '/:h';
- break;
- }
-
- $this->_conditions[] = array('condition' => $string, 'flags' => $flag, 'extra' => $extra);
- }
-
- /**
- * Generates maildrop code to represent the recipe.
- *
- * @return string maildrop code to represent the recipe.
- */
- function generate()
- {
- $text = array();
-
- if (!$this->_valid) {
- return '';
- }
-
- if (count($this->_conditions) > 0) {
-
- $text[] = "if( \\";
-
- $nest = false;
- foreach ($this->_conditions as $condition) {
- $cond = $nest ? $this->_combine : ' ';
- $text[] = $cond . $condition['condition'] . $condition['flags'] . $condition['extra'] . " \\";
- $nest = true;
- }
-
- $text[] = ')';
- }
-
- foreach ($this->_action as $val) {
- $text[] = $val;
- }
-
- if ($this->_disable) {
- $code = '';
- foreach ($text as $val) {
- $comment = new Maildrop_Comment($val);
- $code .= $comment->generate() . "\n";
- }
- return $code . "\n";
- } else {
- return implode("\n", $text) . "\n";
- }
- }
-
- /**
- * Returns a maildrop-ready mailbox path, converting IMAP folder pathname
- * conventions as necessary.
- *
- * @param string $folder The IMAP folder name.
- *
- * @return string The maildrop mailbox path.
- */
- function maildropPath($folder)
- {
- /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
- if (isset($this->_params) &&
- ($this->_params['path_style'] == 'maildir')) {
- if (empty($folder) || ($folder == 'INBOX')) {
- return '"${DEFAULT}"';
- }
- if ($this->_params['strip_inbox'] &&
- substr($folder, 0, 6) == 'INBOX.') {
- $folder = substr($folder, 6);
- }
- return '"${DEFAULT}/.' . $folder . '/"';
- } else {
- if (empty($folder) || ($folder == 'INBOX')) {
- return '${DEFAULT}';
- }
- return str_replace(' ', '\ ', $folder);
- }
- }
-
-}
-
-/**
- * The Maildrop_Variable:: class represents a Maildrop variable.
- *
- * @author Matt Weyland <mathias@weyland.ch>
- * @since Ingo 1.1
- * @package Ingo
- */
-class Maildrop_Variable {
-
- var $_name;
- var $_value;
-
- /**
- * Constructs a new maildrop variable.
- *
- * @param array $params Array of parameters. Expected fields are 'name'
- * and 'value'.
- */
- function Maildrop_Variable($params = array())
- {
- $this->_name = $params['name'];
- $this->_value = $params['value'];
- }
-
- /**
- * Generates maildrop code to represent the variable.
- *
- * @return string maildrop code to represent the variable.
- */
- function generate()
- {
- return $this->_name . '=' . $this->_value . "\n";
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Ingo_Script_procmail:: class represents a Procmail script generator.
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Brent J. Nordquist <bjn@horde.org>
- * @author Ben Chavet <ben@horde.org>
- * @package Ingo
- */
-class Ingo_Script_procmail extends Ingo_Script {
-
- /**
- * The list of actions allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_actions = array(
- Ingo_Storage::ACTION_KEEP,
- Ingo_Storage::ACTION_MOVE,
- Ingo_Storage::ACTION_DISCARD,
- Ingo_Storage::ACTION_REDIRECT,
- Ingo_Storage::ACTION_REDIRECTKEEP,
- Ingo_Storage::ACTION_REJECT
- );
-
- /**
- * The categories of filtering allowed.
- *
- * @var array
- */
- var $_categories = array(
- Ingo_Storage::ACTION_BLACKLIST,
- Ingo_Storage::ACTION_WHITELIST,
- Ingo_Storage::ACTION_VACATION,
- Ingo_Storage::ACTION_FORWARD
- );
-
- /**
- * The types of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_types = array(
- Ingo_Storage::TYPE_HEADER,
- Ingo_Storage::TYPE_BODY
- );
-
- /**
- * A list of any special types that this driver supports.
- *
- * @var array
- */
- var $_special_types = array(
- 'Destination',
- );
-
- /**
- * The list of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_tests = array(
- 'contains',
- 'not contain',
- 'begins with',
- 'not begins with',
- 'ends with',
- 'not ends with',
- 'regex'
- );
-
- /**
- * Can tests be case sensitive?
- *
- * @var boolean
- */
- var $_casesensitive = true;
-
- /**
- * Does the driver support the stop-script option?
- *
- * @var boolean
- */
- var $_supportStopScript = true;
-
- /**
- * Does the driver require a script file to be generated?
- *
- * @var boolean
- */
- var $_scriptfile = true;
-
- /**
- * The recipes that make up the code.
- *
- * @var array
- */
- var $_recipes = array();
-
- /**
- * Returns a script previously generated with generate().
- *
- * @return string The procmail script.
- */
- function toCode()
- {
- $code = '';
- foreach ($this->_recipes as $item) {
- $code .= $item->generate() . "\n";
- }
-
- // If an external delivery program is used, add final rule
- // to deliver to $DEFAULT
- if (isset($this->_params['delivery_agent'])) {
- $code .= ":0 w\n";
- $code .= isset($this->_params['delivery_mailbox_prefix']) ?
- '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' :
- '| ' . $this->_params['delivery_agent'] . ' $DEFAULT';
- }
-
- return rtrim($code) . "\n";
- }
-
- /**
- * Generates the procmail script to do the filtering specified in the
- * rules.
- *
- * @return string The procmail script.
- */
- function generate()
- {
- $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
-
- $this->addItem(new Procmail_Comment(_("procmail script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
-
- /* Add variable information, if present. */
- if (!empty($this->_params['variables']) &&
- is_array($this->_params['variables'])) {
- foreach ($this->_params['variables'] as $key => $val) {
- $this->addItem(new Procmail_Variable(array('name' => $key, 'value' => $val)));
- }
- }
-
- foreach ($filters->getFilterList() as $filter) {
- switch ($filter['action']) {
- case Ingo_Storage::ACTION_BLACKLIST:
- $this->generateBlacklist(!empty($filter['disable']));
- break;
-
- case Ingo_Storage::ACTION_WHITELIST:
- $this->generateWhitelist(!empty($filter['disable']));
- break;
-
- case Ingo_Storage::ACTION_VACATION:
- $this->generateVacation(!empty($filter['disable']));
- break;
-
- case Ingo_Storage::ACTION_FORWARD:
- $this->generateForward(!empty($filter['disable']));
- break;
-
- default:
- if (in_array($filter['action'], $this->_actions)) {
- /* Create filter if using AND. */
- if ($filter['combine'] == Ingo_Storage::COMBINE_ALL) {
- $recipe = new Procmail_Recipe($filter, $this->_params);
- if (!$filter['stop']) {
- $recipe->addFlag('c');
- }
- foreach ($filter['conditions'] as $condition) {
- $recipe->addCondition($condition);
- }
- $this->addItem(new Procmail_Comment($filter['name'], !empty($filter['disable']), true));
- $this->addItem($recipe);
- } else {
- /* Create filter if using OR */
- $this->addItem(new Procmail_Comment($filter['name'], !empty($filter['disable']), true));
- $loop = 0;
- foreach ($filter['conditions'] as $condition) {
- $recipe = new Procmail_Recipe($filter, $this->_params);
- if ($loop++) {
- $recipe->addFlag('E');
- }
- if (!$filter['stop']) {
- $recipe->addFlag('c');
- }
- $recipe->addCondition($condition);
- $this->addItem($recipe);
- }
- }
- }
- }
- }
-
- return $this->toCode();
- }
-
- /**
- * Generates the procmail script to handle the blacklist specified in
- * the rules.
- *
- * @param boolean $disable Disable the blacklist?
- */
- function generateBlacklist($disable = false)
- {
- $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
- $bl_addr = $blacklist->getBlacklist();
- $bl_folder = $blacklist->getBlacklistFolder();
-
- $bl_type = (empty($bl_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
-
- if (!empty($bl_addr)) {
- $this->addItem(new Procmail_Comment(_("Blacklisted Addresses"), $disable, true));
- $params = array('action-value' => $bl_folder,
- 'action' => $bl_type,
- 'disable' => $disable);
-
- foreach ($bl_addr as $address) {
- if (!empty($address)) {
- $recipe = new Procmail_Recipe($params, $this->_params);
- $recipe->addCondition(array('field' => 'From', 'value' => $address, 'match' => 'address'));
- $this->addItem($recipe);
- }
- }
- }
- }
-
- /**
- * Generates the procmail script to handle the whitelist specified in
- * the rules.
- *
- * @param boolean $disable Disable the whitelist?
- */
- function generateWhitelist($disable = false)
- {
- $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
- $wl_addr = $whitelist->getWhitelist();
-
- if (!empty($wl_addr)) {
- $this->addItem(new Procmail_Comment(_("Whitelisted Addresses"), $disable, true));
- foreach ($wl_addr as $address) {
- if (!empty($address)) {
- $recipe = new Procmail_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
- $recipe->addCondition(array('field' => 'From', 'value' => $address, 'match' => 'address'));
- $this->addItem($recipe);
- }
- }
- }
- }
-
- /**
- * Generates the procmail script to handle vacation.
- *
- * @param boolean $disable Disable vacation?
- */
- function generateVacation($disable = false)
- {
- $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
- $addresses = $vacation->getVacationAddresses();
- $actionval = array(
- 'addresses' => $addresses,
- 'subject' => $vacation->getVacationSubject(),
- 'days' => $vacation->getVacationDays(),
- 'reason' => $vacation->getVacationReason(),
- 'ignorelist' => $vacation->getVacationIgnorelist(),
- 'excludes' => $vacation->getVacationExcludes(),
- 'start' => $vacation->getVacationStart(),
- 'end' => $vacation->getVacationEnd(),
- );
-
- if (!empty($addresses)) {
- $this->addItem(new Procmail_Comment(_("Vacation"), $disable, true));
- $params = array('action' => Ingo_Storage::ACTION_VACATION,
- 'action-value' => $actionval,
- 'disable' => $disable);
- $recipe = new Procmail_Recipe($params, $this->_params);
- $this->addItem($recipe);
- }
- }
-
- /**
- * Generates the procmail script to handle mail forwards.
- *
- * @param boolean $disable Disable forwarding?
- */
- function generateForward($disable = false)
- {
- $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
- $addresses = $forward->getForwardAddresses();
-
- if (!empty($addresses)) {
- $this->addItem(new Procmail_Comment(_("Forwards"), $disable, true));
- $params = array('action' => Ingo_Storage::ACTION_FORWARD,
- 'action-value' => $addresses,
- 'disable' => $disable);
- $recipe = new Procmail_Recipe($params, $this->_params);
- if ($forward->getForwardKeep()) {
- $recipe->addFlag('c');
- }
- $this->addItem($recipe);
- }
- }
-
- /**
- * Adds an item to the recipe list.
- *
- * @param object $item The item to add to the recipe list.
- * The object should have a generate() function.
- */
- function addItem($item)
- {
- $this->_recipes[] = $item;
- }
-
-}
-
-/**
- * The Procmail_Comment:: class represents a Procmail comment. This is
- * a pretty simple class, but it makes the code in Ingo_Script_procmail::
- * cleaner as it provides a generate() function and can be added to the
- * recipe list the same way as a recipe can be.
- *
- * @author Ben Chavet <ben@chavet.net>
- * @since Ingo 1.0
- * @package Ingo
- */
-class Procmail_Comment {
-
- /**
- * The comment text.
- *
- * @var string
- */
- var $_comment = '';
-
- /**
- * Constructs a new procmail comment.
- *
- * @param string $comment Comment to be generated.
- * @param boolean $disable Output 'DISABLED' comment?
- * @param boolean $header Output a 'header' comment?
- */
- function Procmail_Comment($comment, $disable = false, $header = false)
- {
- if ($disable) {
- $comment = _("DISABLED: ") . $comment;
- }
-
- if ($header) {
- $this->_comment .= "##### $comment #####";
- } else {
- $this->_comment .= "# $comment";
- }
- }
-
- /**
- * Returns the comment stored by this object.
- *
- * @return string The comment stored by this object.
- */
- function generate()
- {
- return $this->_comment;
- }
-
-}
-
-/**
- * The Procmail_Recipe:: class represents a Procmail recipe.
- *
- * @author Ben Chavet <ben@chavet.net>
- * @since Ingo 1.0
- * @package Ingo
- */
-class Procmail_Recipe {
-
- var $_action = array();
- var $_conditions = array();
- var $_disable = '';
- var $_flags = '';
- var $_params = array(
- 'date' => 'date',
- 'echo' => 'echo',
- 'ls' => 'ls'
- );
- var $_valid = true;
-
- /**
- * Constructs a new procmail recipe.
- *
- * @param array $params Array of parameters.
- * REQUIRED FIELDS:
- * 'action'
- * OPTIONAL FIELDS:
- * 'action-value' (only used if the
- * 'action' requires it)
- * @param array $scriptparams Array of parameters passed to
- * Ingo_Script_procmail::.
- */
- function Procmail_Recipe($params = array(), $scriptparams = array())
- {
- $this->_disable = !empty($params['disable']);
- $this->_params = array_merge($this->_params, $scriptparams);
-
- switch ($params['action']) {
- case Ingo_Storage::ACTION_KEEP:
- // Note: you may have to set the DEFAULT variable in your
- // backend configuration.
- if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
- $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
- } elseif (isset($this->_params['delivery_agent'])) {
- $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' $DEFAULT';
- } else {
- $this->_action[] = '$DEFAULT';
- }
- break;
-
- case Ingo_Storage::ACTION_MOVE:
- if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
- $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . $this->procmailPath($params['action-value']);
- } elseif (isset($this->_params['delivery_agent'])) {
- $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->procmailPath($params['action-value']);
- } else {
- $this->_action[] = $this->procmailPath($params['action-value']);
- }
- break;
-
- case Ingo_Storage::ACTION_DISCARD:
- $this->_action[] = '/dev/null';
- break;
-
- case Ingo_Storage::ACTION_REDIRECT:
- $this->_action[] = '! ' . $params['action-value'];
- break;
-
- case Ingo_Storage::ACTION_REDIRECTKEEP:
- $this->_action[] = '{';
- $this->_action[] = ' :0 c';
- $this->_action[] = ' ! ' . $params['action-value'];
- $this->_action[] = '';
- $this->_action[] = ' :0' . (isset($this->_params['delivery_agent']) ? ' w' : '');
- if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
- $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
- } elseif (isset($this->_params['delivery_agent'])) {
- $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
- } else {
- $this->_action[] = ' $DEFAULT';
- }
- $this->_action[] = '}';
- break;
-
- case Ingo_Storage::ACTION_REJECT:
- $this->_action[] = '{';
- $this->_action[] = ' EXITCODE=' . $params['action-value'];
- $this->_action[] = ' HOST="no.address.here"';
- $this->_action[] = '}';
- break;
-
- case Ingo_Storage::ACTION_VACATION:
- $days = $params['action-value']['days'];
- $timed = !empty($params['action-value']['start']) &&
- !empty($params['action-value']['end']);
- $this->_action[] = '{';
- foreach ($params['action-value']['addresses'] as $address) {
- if (!empty($address)) {
- $this->_action[] = ' FILEDATE=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
- . $this->_params['ls'] . ' -lcn --time-style=+%s ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' | '
- . 'awk \'{ print $6 + (' . $days * 86400 . ') }\'`';
- $this->_action[] = ' DATE=`' . $this->_params['date'] . ' +%s`';
- $this->_action[] = ' DUMMY=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
- . 'test $FILEDATE -le $DATE && '
- . 'rm ${VACATION_DIR:-.}/\'.vacation.' . $address . '\'`';
- if ($timed) {
- $this->_action[] = ' START=' . $params['action-value']['start'];
- $this->_action[] = ' END=' . $params['action-value']['end'];
- }
- $this->_action[] = '';
- $this->_action[] = ' :0 h';
- $this->_action[] = ' SUBJECT=| formail -xSubject:';
- $this->_action[] = '';
- $this->_action[] = ' :0 Whc: ${VACATION_DIR:-.}/vacation.lock';
- if ($timed) {
- $this->_action[] = ' * ? test $DATE -gt $START && test $END -gt $DATE';
- $this->_action[] = ' {';
- $this->_action[] = ' :0 Whc';
- }
- $this->_action[] = ' * ^TO_' . $address;
- $this->_action[] = ' * !^X-Loop: ' . $address;
- $this->_action[] = ' * !^X-Spam-Flag: YES';
- if (count($params['action-value']['excludes']) > 0) {
- foreach ($params['action-value']['excludes'] as $exclude) {
- if (!empty($exclude)) {
- $this->_action[] = ' * !^From.*' . $exclude;
- }
- }
- }
- if ($params['action-value']['ignorelist']) {
- $this->_action[] = ' * !^FROM_DAEMON';
- }
- $this->_action[] = ' | formail -rD 8192 ${VACATION_DIR:-.}/.vacation.' . $address;
- $this->_action[] = ' :0 ehc';
- $this->_action[] = ' | (formail -rI"Precedence: junk" \\';
- $this->_action[] = ' -a"From: <' . $address . '>" \\';
- $this->_action[] = ' -A"X-Loop: ' . $address . '" \\';
- if (Horde_Mime::is8bit($params['action-value']['reason'])) {
- $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', Horde_Nls::getCharset()) . '" \\';
- $this->_action[] = ' -i"Content-Transfer-Encoding: quoted-printable" \\';
- $this->_action[] = ' -i"Content-Type: text/plain; charset=' . Horde_Nls::getCharset() . '" ; \\';
- $reason = Horde_Mime::quotedPrintableEncode($params['action-value']['reason'], "\n");
- } else {
- $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', Horde_Nls::getCharset()) . '" ; \\';
- $reason = $params['action-value']['reason'];
- }
- $reason = addcslashes($reason, "\\\n\r\t\"`");
- $this->_action[] = ' ' . $this->_params['echo'] . ' -e "' . $reason . '" \\';
- $this->_action[] = ' ) | $SENDMAIL -f' . $address . ' -oi -t';
- $this->_action[] = '';
- $this->_action[] = ' :0';
- $this->_action[] = ' /dev/null';
- if ($timed) {
- $this->_action[] = ' }';
- }
- }
- }
- $this->_action[] = '}';
- break;
-
- case Ingo_Storage::ACTION_FORWARD:
- /* Make sure that we prevent mail loops using 3 methods.
- *
- * First, we call sendmail -f to set the envelope sender to be the
- * same as the original sender, so bounces will go to the original
- * sender rather than to us. This unfortunately triggers lots of
- * Authentication-Warning: messages in sendmail's logs.
- *
- * Second, add an X-Loop header, to handle the case where the
- * address we forward to forwards back to us.
- *
- * Third, don't forward mailer daemon messages (i.e., bounces).
- * Method 1 above should make this redundant, unless we're sending
- * mail from this account and have a bad forward-to account.
- *
- * Get the from address, saving a call to formail if possible.
- * The procmail code for doing this is borrowed from the
- * Procmail Library Project, http://pm-lib.sourceforge.net/.
- * The Ingo project has the permission to use Procmail Library code
- * under Apache licence v 1.x or any later version.
- * Permission obtained 2006-04-04 from Author Jari Aalto. */
- $this->_action[] = '{';
- $this->_action[] = ' :0 ';
- $this->_action[] = ' *$ ! ^From *\/[^ ]+';
- $this->_action[] = ' *$ ! ^Sender: *\/[^ ]+';
- $this->_action[] = ' *$ ! ^From: *\/[^ ]+';
- $this->_action[] = ' *$ ! ^Reply-to: *\/[^ ]+';
- $this->_action[] = ' {';
- $this->_action[] = ' OUTPUT = `formail -zxFrom:`';
- $this->_action[] = ' }';
- $this->_action[] = ' :0 E';
- $this->_action[] = ' {';
- $this->_action[] = ' OUTPUT = $MATCH';
- $this->_action[] = ' }';
- $this->_action[] = '';
-
- /* Forward to each address on our list. */
- foreach ($params['action-value'] as $address) {
- if (!empty($address)) {
- $this->_action[] = ' :0 c';
- $this->_action[] = ' * !^FROM_MAILER';
- $this->_action[] = ' * !^X-Loop: to-' . $address;
- $this->_action[] = ' | formail -A"X-Loop: to-' . $address . '" | $SENDMAIL -oi -f $OUTPUT ' . $address;
- }
- }
-
- /* In case of mail loop or bounce, store a copy locally. Note
- * that if we forward to more than one address, only a mail loop
- * on the last address will cause a local copy to be saved. TODO:
- * The next two lines are redundant (and create an extra copy of
- * the message) if "Keep a copy of messages in this account" is
- * checked. */
- $this->_action[] = ' :0 E' . (isset($this->_params['delivery_agent']) ? 'w' : '');
- if (isset($this->_params['delivery_agent'])) {
- $this->_action[] = isset($this->_params['delivery_mailbox_prefix']) ?
- ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' :
- ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
- } else {
- $this->_action[] = ' $DEFAULT';
- }
- $this->_action[] = ' :0 ';
- $this->_action[] = ' /dev/null';
- $this->_action[] = '}';
- break;
-
- default:
- $this->_valid = false;
- break;
- }
- }
-
- /**
- * Adds a flag to the recipe.
- *
- * @param string $flag String of flags to append to the current flags.
- */
- function addFlag($flag)
- {
- $this->_flags .= $flag;
- }
-
- /**
- * Adds a condition to the recipe.
- *
- * @param array $condition Array of parameters. Required keys are 'field'
- * and 'value'. 'case' is an optional key.
- */
- function addCondition($condition = array())
- {
- $flag = !empty($condition['case']) ? 'D' : '';
- $match = isset($condition['match']) ? $condition['match'] : null;
- $string = '';
- $prefix = '';
-
- switch ($condition['field']) {
- case 'Destination':
- $string = '^TO_';
- break;
-
- case 'Body':
- $flag .= 'B';
- break;
-
- default:
- // convert 'field' to PCRE pattern matching
- if (strpos($condition['field'], ',') == false) {
- $string = '^' . $condition['field'] . ':';
- } else {
- $string .= '/^(' . str_replace(',', '|', $condition['field']) . '):';
- }
- $prefix = ' ';
- }
-
- $reverseCondition = false;
- switch ($match) {
- case 'regex':
- $string .= $prefix . $condition['value'];
- break;
-
- case 'address':
- $string .= '(.*\<)?' . quotemeta($condition['value']);
- break;
-
- case 'not begins with':
- $reverseCondition = true;
- // fall through
- case 'begins with':
- $string .= $prefix . quotemeta($condition['value']);
- break;
-
- case 'not ends with':
- $reverseCondition = true;
- // fall through
- case 'ends with':
- $string .= '.*' . quotemeta($condition['value']) . '$';
- break;
-
- case 'not contain':
- $reverseCondition = true;
- // fall through
- case 'contains':
- default:
- $string .= '.*' . quotemeta($condition['value']);
- break;
- }
-
- $this->_conditions[] = array('condition' => ($reverseCondition ? '* !' : '* ') . $string,
- 'flags' => $flag);
- }
-
- /**
- * Generates procmail code to represent the recipe.
- *
- * @return string Procmail code to represent the recipe.
- */
- function generate()
- {
- $nest = 0;
- $prefix = '';
- $text = array();
-
- if (!$this->_valid) {
- return '';
- }
-
- // Set the global flags for the whole rule, each condition
- // will add its own (such as Body or Case Sensitive)
- $global = $this->_flags;
- if (isset($this->_conditions[0])) {
- $global .= $this->_conditions[0]['flags'];
- }
- $text[] = ':0 ' . $global . (isset($this->_params['delivery_agent']) ? 'w' : '');
- foreach ($this->_conditions as $condition) {
- if ($nest > 0) {
- $text[] = str_repeat(' ', $nest - 1) . '{';
- $text[] = str_repeat(' ', $nest) . ':0 ' . $condition['flags'];
- $text[] = str_repeat(' ', $nest) . $condition['condition'];
- } else {
- $text[] = $condition['condition'];
- }
- $nest++;
- }
-
- if (--$nest > 0) {
- $prefix = str_repeat(' ', $nest);
- }
- foreach ($this->_action as $val) {
- $text[] = $prefix . $val;
- }
-
- for ($i = $nest; $i > 0; $i--) {
- $text[] = str_repeat(' ', $i - 1) . '}';
- }
-
- if ($this->_disable) {
- $code = '';
- foreach ($text as $val) {
- $comment = new Procmail_Comment($val);
- $code .= $comment->generate() . "\n";
- }
- return $code . "\n";
- } else {
- return implode("\n", $text) . "\n";
- }
- }
-
- /**
- * Returns a procmail-ready mailbox path, converting IMAP folder
- * pathname conventions as necessary.
- *
- * @param string $folder The IMAP folder name.
- *
- * @return string The procmail mailbox path.
- */
- function procmailPath($folder)
- {
- /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
- if (isset($this->_params) &&
- ($this->_params['path_style'] == 'maildir')) {
- if (empty($folder) || ($folder == 'INBOX')) {
- return '$DEFAULT';
- }
- if (substr($folder, 0, 6) == 'INBOX.') {
- $folder = substr($folder, 6);
- }
- return '"$DEFAULT/.' . escapeshellcmd($folder) . '/"';
- } else {
- if (empty($folder) || ($folder == 'INBOX')) {
- return '$DEFAULT';
- }
- return str_replace(' ', '\ ', escapeshellcmd($folder));
- }
- }
-
-}
-
-/**
- * The Procmail_Variable:: class represents a Procmail variable.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @since Ingo 1.0
- * @package Ingo
- */
-class Procmail_Variable {
-
- var $_name;
- var $_value;
-
- /**
- * Constructs a new procmail variable.
- *
- * @param array $params Array of parameters. Expected fields are 'name'
- * and 'value'.
- */
- function Procmail_Variable($params = array())
- {
- $this->_name = $params['name'];
- $this->_value = $params['value'];
- }
-
- /**
- * Generates procmail code to represent the variable.
- *
- * @return string Procmail code to represent the variable.
- */
- function generate()
- {
- return $this->_name . '=' . $this->_value . "\n";
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Ingo_Script_sieve class represents a Sieve Script.
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Ingo_Script_sieve extends Ingo_Script {
-
- /**
- * The list of actions allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_actions = array(
- Ingo_Storage::ACTION_KEEP,
- Ingo_Storage::ACTION_MOVE,
- Ingo_Storage::ACTION_DISCARD,
- Ingo_Storage::ACTION_REDIRECT,
- Ingo_Storage::ACTION_REDIRECTKEEP,
- Ingo_Storage::ACTION_MOVEKEEP,
- Ingo_Storage::ACTION_REJECT,
- Ingo_Storage::ACTION_FLAGONLY,
- Ingo_Storage::ACTION_NOTIFY
- );
-
- /**
- * The categories of filtering allowed.
- *
- * @var array
- */
- var $_categories = array(
- Ingo_Storage::ACTION_BLACKLIST,
- Ingo_Storage::ACTION_WHITELIST,
- Ingo_Storage::ACTION_VACATION,
- Ingo_Storage::ACTION_FORWARD,
- Ingo_Storage::ACTION_SPAM
- );
-
- /**
- * The list of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_tests = array(
- 'contains', 'not contain', 'is', 'not is', 'begins with',
- 'not begins with', 'ends with', 'not ends with', 'exists', 'not exist',
- 'less than', 'less than or equal to', 'equal', 'not equal',
- 'greater than', 'greater than or equal to', 'regex', 'matches',
- 'not matches'
- );
-
- /**
- * The types of tests allowed (implemented) for this driver.
- *
- * @var array
- */
- var $_types = array(
- Ingo_Storage::TYPE_HEADER,
- Ingo_Storage::TYPE_SIZE,
- Ingo_Storage::TYPE_BODY
- );
-
- /**
- * Can tests be case sensitive?
- *
- * @var boolean
- */
- var $_casesensitive = true;
-
- /**
- * Does the driver support setting IMAP flags?
- *
- * @var boolean
- */
- var $_supportIMAPFlags = true;
-
- /**
- * Does the driver support the stop-script option?
- *
- * @var boolean
- */
- var $_supportStopScript = true;
-
- /**
- * Does the driver require a script file to be generated?
- *
- * @var boolean
- */
- var $_scriptfile = true;
-
- /**
- * The blocks that make up the code.
- *
- * @var array
- */
- var $_blocks = array();
-
- /**
- * The blocks that have to appear at the end of the code.
- *
- * @var array
- */
- var $_endBlocks = array();
-
- /**
- * Returns a script previously generated with generate().
- *
- * @return string The Sieve script.
- */
- function toCode()
- {
- $date_format = $GLOBALS['prefs']->getValue('date_format');
- // %R and %r don't work on Windows, but who runs a Sieve backend on a
- // Windows server?
- $time_format = $GLOBALS['prefs']->getValue('twentyFour') ? '%R' : '%r';
- $code = "# Sieve Filter\n# "
- . _("Generated by Ingo (http://www.horde.org/ingo/)") . ' ('
- . trim(strftime($date_format . ', ' . $time_format))
- . ")\n\n";
- $code = Horde_String::convertCharset($code, Horde_Nls::getCharset(), 'UTF-8');
- $requires = $this->requires();
-
- if (count($requires) > 1) {
- $stringlist = '';
- foreach ($this->requires() as $require) {
- $stringlist .= (empty($stringlist)) ? '"' : ', "';
- $stringlist .= $require . '"';
- }
- $code .= 'require [' . $stringlist . '];' . "\n\n";
- } elseif (count($requires) == 1) {
- foreach ($this->requires() as $require) {
- $code .= 'require "' . $require . '";' . "\n\n";
- }
- }
-
- foreach ($this->_blocks as $block) {
- $code .= $block->toCode() . "\n";
- }
-
- return rtrim($code) . "\n";
- }
-
- /**
- * Escape a string according to Sieve RFC 3028 [2.4.2].
- *
- * @param string $string The string to escape.
- * @param boolean $regexmode Is the escaped string a regex value?
- * Defaults to no.
- *
- * @return string The escaped string.
- */
- function escapeString($string, $regexmode = false)
- {
- /* Remove any backslashes in front of commas. */
- $string = str_replace('\,', ',', $string);
-
- if ($regexmode) {
- return str_replace('"', addslashes('"'), $string);
- } else {
- return str_replace(array('\\', '"'), array(addslashes('\\'), addslashes('"')), $string);
- }
- }
-
- /**
- * Checks if all rules are valid.
- *
- * @return boolean|string True if all rules are valid, an error message
- * otherwise.
- */
- function check()
- {
- foreach ($this->_blocks as $block) {
- $res = $block->check();
- if ($res !== true) {
- return $res;
- }
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- $requires = array();
- foreach ($this->_blocks as $block) {
- $requires = array_merge($requires, $block->requires());
- }
-
- return array_unique($requires);
- }
-
- /**
- * Adds all blocks necessary for the forward rule.
- */
- function _addForwardBlocks()
- {
- if (!$this->_validRule(Ingo_Storage::ACTION_FORWARD)) {
- return;
- }
-
- $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
- $fwd_addr = $forward->getForwardAddresses();
- if (empty($fwd_addr)) {
- return;
- }
-
- $action = array();
- foreach ($fwd_addr as $addr) {
- $addr = trim($addr);
- if (!empty($addr)) {
- $action[] = new Sieve_Action_Redirect(array('address' => $addr));
- }
- }
-
- if (count($action)) {
- if($forward->getForwardKeep()) {
- $this->_endBlocks[] = new Sieve_Comment(_("Forward Keep Action"));
- $if = new Sieve_If(new Sieve_Test_True());
- $if->setActions(array(new Sieve_Action_Keep(),
- new Sieve_Action_Stop()));
- $this->_endBlocks[] = $if;
- } else {
- $action[] = new Sieve_Action_Stop();
- }
- }
-
- $this->_blocks[] = new Sieve_Comment(_("Forwards"));
-
- $test = new Sieve_Test_True();
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- }
-
- /**
- * Adds all blocks necessary for the blacklist rule.
- */
- function _addBlacklistBlocks()
- {
- if (!$this->_validRule(Ingo_Storage::ACTION_BLACKLIST)) {
- return;
- }
-
- $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
- $bl_addr = $blacklist->getBlacklist();
- $folder = $blacklist->getBlacklistFolder();
- if (empty($bl_addr)) {
- return;
- }
-
- $action = array();
- if (empty($folder)) {
- $action[] = new Sieve_Action_Discard();
- } elseif ($folder == INGO_BLACKLIST_MARKER) {
- $action[] = new Sieve_Action_Addflag(array('flags' => Ingo_Storage::FLAG_DELETED));
- $action[] = new Sieve_Action_Keep();
- $action[] = new Sieve_Action_Removeflag(array('flags' => Ingo_Storage::FLAG_DELETED));
- } else {
- $action[] = new Sieve_Action_Fileinto(array('folder' => $folder));
- }
-
- $action[] = new Sieve_Action_Stop();
-
- $this->_blocks[] = new Sieve_Comment(_("Blacklisted Addresses"));
-
- /* Split the test up to only do 5 addresses at a time. */
- $temp = array();
- $wildcards = array();
- foreach ($bl_addr as $address) {
- if (!empty($address)) {
- if ((strstr($address, '*') !== false) ||
- (strstr($address, '?') !== false)) {
- $wildcards[] = $address;
- } else {
- $temp[] = $address;
- }
- }
- if (count($temp) == 5) {
- $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- $temp = array();
- }
- if (count($wildcards) == 5) {
- $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- $wildcards = array();
- }
- }
-
- if ($temp) {
- $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- }
-
- if ($wildcards) {
- $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- }
- }
-
- /**
- * Adds all blocks necessary for the whitelist rule.
- */
- function _addWhitelistBlocks()
- {
- if (!$this->_validRule(Ingo_Storage::ACTION_WHITELIST)) {
- return;
- }
-
- $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
- $wl_addr = $whitelist->getWhitelist();
- if (empty($wl_addr)) {
- return;
- }
-
- $this->_blocks[] = new Sieve_Comment(_("Whitelisted Addresses"));
-
- $action = array(new Sieve_Action_Keep(), new Sieve_Action_Stop());
- $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $wl_addr)));
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- }
-
- /**
- * Adds all blocks necessary for the vacation rule.
- */
- function _addVacationBlocks()
- {
- if (!$this->_validRule(Ingo_Storage::ACTION_VACATION)) {
- return;
- }
-
- $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
- $vacation_addr = $vacation->getVacationAddresses();
- if (!count($vacation_addr)) {
- return;
- }
-
- $vals = array(
- 'subject' => Horde_String::convertCharset($vacation->getVacationSubject(), Horde_Nls::getCharset(), 'UTF-8'),
- 'days' => $vacation->getVacationDays(),
- 'addresses' => $vacation_addr,
- 'start' => $vacation->getVacationStart(),
- 'start_year' => $vacation->getVacationStartYear(),
- 'start_month' => $vacation->getVacationStartMonth(),
- 'start_day' => $vacation->getVacationStartDay(),
- 'end' => $vacation->getVacationEnd(),
- 'end_year' => $vacation->getVacationEndYear(),
- 'end_month' => $vacation->getVacationEndMonth(),
- 'end_day' => $vacation->getVacationEndDay(),
- 'reason' => Horde_String::convertCharset($vacation->getVacationReason(), Horde_Nls::getCharset(), 'UTF-8'),
- );
-
- $action = $tests = array();
- $action[] = new Sieve_Action_Vacation($vals);
-
- if ($vacation->getVacationIgnorelist()) {
- $mime_headers = new Horde_Mime_Headers();
- $headers = $mime_headers->listHeaders();
- $headers['Mailing-List'] = null;
- $tmp = new Sieve_Test_Exists(array('headers' => implode("\n", array_keys($headers))));
- $tests[] = new Sieve_Test_Not($tmp);
- $vals = array('headers' => 'Precedence',
- 'match-type' => ':is',
- 'strings' => "list\nbulk\njunk",
- 'comparator' => 'i;ascii-casemap');
- $tmp = new Sieve_Test_Header($vals);
- $tests[] = new Sieve_Test_Not($tmp);
- $vals = array('headers' => 'To',
- 'match-type' => ':matches',
- 'strings' => 'Multiple recipients of*',
- 'comparator' => 'i;ascii-casemap');
- $tmp = new Sieve_Test_Header($vals);
- $tests[] = new Sieve_Test_Not($tmp);
- }
-
- $addrs = array();
- foreach ($vacation->getVacationExcludes() as $addr) {
- $addr = trim($addr);
- if (!empty($addr)) {
- $addrs[] = $addr;
- }
- }
-
- if ($addrs) {
- $tmp = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $addrs)));
- $tests[] = new Sieve_Test_Not($tmp);
- }
-
- $this->_blocks[] = new Sieve_Comment(_("Vacation"));
-
- if ($tests) {
- $test = new Sieve_Test_Allof($tests);
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- } else {
- $this->_blocks[] = $action[0];
- }
- }
-
- /**
- * Adds all blocks necessary for the spam rule.
- */
- function _addSpamBlocks()
- {
- if (!$this->_validRule(Ingo_Storage::ACTION_SPAM)) {
- return;
- }
-
- $spam = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
- if ($spam === false) {
- return;
- }
-
- $this->_blocks[] = new Sieve_Comment(_("Spam Filter"));
-
- $actions = array();
- $actions[] = new Sieve_Action_Fileinto(array(
- 'folder' => $spam->getSpamFolder()
- ));
-
- if ($GLOBALS['conf']['spam']['compare'] == 'numeric') {
- $vals = array(
- 'headers' => $GLOBALS['conf']['spam']['header'],
- 'comparison' => 'ge',
- 'value' => $spam->getSpamLevel(),
- );
- $test = new Sieve_Test_Relational($vals);
- } elseif ($GLOBALS['conf']['spam']['compare'] == 'string') {
- $vals = array(
- 'headers' => $GLOBALS['conf']['spam']['header'],
- 'match-type' => ':contains',
- 'strings' => str_repeat($GLOBALS['conf']['spam']['char'],
- $spam->getSpamLevel()),
- 'comparator' => 'i;ascii-casemap',
- );
- $test = new Sieve_Test_Header($vals);
- }
-
- $actions[] = new Sieve_Action_Stop();
-
- $if = new Sieve_If($test);
- $if->setActions($actions);
- $this->_blocks[] = $if;
- }
-
- /**
- * Generates the Sieve script to do the filtering specified in
- * the rules.
- *
- * @return string The Sieve script.
- */
- function generate()
- {
- global $ingo_storage;
-
- $filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
- foreach ($filters->getFilterList() as $filter) {
- /* Check to make sure this is a valid rule and that the rule
- is not disabled. */
- if (!$this->_validRule($filter['action']) ||
- !empty($filter['disable'])) {
- continue;
- }
-
- $action = array();
- switch ($filter['action']) {
- case Ingo_Storage::ACTION_KEEP:
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
- }
-
- $action[] = new Sieve_Action_Keep();
-
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
- }
- break;
-
- case Ingo_Storage::ACTION_DISCARD:
- $action[] = new Sieve_Action_Discard();
- break;
-
- case Ingo_Storage::ACTION_MOVE:
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
- }
-
- $action[] = new Sieve_Action_Fileinto(array('folder' => $filter['action-value']));
-
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
- }
- break;
-
- case Ingo_Storage::ACTION_REJECT:
- $action[] = new Sieve_Action_Reject(array('reason' => $filter['action-value']));
- break;
-
- case Ingo_Storage::ACTION_REDIRECT:
- $action[] = new Sieve_Action_Redirect(array('address' => $filter['action-value']));
- break;
-
- case Ingo_Storage::ACTION_REDIRECTKEEP:
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
- }
-
- $action[] = new Sieve_Action_Redirect(array('address' => $filter['action-value']));
- $action[] = new Sieve_Action_Keep();
-
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
- }
- break;
-
- case Ingo_Storage::ACTION_MOVEKEEP:
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
- }
-
- $action[] = new Sieve_Action_Keep();
- $action[] = new Sieve_Action_Fileinto(array('folder' => $filter['action-value']));
-
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
- }
- break;
-
- case Ingo_Storage::ACTION_FLAGONLY:
- if (!empty($filter['flags'])) {
- $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
- }
- break;
-
- case Ingo_Storage::ACTION_NOTIFY:
- $action[] = new Sieve_Action_Notify(array('address' => $filter['action-value'], 'name' => $filter['name']));
- break;
-
- case Ingo_Storage::ACTION_WHITELIST:
- $this->_addWhitelistBlocks();
- continue 2;
-
- case Ingo_Storage::ACTION_BLACKLIST:
- $this->_addBlacklistBlocks();
- continue 2;
-
- case Ingo_Storage::ACTION_VACATION:
- $this->_addVacationBlocks();
- continue 2;
-
- case Ingo_Storage::ACTION_FORWARD:
- $this->_addForwardBlocks();
- continue 2;
-
- case Ingo_Storage::ACTION_SPAM:
- $this->_addSpamBlocks();
- continue 2;
- }
-
- $this->_blocks[] = new Sieve_Comment($filter['name']);
-
- if ($filter['stop']) {
- $action[] = new Sieve_Action_Stop();
- }
-
- $test = new Sieve_Test();
- if ($filter['combine'] == Ingo_Storage::COMBINE_ANY) {
- $test = new Sieve_Test_Anyof();
- } else {
- $test = new Sieve_Test_Allof();
- }
-
- foreach ($filter['conditions'] as $condition) {
- $tmp = '';
- switch ($condition['match']) {
- case 'equal':
- $tmp = new Sieve_Test_Relational(array('comparison' => 'eq', 'headers' => $condition['field'], 'value' => $condition['value']));
- $test->addTest($tmp);
- break;
-
- case 'not equal':
- $tmp = new Sieve_Test_Relational(array('comparison' => 'ne', 'headers' => $condition['field'], 'value' => $condition['value']));
- $test->addTest($tmp);
- break;
-
- case 'less than':
- if ($condition['field'] == 'Size') {
- /* Message Size Test. */
- $tmp = new Sieve_Test_Size(array('comparison' => ':under', 'size' => $condition['value']));
- } else {
- /* Relational Test. */
- $tmp = new Sieve_Test_Relational(array('comparison' => 'lt', 'headers' => $condition['field'], 'value' => $condition['value']));
- }
- $test->addTest($tmp);
- break;
-
- case 'less than or equal to':
- $tmp = new Sieve_Test_Relational(array('comparison' => 'le', 'headers' => $condition['field'], 'value' => $condition['value']));
- $test->addTest($tmp);
- break;
-
- case 'greater than':
- if ($condition['field'] == 'Size') {
- /* Message Size Test. */
- $tmp = new Sieve_Test_Size(array('comparison' => ':over', 'size' => $condition['value']));
- } else {
- /* Relational Test. */
- $tmp = new Sieve_Test_Relational(array('comparison' => 'gt', 'headers' => $condition['field'], 'value' => $condition['value']));
- }
- $test->addTest($tmp);
- break;
-
- case 'greater than or equal to':
- $tmp = new Sieve_Test_Relational(array('comparison' => 'ge', 'headers' => $condition['field'], 'value' => $condition['value']));
- $test->addTest($tmp);
- break;
-
- case 'exists':
- $tmp = new Sieve_Test_Exists(array('headers' => $condition['field']));
- $test->addTest($tmp);
- break;
-
- case 'not exist':
- $tmp = new Sieve_Test_Exists(array('headers' => $condition['field']));
- $test->addTest(new Sieve_Test_Not($tmp));
- break;
-
- case 'contains':
- case 'not contain':
- case 'is':
- case 'not is':
- case 'begins with':
- case 'not begins with':
- case 'ends with':
- case 'not ends with':
- case 'regex':
- case 'matches':
- case 'not matches':
- $comparator = (isset($condition['case']) &&
- $condition['case'])
- ? 'i;octet'
- : 'i;ascii-casemap';
- $vals = array('headers' => preg_replace('/(.)(?<!\\\)\,(.)/',
- "$1\n$2",
- $condition['field']),
- 'comparator' => $comparator);
- $use_address_test = false;
-
- if ($condition['match'] != 'regex') {
- $condition['value'] = preg_replace('/(.)(?<!\\\)\,(.)/',
- "$1\n$2",
- $condition['value']);
- }
-
- /* Do 'smarter' searching for fields where we know we have
- * e-mail addresses. */
- if (preg_match('/^(From|To|Cc|Bcc)/', $condition['field'])) {
- $vals['addresses'] = $condition['value'];
- $use_address_test = true;
- } else {
- $vals['strings'] = $condition['value'];
- }
-
- switch ($condition['match']) {
- case 'contains':
- $vals['match-type'] = ':contains';
- if ($use_address_test) {
- $tmp = new Sieve_Test_Address($vals);
- } elseif ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- $test->addTest($tmp);
- break;
-
- case 'not contain':
- $vals['match-type'] = ':contains';
- if ($use_address_test) {
- $tmp = new Sieve_Test_Address($vals);
- } elseif ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- $test->addTest(new Sieve_Test_Not($tmp));
- break;
-
- case 'is':
- $vals['match-type'] = ':is';
- if ($use_address_test) {
- $tmp = new Sieve_Test_Address($vals);
- } elseif ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- $test->addTest($tmp);
- break;
-
- case 'not is':
- $vals['match-type'] = ':is';
- if ($use_address_test) {
- $tmp = new Sieve_Test_Address($vals);
- } elseif ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- $test->addTest(new Sieve_Test_Not($tmp));
- break;
-
- case 'begins with':
- $vals['match-type'] = ':matches';
- if ($use_address_test) {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = $v . '*';
- }
- $vals['addresses'] = implode("\r\n", $add_arr);
- } else {
- $vals['addresses'] .= '*';
- }
- $tmp = new Sieve_Test_Address($vals);
- } else {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = $v . '*';
- }
- $vals['strings'] = implode("\r\n", $add_arr);
- } else {
- $vals['strings'] .= '*';
- }
- if ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- }
- $test->addTest($tmp);
- break;
-
- case 'not begins with':
- $vals['match-type'] = ':matches';
- if ($use_address_test) {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = $v . '*';
- }
- $vals['addresses'] = implode("\r\n", $add_arr);
- } else {
- $vals['addresses'] .= '*';
- }
- $tmp = new Sieve_Test_Address($vals);
- } else {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = $v . '*';
- }
- $vals['strings'] = implode("\r\n", $add_arr);
- } else {
- $vals['strings'] .= '*';
- }
- if ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- }
- $test->addTest(new Sieve_Test_Not($tmp));
- break;
-
- case 'ends with':
- $vals['match-type'] = ':matches';
- if ($use_address_test) {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = '*' . $v;
- }
- $vals['addresses'] = implode("\r\n", $add_arr);
- } else {
- $vals['addresses'] = '*' . $vals['addresses'];
- }
- $tmp = new Sieve_Test_Address($vals);
- } else {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = '*' . $v;
- }
- $vals['strings'] = implode("\r\n", $add_arr);
- } else {
- $vals['strings'] = '*' . $vals['strings'];
- }
- if ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- }
- $test->addTest($tmp);
- break;
-
- case 'not ends with':
- $vals['match-type'] = ':matches';
- if ($use_address_test) {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['addresses']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = '*' . $v;
- }
- $vals['addresses'] = implode("\r\n", $add_arr);
- } else {
- $vals['addresses'] = '*' . $vals['addresses'];
- }
- $tmp = new Sieve_Test_Address($vals);
- } else {
- $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
- if (count($add_arr) > 1) {
- foreach ($add_arr as $k => $v) {
- $add_arr[$k] = '*' . $v;
- }
- $vals['strings'] = implode("\r\n", $add_arr);
- } else {
- $vals['strings'] = '*' . $vals['strings'];
- }
- if ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- }
- $test->addTest(new Sieve_Test_Not($tmp));
- break;
-
- case 'regex':
- $vals['match-type'] = ':regex';
- if ($use_address_test) {
- $tmp = new Sieve_Test_Address($vals);
- } elseif ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- $test->addTest($tmp);
- break;
-
- case 'matches':
- $vals['match-type'] = ':matches';
- if ($use_address_test) {
- $tmp = new Sieve_Test_Address($vals);
- } elseif ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- $test->addTest($tmp);
- break;
-
- case 'not matches':
- $vals['match-type'] = ':matches';
- if ($use_address_test) {
- $tmp = new Sieve_Test_Address($vals);
- } elseif ($condition['field'] == 'Body') {
- $tmp = new Sieve_Test_Body($vals);
- } else {
- $tmp = new Sieve_Test_Header($vals);
- }
- $test->addTest(new Sieve_Test_Not($tmp));
- break;
- }
- }
- }
-
- $if = new Sieve_If($test);
- $if->setActions($action);
- $this->_blocks[] = $if;
- }
-
- /* Add blocks that have to go to the end. */
- foreach ($this->_endBlocks as $block) {
- $this->_blocks[] = $block;
- }
-
- return $this->toCode();
- }
-
-}
-
-/**
- * The Sieve_If class represents a Sieve If Statement
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_If {
-
- /**
- * The Sieve_Test object for the if test.
- *
- * @var Sieve_Test
- */
- var $_test;
-
- /**
- * A list of Sieve_Action objects that go into the if clause.
- *
- * @var array
- */
- var $_actions = array();
-
- /**
- * A list of Sieve_Elseif objects that create optional elsif clauses.
- *
- * @var array
- */
- var $_elsifs = array();
-
- /**
- * A Sieve_Else object that creates an optional else clause.
- *
- * @var Sieve_Else
- */
- var $_else;
-
- /**
- * Constructor.
- *
- * @params Sieve_Test $test A Sieve_Test object.
- */
- function Sieve_If($test = null)
- {
- if (is_null($test)) {
- $this->_test = new Sieve_Test_False();
- } else {
- $this->_test = $test;
- }
-
- $this->_actions[] = new Sieve_Action_Keep();
- $this->_else = new Sieve_Else();
- }
-
- function getTest()
- {
- return $this->_test;
- }
-
- function setTest($test)
- {
- $this->_test = $test;
- }
-
- function getActions()
- {
- return $this->_actions;
- }
-
- function setActions($actions)
- {
- $this->_actions = $actions;
- }
-
- function getElsifs()
- {
- return $this->_elsifs;
- }
-
- function setElsifs($elsifs)
- {
- $this->_elsifs = $elsifs;
- }
-
- function addElsif($elsif)
- {
- $this->_elsifs[] = $elsif;
- }
-
- function getElse()
- {
- return $this->_else;
- }
-
- function setElse($else)
- {
- $this->_else = $else;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = 'if ' . $this->_test->toCode() . " { \n";
- foreach ($this->_actions as $action) {
- $code .= ' ' . $action->toCode() . "\n";
- }
- $code .= "} ";
-
- foreach ($this->_elsifs as $elsif) {
- $code .= $elsif->toCode();
- }
-
- $code .= $this->_else->toCode();
-
- return $code . "\n";
- }
-
- /**
- * Checks if all sub-rules are valid.
- *
- * @return boolean|string True if all rules are valid, an error message
- * otherwise.
- */
- function check()
- {
- $res = $this->_test->check();
- if ($res !== true) {
- return $res;
- }
-
- foreach ($this->_elsifs as $elsif) {
- $res = $elsif->check();
- if ($res !== true) {
- return $res;
- }
- }
-
- $res = $this->_else->check();
- if ($res !== true) {
- return $res;
- }
-
- foreach ($this->_actions as $action) {
- $res = $action->check();
- if ($res !== true) {
- return $res;
- }
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- $requires = array();
-
- foreach ($this->_actions as $action) {
- $requires = array_merge($requires, $action->requires());
- }
-
- foreach ($this->_elsifs as $elsif) {
- $requires = array_merge($requires, $elsif->requires());
- }
-
- $requires = array_merge($requires, $this->_test->requires(), $this->_else->requires());
-
- return $requires;
- }
-
-}
-
-/**
- * The Sieve_Else class represents a Sieve Else Statement
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Else {
-
- /**
- * A list of Sieve_Action objects that go into the else clause.
- *
- * @var array
- */
- var $_actions = array();
-
- /**
- * Constructor.
- *
- * @params Sieve_Action|array $actions A Sieve_Action object or a list of
- * Sieve_Action objects.
- */
- function Sieve_Else($actions = null)
- {
- if (is_array($actions)) {
- $this->_actions = $actions;
- } elseif (!is_null($actions)) {
- $this->_actions[] = $actions;
- }
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- if (count($this->_actions) == 0) {
- return '';
- }
-
- $code = 'else' . " { \n";
- foreach ($this->_actions as $action) {
- $code .= ' ' . $action->toCode() . "\n";
- }
- $code .= "} ";
-
- return $code;
- }
-
- function setActions($actions)
- {
- $this->_actions = $actions;
- }
-
- function getActions()
- {
- return $this->_actions;
- }
-
- /**
- * Checks if all sub-rules are valid.
- *
- * @return boolean|string True if all rules are valid, an error message
- * otherwise.
- */
- function check()
- {
- foreach ($this->_actions as $action) {
- $res = $action->check();
- if ($res !== true) {
- return $res;
- }
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- $requires = array();
-
- foreach ($this->_actions as $action) {
- $requires = array_merge($requires, $action->requires());
- }
-
- return $requires;
- }
-
-}
-
-/**
- * The Sieve_Elsif class represents a Sieve Elsif Statement
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Elsif {
-
- /**
- * The Sieve_Test object for the if test.
- *
- * @var Sieve_Test
- */
- var $_test;
-
- /**
- * A list of Sieve_Action objects that go into the if clause.
- *
- * @var array
- */
- var $_actions = array();
-
- /**
- * Constructor.
- *
- * @params Sieve_Test $test A Sieve_Test object.
- */
- function Sieve_Elsif($test = null)
- {
- if (is_null($test)) {
- $this->_test = new Sieve_Test_False();
- } else {
- $this->_test = $test;
- }
- $this->_actions[] = new Sieve_Action_Keep();
- }
-
- function getTest()
- {
- return $this->_test;
- }
-
- function setTest($test)
- {
- $this->_test = $test;
- }
-
- function getActions()
- {
- return $this->_actions;
- }
-
- function setActions($actions)
- {
- $this->_actions = $actions;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = 'elsif ' . $this->_test->toCode() . " { \n";
- foreach ($this->_actions as $action) {
- $code .= ' ' . $action->toCode() . "\n";
- }
- $code .= "} ";
-
- return $code;
- }
-
- /**
- * Checks if all sub-rules are valid.
- *
- * @return boolean|string True if all rules are valid, an error message
- * otherwise.
- */
- function check()
- {
- $res = $this->_test->check();
- if ($res !== true) {
- return $res;
- }
-
- foreach ($this->_actions as $action) {
- $res = $action->check();
- if ($res !== true) {
- return $res;
- }
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- $requires = array();
-
- foreach ($this->_actions as $action) {
- $requires = array_merge($requires, $action->requires());
- }
-
- $requires = array_merge($requires, $this->_test->requires());
-
- return $requires;
- }
-
-}
-
-/**
- * The Sieve_Test class represents a Sieve Test.
- *
- * A test is a piece of code that evaluates to true or false.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test {
-
- /**
- * Any necessary test parameters.
- *
- * @var array
- */
- var $_vars = array();
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'toCode() Function Not Implemented in class ' . get_class($this);
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return 'check() Function Not Implemented in class ' . get_class($this);
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array();
- }
-
-}
-
-/**
- * The Sieve_Test_True class represents a test that always evaluates to true.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_True extends Sieve_Test {
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'true';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return true;
- }
-
-}
-
-/**
- * The Sieve_Test_False class represents a test that always evaluates to
- * false.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_False extends Sieve_Test {
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'false';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return true;
- }
-
-}
-
-/**
- * The Sieve_Test_Allof class represents a Allof test structure.
- *
- * Equivalent to a logical AND of all the tests it contains.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_Allof extends Sieve_Test {
-
- var $_tests = array();
-
- /**
- * Constructor.
- *
- * @params Sieve_Test|array $test A Sieve_Test object or a list of
- * Sieve_Test objects.
- */
- function Sieve_Test_Allof($test = null)
- {
- if (is_array($test)) {
- $this->_tests = $test;
- } elseif (!is_null($test)) {
- $this->_tests[] = $test;
- }
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = '';
- if (count($this->_tests) > 1) {
- $testlist = '';
- foreach ($this->_tests as $test) {
- $testlist .= (empty($testlist)) ? '' : ', ';
- $testlist .= trim($test->toCode());
- }
-
- $code = "allof ( $testlist )";
- } elseif (count($this->_tests) == 1) {
- $code = $this->_tests[0]->toCode();
- } else {
- return 'true';
- }
- return $code;
- }
-
- /**
- * Checks if all sub-rules are valid.
- *
- * @return boolean|string True if all rules are valid, an error message
- * otherwise.
- */
- function check()
- {
- foreach ($this->_tests as $test) {
- $res = $test->check();
- if ($res !== true) {
- return $res;
- }
- }
-
- return true;
- }
-
- function addTest($test)
- {
- $this->_tests[] = $test;
- }
-
- function getTests()
- {
- return $this->_tests;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- $requires = array();
-
- foreach ($this->_tests as $test) {
- $requires = array_merge($requires, $test->requires());
- }
-
- return $requires;
- }
-
-}
-
-/**
- * The Sieve_Test_Anyof class represents a Anyof test structure.
- *
- * Equivalent to a logical OR of all the tests it contains.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_Anyof extends Sieve_Test {
-
- var $_tests = array();
-
- /**
- * Constructor.
- *
- * @params Sieve_Test|array $test A Sieve_Test object or a list of
- * Sieve_Test objects.
- */
- function Sieve_Test_Anyof($test = null)
- {
- if (is_array($test)) {
- $this->_tests = $test;
- } elseif (!is_null($test)) {
- $this->_tests[] = $test;
- }
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $testlist = '';
- if (count($this->_tests) > 1) {
- $testlist = '';
- foreach ($this->_tests as $test) {
- $testlist .= (empty($testlist)) ? '' : ', ';
- $testlist .= trim($test->toCode());
- }
-
- $code = "anyof ( $testlist )";
- } elseif (count($this->_tests) == 1) {
- $code = $this->_tests[0]->toCode();
- } else {
- return 'true';
- }
- return $code;
- }
-
- function addTest($test)
- {
- $this->_tests[] = $test;
- }
-
- function getTests()
- {
- return $this->_tests;
- }
-
- /**
- * Checks if all sub-rules are valid.
- *
- * @return boolean|string True if all rules are valid, an error message
- * otherwise.
- */
- function check()
- {
- foreach ($this->_tests as $test) {
- $res = $test->check();
- if ($res !== true) {
- return $res;
- }
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- $requires = array();
-
- foreach ($this->_tests as $test) {
- $requires = array_merge($requires, $test->requires());
- }
-
- return $requires;
- }
-
-}
-
-/**
- * The Sieve_Test_Relational class represents a relational test.
- *
- * @author Todd Merritt <tmerritt@email.arizona.edu>
- * @since Ingo 1.0
- * @package Ingo
- */
-class Sieve_Test_Relational extends Sieve_Test {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Test_Relational($vars = array())
- {
- $this->_vars['comparison'] = (isset($vars['comparison'])) ? $vars['comparison'] : '';
- $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
- $this->_vars['value'] = (isset($vars['value'])) ? $vars['value'] : 0;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = 'header :value "' .
- $this->_vars['comparison'] . '" ' .
- ':comparator "i;ascii-numeric" ';
-
- $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
- $header_count = count($headers);
-
- if ($header_count > 1) {
- $code .= "[";
- $headerstr = '';
-
- foreach ($headers as $val) {
- $headerstr .= (empty($headerstr) ? '"' : ', "') .
- Ingo_Script_sieve::escapeString($val) . '"';
- }
-
- $code .= $headerstr . "] ";
- } elseif ($header_count == 1) {
- $code .= '"' . Ingo_Script_sieve::escapeString($headers[0]) . '" ';
- }
-
- return $code . '["' . $this->_vars['value'] . '"]';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
- return $headers ? true : _("No headers specified");
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array('relational', 'comparator-i;ascii-numeric');
- }
-
-}
-
-/**
- * The Sieve_Test_Size class represents a message size test.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_Size extends Sieve_Test {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Test_Size($vars = array())
- {
- $this->_vars['comparison'] = (isset($vars['comparison'])) ? $vars['comparison'] : '';
- $this->_vars['size'] = (isset($vars['size'])) ? $vars['size'] : '';
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'size ' . $this->_vars['comparison'] . ' ' . $this->_vars['size'];
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- if (!(isset($this->_vars['comparison']) &&
- isset($this->_vars['size']))) {
- return false;
- }
-
- return true;
- }
-
-}
-
-/**
- * The Sieve_Test_Not class represents the inverse of a given test.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_Not extends Sieve_Test {
-
- var $_test = array();
-
- /**
- * Constructor.
- *
- * @params Sieve_Test $test A Sieve_Test object.
- */
- function Sieve_Test_Not($test)
- {
- $this->_test = $test;
- }
-
- /**
- * Checks if the sub-rule is valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return $this->_test->check();
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'not ' . $this->_test->toCode();
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return $this->_test->requires();
- }
-
-}
-
-/**
- * The Sieve_Test_Exists class represents a test for the existsance of one or
- * more headers in a message.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_Exists extends Sieve_Test {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Test_Exists($vars = array())
- {
- $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
- if (!$headers) {
- return _("No headers specified");
- }
-
- return true;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = 'exists ';
- $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
- if (count($headers) > 1) {
- $code .= "[";
- $headerstr = '';
- foreach ($headers as $header) {
- $headerstr .= (empty($headerstr) ? '"' : ', "') .
- Ingo_Script_sieve::escapeString($header) . '"';
- }
- $code .= $headerstr . "] ";
- } elseif (count($headers) == 1) {
- $code .= '"' . Ingo_Script_sieve::escapeString($headers[0]) . '" ';
- } else {
- return "**error** No Headers Specified";
- }
-
- return $code;
- }
-
-}
-
-/**
- * The Sieve_Test_Address class represents a test on parts or all of the
- * addresses in the given fields.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_Address extends Sieve_Test {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Test_Address($vars)
- {
- $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
- $this->_vars['comparator'] = (isset($vars['comparator'])) ? $vars['comparator'] : 'i;ascii-casemap';
- $this->_vars['match-type'] = (isset($vars['match-type'])) ? $vars['match-type'] : ':is';
- $this->_vars['address-part'] = (isset($vars['address-part'])) ? $vars['address-part'] : ':all';
- $this->_vars['addresses'] = (isset($vars['addresses'])) ? $vars['addresses'] : '';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
- if (!$headers) {
- return false;
- }
-
- $addresses = preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
- if (!$addresses) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = 'address ' .
- $this->_vars['address-part'] . ' ' .
- ':comparator "' . $this->_vars['comparator'] . '" ' .
- $this->_vars['match-type'] . ' ';
-
- $headers = preg_split('(\r\n|\n|\r|,)', $this->_vars['headers']);
- $headers = array_filter($headers);
- if (count($headers) > 1) {
- $code .= "[";
- $headerstr = '';
- foreach ($headers as $header) {
- $header = trim($header);
- if (!empty($header)) {
- $headerstr .= empty($headerstr) ? '"' : ', "';
- $headerstr .= Ingo_Script_sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
- }
- }
- $code .= $headerstr . "] ";
- } elseif (count($headers) == 1) {
- $code .= '"' . Ingo_Script_sieve::escapeString($headers[0], $this->_vars['match-type'] == ':regex') . '" ';
- } else {
- return "No Headers Specified";
- }
-
- $addresses = preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
- $addresses = array_filter($addresses);
- if (count($addresses) > 1) {
- $code .= "[";
- $addressstr = '';
- foreach ($addresses as $addr) {
- $addr = trim($addr);
- if (!empty($addr)) {
- $addressstr .= empty($addressstr) ? '"' : ', "';
- $addressstr .= Ingo_Script_sieve::escapeString($addr, $this->_vars['match-type'] == ':regex') . '"';
- }
- }
- $code .= $addressstr . "] ";
- } elseif (count($addresses) == 1) {
- $code .= '"' . Ingo_Script_sieve::escapeString($addresses[0], $this->_vars['match-type'] == ':regex') . '" ';
- } else {
- return "No Addresses Specified";
- }
-
- return $code;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- if ($this->_vars['match-type'] == ':regex') {
- return array('regex');
- }
- return array();
- }
-
-}
-
-/**
- * The Sieve_Test_Header class represents a test on the contents of one or
- * more headers in a message.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Test_Header extends Sieve_Test {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Test_Header($vars = array())
- {
- $this->_vars['headers'] = isset($vars['headers'])
- ? $vars['headers']
- : 'Subject';
- $this->_vars['comparator'] = isset($vars['comparator'])
- ? $vars['comparator']
- : 'i;ascii-casemap';
- $this->_vars['match-type'] = isset($vars['match-type'])
- ? $vars['match-type']
- : ':is';
- $this->_vars['strings'] = isset($vars['strings'])
- ? $vars['strings']
- : '';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- $headers = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['headers']);
- if (!$headers) {
- return false;
- }
-
- $strings = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
- if (!$strings) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = 'header ' .
- ':comparator "' . $this->_vars['comparator'] . '" ' .
- $this->_vars['match-type'] . ' ';
-
- $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
- $headers = array_filter($headers);
- if (count($headers) > 1) {
- $code .= "[";
- $headerstr = '';
- foreach ($headers as $header) {
- $headerstr .= empty($headerstr) ? '"' : ', "';
- $headerstr .= Ingo_Script_sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
- }
- $code .= $headerstr . "] ";
- } elseif (count($headers) == 1) {
- $code .= '"' . $headers[0] . '" ';
- } else {
- return _("No headers specified");
- }
-
- $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
- $strings = array_filter($strings);
- if (count($strings) > 1) {
- $code .= "[";
- $stringlist = '';
- foreach ($strings as $str) {
- $stringlist .= empty($stringlist) ? '"' : ', "';
- $stringlist .= Ingo_Script_sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
- }
- $code .= $stringlist . "] ";
- } elseif (count($strings) == 1) {
- $code .= '"' . Ingo_Script_sieve::escapeString(reset($strings), $this->_vars['match-type'] == ':regex') . '" ';
- } else {
- return _("No strings specified");
- }
-
- return $code;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- if ($this->_vars['match-type'] == ':regex') {
- return array('regex');
- }
- return array();
- }
-
-}
-
-/**
- * The Sieve_Test_Body class represents a test on the contents of the body in
- * a message.
- *
- * @author Michael Menge <michael.menge@zdv.uni-tuebingen.de>
- * @since Ingo 1.2
- * @package Ingo
- */
-class Sieve_Test_Body extends Sieve_Test {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Test_Body($vars = array())
- {
- $this->_vars['comparator'] = (isset($vars['comparator'])) ? $vars['comparator'] : 'i;ascii-casemap';
- $this->_vars['match-type'] = (isset($vars['match-type'])) ? $vars['match-type'] : ':is';
- $this->_vars['strings'] = (isset($vars['strings'])) ? $vars['strings'] : '';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- $strings = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
- if (!$strings) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = 'body ' .
- ':comparator "' . $this->_vars['comparator'] . '" ' .
- $this->_vars['match-type'] . ' ';
-
- $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
- $strings = array_filter($strings);
- if (count($strings) > 1) {
- $code .= "[";
- $stringlist = '';
- foreach ($strings as $str) {
- $stringlist .= empty($stringlist) ? '"' : ', "';
- $stringlist .= Ingo_Script_sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
- }
- $code .= $stringlist . "] ";
- } elseif (count($strings) == 1) {
- $code .= '"' . Ingo_Script_sieve::escapeString($strings[0], $this->_vars['match-type'] == ':regex') . '" ';
- } else {
- return _("No strings specified");
- }
-
- return $code;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- if ($this->_vars['match-type'] == ':regex') {
- return array('regex', 'body');
- }
-
- return array('body');
- }
-
-}
-
-/**
- * A Comment.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- * @todo This and Sieve_If should really extends a Sieve_Block eventually.
- */
-class Sieve_Comment {
-
- var $_comment;
-
- /**
- * Constructor.
- *
- * @params string $comment The comment text.
- */
- function Sieve_Comment($comment)
- {
- $this->_comment = $comment;
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $code = '';
- $lines = preg_split('(\r\n|\n|\r)', $this->_comment);
- foreach ($lines as $line) {
- $line = trim($line);
- if (strlen($line)) {
- $code .= (empty($code) ? '' : "\n") . '# ' . $line;
- }
- }
- return Horde_String::convertCharset($code, Horde_Nls::getCharset(), 'UTF-8');
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array();
- }
-
-}
-
-/**
- * The Sieve_Action class represents an action in a Sieve script.
- *
- * An action is anything that has a side effect eg: discard, redirect.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action {
-
- /**
- * Any necessary action parameters.
- *
- * @var array
- */
- var $_vars = array();
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'toCode() Function Not Implemented in class ' . get_class($this) ;
- }
-
- function toString()
- {
- return $this->toCode();
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return 'check() Function Not Implemented in class ' . get_class($this) ;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array();
- }
-
-}
-
-/**
- * The Sieve_Action_Redirect class represents a redirect action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Redirect extends Sieve_Action {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Action_Redirect($vars = array())
- {
- $this->_vars['address'] = (isset($vars['address'])) ? $vars['address'] : '';
- }
-
- function toCode($depth = 0)
- {
- return str_repeat(' ', $depth * 4) . 'redirect ' .
- '"' . Ingo_Script_sieve::escapeString($this->_vars['address']) . '";';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- if (empty($this->_vars['address'])) {
- return _("Missing address to redirect message to");
- }
-
- return true;
- }
-
-}
-
-/**
- * The Sieve_Action_Reject class represents a reject action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Reject extends Sieve_Action {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Action_Reject($vars = array())
- {
- $this->_vars['reason'] = (isset($vars['reason'])) ? $vars['reason'] : '';
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'reject "' . Ingo_Script_sieve::escapeString($this->_vars['reason']) . '";';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- if (empty($this->_vars['reason'])) {
- return _("Missing reason for reject");
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array('reject');
- }
-
-}
-
-/**
- * The Sieve_Action_Keep class represents a keep action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Keep extends Sieve_Action {
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'keep;';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return true;
- }
-
-}
-
-/**
- * The Sieve_Action_Discard class represents a discard action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Discard extends Sieve_Action {
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'discard;';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return true;
- }
-
-}
-
-/**
- * The Sieve_Action_Stop class represents a stop action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Stop extends Sieve_Action {
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'stop;';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return true;
- }
-
-}
-
-/**
- * The Sieve_Action_Fileinto class represents a fileinto action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Fileinto extends Sieve_Action {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Action_Fileinto($vars = array())
- {
- $this->_vars['folder'] = (isset($vars['folder'])) ? $vars['folder'] : '';
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'fileinto "' . Ingo_Script_sieve::escapeString($this->_vars['folder']) . '";';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- if (empty($this->_vars['folder'])) {
- return _("Inexistant mailbox specified for message delivery.");
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array('fileinto');
- }
-
-}
-
-/**
- * The Sieve_Action_Vacation class represents a vacation action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Vacation extends Sieve_Action {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Action_Vacation($vars = array())
- {
- $this->_vars['days'] = isset($vars['days']) ? intval($vars['days']) : '';
- $this->_vars['addresses'] = isset($vars['addresses']) ? $vars['addresses'] : '';
- $this->_vars['subject'] = isset($vars['subject']) ? $vars['subject'] : '';
- $this->_vars['reason'] = isset($vars['reason']) ? $vars['reason'] : '';
- $this->_vars['start'] = isset($vars['start']) ? $vars['start'] : '';
- $this->_vars['start_year'] = isset($vars['start_year']) ? $vars['start_year'] : '';
- $this->_vars['start_month'] = isset($vars['start_month']) ? $vars['start_month'] : '';
- $this->_vars['start_day'] = isset($vars['start_day']) ? $vars['start_day'] : '';
- $this->_vars['end'] = isset($vars['end']) ? $vars['end'] : '';
- $this->_vars['end_year'] = isset($vars['end_year']) ? $vars['end_year'] : '';
- $this->_vars['end_month'] = isset($vars['end_month']) ? $vars['end_month'] : '';
- $this->_vars['end_day'] = isset($vars['end_day']) ? $vars['end_day'] : '';
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- $start_year = $this->_vars['start_year'];
- $start_month = $this->_vars['start_month'];
- $start_day = $this->_vars['start_day'];
-
- $end_year = $this->_vars['end_year'];
- $end_month = $this->_vars['end_month'];
- $end_day = $this->_vars['end_day'];
-
- $code = '';
-
- if (empty($this->_vars['start']) || empty($this->_vars['end'])) {
- return $this->_vacationCode();
- } elseif ($end_year > $start_year + 1) {
- $code .= $this->_yearCheck($start_year + 1, $end_year - 1)
- . $this->_vacationCode()
- . "\n}\n"
- . $this->_yearCheck($start_year, $start_year);
- if ($start_month < 12) {
- $code .= $this->_monthCheck($start_month + 1, 12)
- . $this->_vacationCode()
- . "\n}\n";
- }
- $code .= $this->_monthCheck($start_month, $start_month)
- . $this->_dayCheck($start_day, 31)
- . $this->_vacationCode()
- . "\n}\n}\n}\n"
- . $this->_yearCheck($end_year, $end_year);
- if ($end_month > 1) {
- $code .= $this->_monthCheck(1, $end_month - 1)
- . $this->_vacationCode()
- . "\n}\n";
- }
- $code .= $this->_monthCheck($end_month, $end_month)
- . $this->_dayCheck(1, $end_day)
- . $this->_vacationCode()
- . "\n}\n}\n}\n";
- } elseif ($end_year == $start_year + 1) {
- $code .= $this->_yearCheck($start_year, $start_year);
- if ($start_month < 12) {
- $code .= $this->_monthCheck($start_month + 1, 12)
- . $this->_vacationCode()
- . "\n}\n";
- }
- $code .= $this->_monthCheck($start_month, $start_month)
- . $this->_dayCheck($start_day, 31)
- . $this->_vacationCode()
- . "\n}\n}\n}\n"
- . $this->_yearCheck($end_year, $end_year);
- if ($end_month > 1) {
- $code .= $this->_monthCheck(1, $end_month - 1)
- . $this->_vacationCode()
- . "\n}\n";
- }
- $code .= $this->_monthCheck($end_month, $end_month)
- . $this->_dayCheck(1, $end_day)
- . $this->_vacationCode()
- . "\n}\n}\n}\n";
- } elseif ($end_year == $start_year) {
- $code .= $this->_yearCheck($start_year, $start_year);
- if ($end_month > $start_month) {
- if ($end_month > $start_month + 1) {
- $code .= $this->_monthCheck($start_month + 1, $end_month - 1)
- . $this->_vacationCode()
- . "\n}\n";
- }
- $code .= $this->_monthCheck($start_month, $start_month)
- . $this->_dayCheck($start_day, 31)
- . $this->_vacationCode()
- . "\n}\n}\n"
- . $this->_monthCheck($end_month, $end_month)
- . $this->_dayCheck(1, $end_day)
- . $this->_vacationCode()
- . "\n}\n}\n";
- } elseif ($end_month == $start_month) {
- $code .= $this->_monthCheck($start_month, $start_month)
- . $this->_dayCheck($start_day, $end_day)
- . $this->_vacationCode()
- . "\n}\n}\n";
- }
- $code .= "}\n";
- }
-
- return $code;
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- if (empty($this->_vars['reason'])) {
- return _("Missing reason in vacation.");
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array('vacation', 'regex');
- }
-
- /**
- */
- function _vacationCode()
- {
- $code = 'vacation :days ' . $this->_vars['days'] . ' ';
- $addresses = $this->_vars['addresses'];
- $stringlist = '';
- if (count($addresses) > 1) {
- foreach ($addresses as $address) {
- $address = trim($address);
- if (!empty($address)) {
- $stringlist .= empty($stringlist) ? '"' : ', "';
- $stringlist .= Ingo_Script_sieve::escapeString($address) . '"';
- }
- }
- $stringlist = "[" . $stringlist . "] ";
- } elseif (count($addresses) == 1) {
- $stringlist = '"' . Ingo_Script_sieve::escapeString($addresses[0]) . '" ';
- }
-
- if (!empty($stringlist)) {
- $code .= ':addresses ' . $stringlist;
- }
-
- if (!empty($this->_vars['subject'])) {
- include_once 'Horde/MIME.php';
- $code .= ':subject "' . MIME::encode(Ingo_Script_sieve::escapeString($this->_vars['subject']), 'UTF-8') . '" ';
- }
- return $code
- . '"' . Ingo_Script_sieve::escapeString($this->_vars['reason'])
- . '";';
- }
-
- /**
- */
- function _yearCheck($begin, $end)
- {
- $code = 'if header :regex "Received" "^.*(' . $begin;
- for ($i = $begin + 1; $i <= $end; $i++) {
- $code .= '|' . $i;
- }
- return $code
- . ') (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?(\\\\+|\\\\-)....( \\\\(.*\\\\))?$" {'
- . "\n ";
- }
-
- /**
- */
- function _monthCheck($begin, $end)
- {
- $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
- $code = 'if header :regex "Received" "^.*(' . $months[$begin - 1];
- for ($i = $begin + 1; $i <= $end; $i++) {
- $code .= '|' . $months[$i - 1];
- }
- return $code
- . ') (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?(\\\\+|\\\\-)....( \\\\(.*\\\\))?$" {'
- . "\n ";
- }
-
- /**
- */
- function _dayCheck($begin, $end)
- {
- $code = 'if header :regex "Received" "^.*(' . str_repeat('[0 ]', 2 - strlen($begin)) . $begin;
- for ($i = $begin + 1; $i <= $end; $i++) {
- $code .= '|' . str_repeat('[0 ]', 2 - strlen($i)) . $i;
- }
- return $code
- . ') (\\\\(.*\\\\) )?... (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?(\\\\+|\\\\-)....( \\\\(.*\\\\))?$" {'
- . "\n ";
- }
-
-}
-
-/**
- * The Sieve_Action_Flag class is the base class for flag actions.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Flag extends Sieve_Action {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Action_Flag($vars = array())
- {
- if (isset($vars['flags'])) {
- if ($vars['flags'] & Ingo_Storage::FLAG_ANSWERED) {
- $this->_vars['flags'][] = '\Answered';
- }
- if ($vars['flags'] & Ingo_Storage::FLAG_DELETED) {
- $this->_vars['flags'][] = '\Deleted';
- }
- if ($vars['flags'] & Ingo_Storage::FLAG_FLAGGED) {
- $this->_vars['flags'][] = '\Flagged';
- }
- if ($vars['flags'] & Ingo_Storage::FLAG_SEEN) {
- $this->_vars['flags'][] = '\Seen';
- }
- } else {
- $this->_vars['flags'] = '';
- }
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @param string $mode The sieve flag command to use. Either 'removeflag'
- * or 'addflag'.
- *
- * @return string A Sieve script snippet.
- */
- function _toCode($mode)
- {
- $code = '';
-
- if (is_array($this->_vars['flags']) && !empty($this->_vars['flags'])) {
- $code .= $mode . ' ';
- if (count($this->_vars['flags']) > 1) {
- $stringlist = '';
- foreach ($this->_vars['flags'] as $flag) {
- $flag = trim($flag);
- if (!empty($flag)) {
- $stringlist .= empty($stringlist) ? '"' : ', "';
- $stringlist .= Ingo_Script_sieve::escapeString($flag) . '"';
- }
- }
- $stringlist = '[' . $stringlist . ']';
- $code .= $stringlist . ';';
- } else {
- $code .= '"' . Ingo_Script_sieve::escapeString($this->_vars['flags'][0]) . '";';
- }
- }
- return $code;
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array('imapflags');
- }
-
-}
-
-/**
- * The Sieve_Action_Addflag class represents an add flag action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Addflag extends Sieve_Action_Flag {
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return $this->_toCode('addflag');
- }
-
-}
-
-/**
- * The Sieve_Action_Removeflag class represents a remove flag action.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @since Ingo 0.1
- * @package Ingo
- */
-class Sieve_Action_Removeflag extends Sieve_Action_Flag {
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return $this->_toCode('removeflag');
- }
-
-}
-
-/**
- * The Sieve_Action_Notify class represents a notify action.
- *
- * @author Paul Wolstenholme <wolstena@sfu.ca>
- * @since Ingo 1.1
- * @package Ingo
- */
-class Sieve_Action_Notify extends Sieve_Action {
-
- /**
- * Constructor.
- *
- * @params array $vars Any required parameters.
- */
- function Sieve_Action_Notify($vars = array())
- {
- $this->_vars['address'] = isset($vars['address']) ? $vars['address'] : '';
- $this->_vars['name'] = isset($vars['name']) ? $vars['name'] : '';
- }
-
- /**
- * Returns a script snippet representing this rule and any sub-rules.
- *
- * @return string A Sieve script snippet.
- */
- function toCode()
- {
- return 'notify :method "mailto" :options "' .
- Ingo_Script_sieve::escapeString($this->_vars['address']) .
- '" :message "' .
- _("You have received a new message") . "\n" .
- _("From:") . " \$from\$ \n" .
- _("Subject:") . " \$subject\$ \n" .
- _("Rule:") . ' ' . $this->_vars['name'] . '";';
- }
-
- /**
- * Checks if the rule parameters are valid.
- *
- * @return boolean|string True if this rule is valid, an error message
- * otherwise.
- */
- function check()
- {
- if (empty($this->_vars['address'])) {
- return _("Missing address to notify");
- }
-
- return true;
- }
-
- /**
- * Returns a list of sieve extensions required for this rule and any
- * sub-rules.
- *
- * @return array A Sieve extension list.
- */
- function requires()
- {
- return array('notify');
- }
-
-}
$params = Horde::getDriverConfig('storage', $driver);
}
- $class = 'Ingo_Storage_' . $driver;
+ $class = 'Ingo_Storage_' . ucfirst($driver);
return class_exists($class)
? new $class($params)
: false;
*/
public function __destruct()
{
- $cache = &Horde_SessionObjects::singleton();
+ $cache = Horde_SessionObjects::singleton();
/* Store the current objects. */
foreach ($this->_cache as $key => $val) {
* @param boolean $cache Use the cached object?
* @param boolean $readonly Whether to disable any write operations.
*
- * @return Ingo_Storage_rule|Ingo_Storage_filters The specified object.
+ * @return Ingo_Storage_Rule|Ingo_Storage_Filters The specified object.
*/
public function retrieve($field, $cache = true, $readonly = false)
{
if (!isset($this->_cache[$field])) {
$this->_cache[$field] = array('mod' => false);
if (isset($_SESSION['ingo']['storage'][$field])) {
- $cacheSess = &Horde_SessionObjects::singleton();
+ $cacheSess = Horde_SessionObjects::singleton();
$this->_cache[$field]['ob'] = $cacheSess->query($_SESSION['ingo']['storage'][$field]);
} else {
$this->_cache[$field]['ob'] = &$this->_retrieve($field, $readonly);
* See lib/Storage.php for the available fields.
* @param boolean $readonly Whether to disable any write operations.
*
- * @return Ingo_Storage_rule|Ingo_Storage_filters The specified data.
+ * @return Ingo_Storage_Rule|Ingo_Storage_Filters The specified data.
*/
protected function _retrieve($field, $readonly = false)
{
/**
* Stores the specified data.
*
- * @param Ingo_Storage_rule|Ingo_Storage_filters $ob The object to store.
+ * @param Ingo_Storage_Rule|Ingo_Storage_Filters $ob The object to store.
* @param boolean $cache Cache the object?
*
* @return boolean True on success.
*
* @abstract
*
- * @param Ingo_Storage_rule|Ingo_Storage_filters $ob The object to store.
+ * @param Ingo_Storage_Rule|Ingo_Storage_Filters $ob The object to store.
*
* @return boolean True on success.
*/
}
}
-
-/**
- * Ingo_Storage_rule:: is the base class for the various action objects
- * used by Ingo_Storage.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_rule
-{
- /**
- * The object type.
- *
- * @var integer
- */
- protected $_obtype;
-
- /**
- * Whether the rule has been saved (if being saved separately).
- *
- * @var boolean
- */
- protected $_saved = false;
-
- /**
- * Returns the object rule type.
- *
- * @return integer The object rule type.
- */
- public function obType()
- {
- return $this->_obtype;
- }
-
- /**
- * Marks the rule as saved or unsaved.
- *
- * @param boolean $data Whether the rule has been saved.
- */
- public function setSaved($data)
- {
- $this->_saved = $data;
- }
-
- /**
- * Returns whether the rule has been saved.
- *
- * @return boolean True if the rule has been saved.
- */
- public function isSaved()
- {
- return $this->_saved;
- }
-
- /**
- * Function to manage an internal address list.
- *
- * @param mixed $data The incoming data (array or string).
- * @param boolean $sort Sort the list?
- *
- * @return array The address list.
- */
- protected function _addressList($data, $sort)
- {
- $output = array();
-
- if (is_array($data)) {
- $output = $data;
- } else {
- $data = trim($data);
- $output = (empty($data)) ? array() : preg_split("/\s+/", $data);
- }
-
- if ($sort) {
- $output = Horde_Array::prepareAddressList($output);
- }
-
- return $output;
- }
-
-}
-
-/**
- * Ingo_Storage_blacklist is the object used to hold blacklist rule
- * information.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_blacklist extends Ingo_Storage_rule
-{
- protected $_addr = array();
- protected $_folder = '';
- protected $_obtype = Ingo_Storage::ACTION_BLACKLIST;
-
- /**
- * Sets the list of blacklisted addresses.
- *
- * @param mixed $data The list of addresses (array or string).
- * @param boolean $sort Sort the list?
- *
- * @return mixed PEAR_Error on error, true on success.
- */
- public function setBlacklist($data, $sort = true)
- {
- $addr = &$this->_addressList($data, $sort);
- if (!empty($GLOBALS['conf']['storage']['maxblacklist'])) {
- $addr_count = count($addr);
- if ($addr_count > $GLOBALS['conf']['storage']['maxblacklist']) {
- return PEAR::raiseError(sprintf(_("Maximum number of blacklisted addresses exceeded (Total addresses: %s, Maximum addresses: %s). Could not add new addresses to blacklist."), $addr_count, $GLOBALS['conf']['storage']['maxblacklist']), 'horde.error');
- }
- }
-
- $this->_addr = $addr;
- return true;
- }
-
- public function setBlacklistFolder($data)
- {
- $this->_folder = $data;
- }
-
- public function getBlacklist()
- {
- return empty($this->_addr)
- ? array()
- : array_filter($this->_addr, array('Ingo', 'filterEmptyAddress'));
- }
-
- public function getBlacklistFolder()
- {
- return $this->_folder;
- }
-
-}
-
-/**
- * Ingo_Storage_whitelist is the object used to hold whitelist rule
- * information.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_whitelist extends Ingo_Storage_rule
-{
- protected $_addr = array();
- protected $_obtype = Ingo_Storage::ACTION_WHITELIST;
-
- /**
- * Sets the list of whitelisted addresses.
- *
- * @param mixed $data The list of addresses (array or string).
- * @param boolean $sort Sort the list?
- *
- * @return mixed PEAR_Error on error, true on success.
- */
- public function setWhitelist($data, $sort = true)
- {
- $addr = &$this->_addressList($data, $sort);
- $addr = array_filter($addr, array('Ingo', 'filterEmptyAddress'));
- if (!empty($GLOBALS['conf']['storage']['maxwhitelist'])) {
- $addr_count = count($addr);
- if ($addr_count > $GLOBALS['conf']['storage']['maxwhitelist']) {
- return PEAR::raiseError(sprintf(_("Maximum number of whitelisted addresses exceeded (Total addresses: %s, Maximum addresses: %s). Could not add new addresses to whitelist."), $addr_count, $GLOBALS['conf']['storage']['maxwhitelist']), 'horde.error');
- }
- }
-
- $this->_addr = $addr;
- return true;
- }
-
- public function getWhitelist()
- {
- return empty($this->_addr)
- ? array()
- : array_filter($this->_addr, array('Ingo', 'filterEmptyAddress'));
- }
-
-}
-
-/**
- * Ingo_Storage_forward is the object used to hold mail forwarding rule
- * information.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_forward extends Ingo_Storage_rule
-{
- protected $_addr = array();
- protected $_keep = true;
- protected $_obtype = Ingo_Storage::ACTION_FORWARD;
-
- public function setForwardAddresses($data, $sort = true)
- {
- $this->_addr = &$this->_addressList($data, $sort);
- }
-
- public function setForwardKeep($data)
- {
- $this->_keep = $data;
- }
-
- public function getForwardAddresses()
- {
- if (is_array($this->_addr)) {
- foreach ($this->_addr as $key => $val) {
- if (empty($val)) {
- unset($this->_addr[$key]);
- }
- }
- }
- return $this->_addr;
- }
-
- public function getForwardKeep()
- {
- return $this->_keep;
- }
-
-}
-
-/**
- * Ingo_Storage_vacation is the object used to hold vacation rule
- * information.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_vacation extends Ingo_Storage_rule
-{
- protected $_addr = array();
- protected $_days = 7;
- protected $_excludes = array();
- protected $_ignorelist = true;
- protected $_reason = '';
- protected $_subject = '';
- protected $_start;
- protected $_end;
- protected $_obtype = Ingo_Storage::ACTION_VACATION;
-
- public function setVacationAddresses($data, $sort = true)
- {
- $this->_addr = &$this->_addressList($data, $sort);
- }
-
- public function setVacationDays($data)
- {
- $this->_days = $data;
- }
-
- public function setVacationExcludes($data, $sort = true)
- {
- $this->_excludes = &$this->_addressList($data, $sort);
- }
-
- public function setVacationIgnorelist($data)
- {
- $this->_ignorelist = $data;
- }
-
- public function setVacationReason($data)
- {
- $this->_reason = $data;
- }
-
- public function setVacationSubject($data)
- {
- $this->_subject = $data;
- }
-
- public function setVacationStart($data)
- {
- $this->_start = $data;
- }
-
- public function setVacationEnd($data)
- {
- $this->_end = $data;
- }
-
- public function getVacationAddresses()
- {
- if (empty($GLOBALS['conf']['hooks']['vacation_addresses'])) {
- return $this->_addr;
- }
-
- try {
- return Horde::callHook('_ingo_hook_vacation_addresses', array(Ingo::getUser()), 'ingo');
- } catch (Horde_Exception $e) {
- return array();
- }
- }
-
- public function getVacationDays()
- {
- return $this->_days;
- }
-
- public function getVacationExcludes()
- {
- return $this->_excludes;
- }
-
- public function getVacationIgnorelist()
- {
- return $this->_ignorelist;
- }
-
- public function getVacationReason()
- {
- return $this->_reason;
- }
-
- public function getVacationSubject()
- {
- return $this->_subject;
- }
-
- public function getVacationStart()
- {
- return $this->_start;
- }
-
- public function getVacationStartYear()
- {
- return date('Y', $this->_start);
- }
-
- public function getVacationStartMonth()
- {
- return date('n', $this->_start);
- }
-
- public function getVacationStartDay()
- {
- return date('j', $this->_start);
- }
-
- public function getVacationEnd()
- {
- return $this->_end;
- }
-
- public function getVacationEndYear()
- {
- return date('Y', $this->_end);
- }
-
- public function getVacationEndMonth()
- {
- return date('n', $this->_end);
- }
-
- public function getVacationEndDay()
- {
- return date('j', $this->_end);
- }
-
-}
-
-/**
- * Ingo_Storage_spam is an object used to hold default spam-rule filtering
- * information.
- *
- * @author Jason M. Felice <jason.m.felice@gmail.com>
- * @package Ingo
- */
-class Ingo_Storage_spam extends Ingo_Storage_rule
-{
-
- /**
- * The object type.
- *
- * @var integer
- */
- protected $_obtype = Ingo_Storage::ACTION_SPAM;
-
- protected $_folder = null;
- protected $_level = 5;
-
- public function setSpamFolder($folder)
- {
- $this->_folder = $folder;
- }
-
- public function setSpamLevel($level)
- {
- $this->_level = $level;
- }
-
- public function getSpamFolder()
- {
- return $this->_folder;
- }
-
- public function getSpamLevel()
- {
- return $this->_level;
- }
-
-}
-
-/**
- * Ingo_Storage_filters is the object used to hold user-defined filtering rule
- * information.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_filters
-{
- /**
- * The filter list.
- *
- * @var array
- */
- protected $_filters = array();
-
- /**
- * The object type.
- *
- * @var integer
- */
- protected $_obtype = Ingo_Storage::ACTION_FILTERS;
-
- /**
- * Returns the object rule type.
- *
- * @return integer The object rule type.
- */
- public function obType()
- {
- return $this->_obtype;
- }
-
- /**
- * Propagates the filter list with data.
- *
- * @param array $data A list of rule hashes.
- */
- public function setFilterlist($data)
- {
- $this->_filters = $data;
- }
-
- /**
- * Returns the filter list.
- *
- * @return array The list of rule hashes.
- */
- public function getFilterList()
- {
- return $this->_filters;
- }
-
- /**
- * Return the filter entry for a given ID.
- *
- * @return mixed The rule hash entry, or false if not defined.
- */
- public function getFilter($id)
- {
- return isset($this->_filters[$id])
- ? $this->_filters[$id]
- : false;
- }
-
- /**
- * Returns a single rule hash.
- *
- * @param integer $id A rule number.
- *
- * @return array The requested rule hash.
- */
- public function getRule($id)
- {
- return $this->_filters[$id];
- }
-
- /**
- * Returns a rule hash with default value used when creating new rules.
- *
- * @return array A rule hash.
- */
- public function getDefaultRule()
- {
- return array(
- 'name' => _("New Rule"),
- 'combine' => Ingo_Storage::COMBINE_ALL,
- 'conditions' => array(),
- 'action' => Ingo_Storage::ACTION_KEEP,
- 'action-value' => '',
- 'stop' => true,
- 'flags' => 0,
- 'disable' => false
- );
- }
-
- /**
- * Searches for the first rule of a certain action type and returns its
- * number.
- *
- * @param integer $action The field type of the searched rule
- * (ACTION_* constants).
- *
- * @return integer The number of the first matching rule or null.
- */
- public function findRuleId($action)
- {
- foreach ($this->_filters as $id => $rule) {
- if ($rule['action'] == $action) {
- return $id;
- }
- }
- }
-
- /**
- * Searches for and returns the first rule of a certain action type.
- *
- * @param integer $action The field type of the searched rule
- * (ACTION_* constants).
- *
- * @return array The first matching rule hash or null.
- */
- public function findRule($action)
- {
- $id = $this->findRuleId($action);
- if ($id !== null) {
- return $this->getRule($id);
- }
- }
-
- /**
- * Adds a rule hash to the filters list.
- *
- * @param array $rule A rule hash.
- * @param boolean $default If true merge the rule hash with default rule
- * values.
- */
- public function addRule($rule, $default = true)
- {
- if ($default) {
- $this->_filters[] = array_merge($this->getDefaultRule(), $rule);
- } else {
- $this->_filters[] = $rule;
- }
- }
-
- /**
- * Updates an existing rule with a rule hash.
- *
- * @param array $rule A rule hash
- * @param integer $id A rule number
- */
- public function updateRule($rule, $id)
- {
- $this->_filters[$id] = $rule;
- }
-
- /**
- * Deletes a rule from the filters list.
- *
- * @param integer $id Number of the rule to delete.
- *
- * @return boolean True if the rule has been found and deleted.
- */
- public function deleteRule($id)
- {
- if (isset($this->_filters[$id])) {
- unset($this->_filters[$id]);
- $this->_filters = array_values($this->_filters);
- return true;
- }
-
- return false;
- }
-
- /**
- * Creates a copy of an existing rule.
- *
- * The created copy is added to the filters list right after the original
- * rule.
- *
- * @param integer $id Number of the rule to copy.
- *
- * @return boolean True if the rule has been found and copied.
- */
- public function copyRule($id)
- {
- if (isset($this->_filters[$id])) {
- $newrule = $this->_filters[$id];
- $newrule['name'] = sprintf(_("Copy of %s"), $this->_filters[$id]['name']);
- $this->_filters = array_merge(array_slice($this->_filters, 0, $id + 1), array($newrule), array_slice($this->_filters, $id + 1));
- return true;
- }
-
- return false;
- }
-
- /**
- * Moves a rule up in the filters list.
- *
- * @param integer $id Number of the rule to move.
- * @param integer $steps Number of positions to move the rule up.
- */
- public function ruleUp($id, $steps = 1)
- {
- for ($i = 0; $i < $steps && $id > 0;) {
- $temp = $this->_filters[$id - 1];
- $this->_filters[$id - 1] = $this->_filters[$id];
- $this->_filters[$id] = $temp;
- /* Continue to move up until we swap with a viewable category. */
- if (in_array($temp['action'], $_SESSION['ingo']['script_categories'])) {
- $i++;
- }
- $id--;
- }
- }
-
- /**
- * Moves a rule down in the filters list.
- *
- * @param integer $id Number of the rule to move.
- * @param integer $steps Number of positions to move the rule down.
- */
- public function ruleDown($id, $steps = 1)
- {
- $rulecount = count($this->_filters) - 1;
- for ($i = 0; $i < $steps && $id < $rulecount;) {
- $temp = $this->_filters[$id + 1];
- $this->_filters[$id + 1] = $this->_filters[$id];
- $this->_filters[$id] = $temp;
- /* Continue to move down until we swap with a viewable
- category. */
- if (in_array($temp['action'], $_SESSION['ingo']['script_categories'])) {
- $i++;
- }
- $id++;
- }
- }
-
- /**
- * Disables a rule.
- *
- * @param integer $id Number of the rule to disable.
- */
- public function ruleDisable($id)
- {
- $this->_filters[$id]['disable'] = true;
- }
-
- /**
- * Enables a rule.
- *
- * @param integer $id Number of the rule to enable.
- */
- public function ruleEnable($id)
- {
- $this->_filters[$id]['disable'] = false;
- }
-
-}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Blacklist is the object used to hold blacklist rule
+ * information.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Blacklist extends Ingo_Storage_Rule
+{
+ protected $_addr = array();
+ protected $_folder = '';
+ protected $_obtype = Ingo_Storage::ACTION_BLACKLIST;
+
+ /**
+ * Sets the list of blacklisted addresses.
+ *
+ * @param mixed $data The list of addresses (array or string).
+ * @param boolean $sort Sort the list?
+ *
+ * @return mixed PEAR_Error on error, true on success.
+ */
+ public function setBlacklist($data, $sort = true)
+ {
+ $addr = &$this->_addressList($data, $sort);
+ if (!empty($GLOBALS['conf']['storage']['maxblacklist'])) {
+ $addr_count = count($addr);
+ if ($addr_count > $GLOBALS['conf']['storage']['maxblacklist']) {
+ return PEAR::raiseError(sprintf(_("Maximum number of blacklisted addresses exceeded (Total addresses: %s, Maximum addresses: %s). Could not add new addresses to blacklist."), $addr_count, $GLOBALS['conf']['storage']['maxblacklist']), 'horde.error');
+ }
+ }
+
+ $this->_addr = $addr;
+ return true;
+ }
+
+ public function setBlacklistFolder($data)
+ {
+ $this->_folder = $data;
+ }
+
+ public function getBlacklist()
+ {
+ return empty($this->_addr)
+ ? array()
+ : array_filter($this->_addr, array('Ingo', 'filterEmptyAddress'));
+ }
+
+ public function getBlacklistFolder()
+ {
+ return $this->_folder;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Filters is the object used to hold user-defined filtering rule
+ * information.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Filters
+{
+ /**
+ * The filter list.
+ *
+ * @var array
+ */
+ protected $_filters = array();
+
+ /**
+ * The object type.
+ *
+ * @var integer
+ */
+ protected $_obtype = Ingo_Storage::ACTION_FILTERS;
+
+ /**
+ * Returns the object rule type.
+ *
+ * @return integer The object rule type.
+ */
+ public function obType()
+ {
+ return $this->_obtype;
+ }
+
+ /**
+ * Propagates the filter list with data.
+ *
+ * @param array $data A list of rule hashes.
+ */
+ public function setFilterlist($data)
+ {
+ $this->_filters = $data;
+ }
+
+ /**
+ * Returns the filter list.
+ *
+ * @return array The list of rule hashes.
+ */
+ public function getFilterList()
+ {
+ return $this->_filters;
+ }
+
+ /**
+ * Return the filter entry for a given ID.
+ *
+ * @return mixed The rule hash entry, or false if not defined.
+ */
+ public function getFilter($id)
+ {
+ return isset($this->_filters[$id])
+ ? $this->_filters[$id]
+ : false;
+ }
+
+ /**
+ * Returns a single rule hash.
+ *
+ * @param integer $id A rule number.
+ *
+ * @return array The requested rule hash.
+ */
+ public function getRule($id)
+ {
+ return $this->_filters[$id];
+ }
+
+ /**
+ * Returns a rule hash with default value used when creating new rules.
+ *
+ * @return array A rule hash.
+ */
+ public function getDefaultRule()
+ {
+ return array(
+ 'name' => _("New Rule"),
+ 'combine' => Ingo_Storage::COMBINE_ALL,
+ 'conditions' => array(),
+ 'action' => Ingo_Storage::ACTION_KEEP,
+ 'action-value' => '',
+ 'stop' => true,
+ 'flags' => 0,
+ 'disable' => false
+ );
+ }
+
+ /**
+ * Searches for the first rule of a certain action type and returns its
+ * number.
+ *
+ * @param integer $action The field type of the searched rule
+ * (ACTION_* constants).
+ *
+ * @return integer The number of the first matching rule or null.
+ */
+ public function findRuleId($action)
+ {
+ foreach ($this->_filters as $id => $rule) {
+ if ($rule['action'] == $action) {
+ return $id;
+ }
+ }
+ }
+
+ /**
+ * Searches for and returns the first rule of a certain action type.
+ *
+ * @param integer $action The field type of the searched rule
+ * (ACTION_* constants).
+ *
+ * @return array The first matching rule hash or null.
+ */
+ public function findRule($action)
+ {
+ $id = $this->findRuleId($action);
+ if ($id !== null) {
+ return $this->getRule($id);
+ }
+ }
+
+ /**
+ * Adds a rule hash to the filters list.
+ *
+ * @param array $rule A rule hash.
+ * @param boolean $default If true merge the rule hash with default rule
+ * values.
+ */
+ public function addRule($rule, $default = true)
+ {
+ if ($default) {
+ $this->_filters[] = array_merge($this->getDefaultRule(), $rule);
+ } else {
+ $this->_filters[] = $rule;
+ }
+ }
+
+ /**
+ * Updates an existing rule with a rule hash.
+ *
+ * @param array $rule A rule hash
+ * @param integer $id A rule number
+ */
+ public function updateRule($rule, $id)
+ {
+ $this->_filters[$id] = $rule;
+ }
+
+ /**
+ * Deletes a rule from the filters list.
+ *
+ * @param integer $id Number of the rule to delete.
+ *
+ * @return boolean True if the rule has been found and deleted.
+ */
+ public function deleteRule($id)
+ {
+ if (isset($this->_filters[$id])) {
+ unset($this->_filters[$id]);
+ $this->_filters = array_values($this->_filters);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Creates a copy of an existing rule.
+ *
+ * The created copy is added to the filters list right after the original
+ * rule.
+ *
+ * @param integer $id Number of the rule to copy.
+ *
+ * @return boolean True if the rule has been found and copied.
+ */
+ public function copyRule($id)
+ {
+ if (isset($this->_filters[$id])) {
+ $newrule = $this->_filters[$id];
+ $newrule['name'] = sprintf(_("Copy of %s"), $this->_filters[$id]['name']);
+ $this->_filters = array_merge(array_slice($this->_filters, 0, $id + 1), array($newrule), array_slice($this->_filters, $id + 1));
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Moves a rule up in the filters list.
+ *
+ * @param integer $id Number of the rule to move.
+ * @param integer $steps Number of positions to move the rule up.
+ */
+ public function ruleUp($id, $steps = 1)
+ {
+ for ($i = 0; $i < $steps && $id > 0;) {
+ $temp = $this->_filters[$id - 1];
+ $this->_filters[$id - 1] = $this->_filters[$id];
+ $this->_filters[$id] = $temp;
+ /* Continue to move up until we swap with a viewable category. */
+ if (in_array($temp['action'], $_SESSION['ingo']['script_categories'])) {
+ $i++;
+ }
+ $id--;
+ }
+ }
+
+ /**
+ * Moves a rule down in the filters list.
+ *
+ * @param integer $id Number of the rule to move.
+ * @param integer $steps Number of positions to move the rule down.
+ */
+ public function ruleDown($id, $steps = 1)
+ {
+ $rulecount = count($this->_filters) - 1;
+ for ($i = 0; $i < $steps && $id < $rulecount;) {
+ $temp = $this->_filters[$id + 1];
+ $this->_filters[$id + 1] = $this->_filters[$id];
+ $this->_filters[$id] = $temp;
+ /* Continue to move down until we swap with a viewable
+ category. */
+ if (in_array($temp['action'], $_SESSION['ingo']['script_categories'])) {
+ $i++;
+ }
+ $id++;
+ }
+ }
+
+ /**
+ * Disables a rule.
+ *
+ * @param integer $id Number of the rule to disable.
+ */
+ public function ruleDisable($id)
+ {
+ $this->_filters[$id]['disable'] = true;
+ }
+
+ /**
+ * Enables a rule.
+ *
+ * @param integer $id Number of the rule to enable.
+ */
+ public function ruleEnable($id)
+ {
+ $this->_filters[$id]['disable'] = false;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Filters_Sql is the object used to hold user-defined filtering
+ * rule information.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Filters_Sql extends Ingo_Storage_Filters {
+
+ /**
+ * Handle for the current database connection.
+ *
+ * @var DB
+ */
+ protected $_db;
+
+ /**
+ * Handle for the current database connection, used for writing.
+ *
+ * Defaults to the same handle as $_db if a separate write database is not
+ * required.
+ *
+ * @var DB
+ */
+ protected $_write_db;
+
+ /**
+ * Driver specific parameters.
+ *
+ * @var array
+ */
+ protected $_params;
+
+ /**
+ * Constructor.
+ *
+ * @param DB $db Handle for the database connection.
+ * @param DB $write_db Handle for the database connection, used for
+ * writing.
+ * @param array $params Driver specific parameters.
+ */
+ public function __construct($db, $write_db, $params)
+ {
+ $this->_db = $db;
+ $this->_write_db = $write_db;
+ $this->_params = $params;
+ }
+
+ /**
+ * Loads all rules from the DB backend.
+ *
+ * @param boolean $readonly Whether to disable any write operations.
+ */
+ public function init($readonly = false)
+ {
+ $query = sprintf('SELECT * FROM %s WHERE rule_owner = ? ORDER BY rule_order',
+ $this->_params['table_rules']);
+ $values = array(Ingo::getUser());
+ Horde::logMessage('Ingo_Storage_Filters_Sql(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $data = array();
+ while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
+ $data[$row['rule_order']] = array(
+ 'id' => (int)$row['rule_id'],
+ 'name' => Horde_String::convertCharset($row['rule_name'], $this->_params['charset']),
+ 'action' => (int)$row['rule_action'],
+ 'action-value' => Horde_String::convertCharset($row['rule_value'], $this->_params['charset']),
+ 'flags' => (int)$row['rule_flags'],
+ 'conditions' => empty($row['rule_conditions']) ? null : Horde_String::convertCharset(unserialize($row['rule_conditions']), $this->_params['charset']),
+ 'combine' => (int)$row['rule_combine'],
+ 'stop' => (bool)$row['rule_stop'],
+ 'disable' => !(bool)$row['rule_active']);
+ }
+ $this->setFilterlist($data);
+
+ if (empty($data) && !$readonly) {
+ $data = @unserialize($GLOBALS['prefs']->getDefault('rules'));
+ if ($data) {
+ foreach ($data as $val) {
+ $this->addRule($val, false);
+ }
+ } else {
+ $this->addRule(
+ array('name' => 'Whitelist',
+ 'action' => Ingo_Storage::ACTION_WHITELIST),
+ false);
+ $this->addRule(
+ array('name' => 'Vacation',
+ 'action' => Ingo_Storage::ACTION_VACATION,
+ 'disable' => true),
+ false);
+ $this->addRule(
+ array('name' => 'Blacklist',
+ 'action' => Ingo_Storage::ACTION_BLACKLIST),
+ false);
+ $this->addRule(
+ array('name' => 'Spam Filter',
+ 'action' => Ingo_Storage::ACTION_SPAM,
+ 'disable' => true),
+ false);
+ $this->addRule(
+ array('name' => 'Forward',
+ 'action' => Ingo_Storage::ACTION_FORWARD),
+ false);
+ }
+ }
+ }
+
+ /**
+ * Converts a rule hash from Ingo's internal format to the database
+ * format.
+ *
+ * @param array $rule Rule hash in Ingo's format.
+ *
+ * @return array Rule hash in DB's format.
+ */
+ protected function _ruleToBackend($rule)
+ {
+ return array(Horde_String::convertCharset($rule['name'], Horde_Nls::getCharset(), $this->_params['charset']),
+ (int)$rule['action'],
+ isset($rule['action-value']) ? Horde_String::convertCharset($rule['action-value'], Horde_Nls::getCharset(), $this->_params['charset']) : null,
+ isset($rule['flags']) ? (int)$rule['flags'] : null,
+ isset($rule['conditions']) ? serialize(Horde_String::convertCharset($rule['conditions'], Horde_Nls::getCharset(), $this->_params['charset'])) : null,
+ isset($rule['combine']) ? (int)$rule['combine'] : null,
+ isset($rule['stop']) ? (int)$rule['stop'] : null,
+ isset($rule['disable']) ? (int)(!$rule['disable']) : 1);
+ }
+
+ /**
+ * Adds a rule hash to the filters list.
+ *
+ * @param array $rule A rule hash.
+ * @param boolean $default If true merge the rule hash with default rule
+ * values.
+ */
+ public function addRule($rule, $default = true)
+ {
+ if ($default) {
+ $rule = array_merge($this->getDefaultRule(), $rule);
+ }
+
+ $query = sprintf('INSERT INTO %s (rule_id, rule_owner, rule_name, rule_action, rule_value, rule_flags, rule_conditions, rule_combine, rule_stop, rule_active, rule_order) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
+ $this->_params['table_rules']);
+ $id = $this->_write_db->nextId($this->_params['table_rules']);
+ if (is_a($id, 'PEAR_Error')) {
+ return $id;
+ }
+ $order = key(array_reverse($this->_filters, true)) + 1;
+ $values = array_merge(array($id, Ingo::getUser()),
+ $this->_ruleToBackend($rule),
+ array($order));
+ Horde::logMessage('Ingo_Storage_Filters_Sql::addRule(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $rule['id'] = $id;
+ $this->_filters[$order] = $rule;
+ }
+
+ /**
+ * Updates an existing rule with a rule hash.
+ *
+ * @param array $rule A rule hash
+ * @param integer $id A rule number
+ */
+ public function updateRule($rule, $id)
+ {
+ $query = sprintf('UPDATE %s SET rule_name = ?, rule_action = ?, rule_value = ?, rule_flags = ?, rule_conditions = ?, rule_combine = ?, rule_stop = ?, rule_active = ?, rule_order = ? WHERE rule_id = ? AND rule_owner = ?',
+ $this->_params['table_rules']);
+ $values = array_merge($this->_ruleToBackend($rule),
+ array($id, $rule['id'], Ingo::getUser()));
+ Horde::logMessage('Ingo_Storage_Filters_Sql::updateRule(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $this->_filters[$id] = $rule;
+ }
+
+ /**
+ * Deletes a rule from the filters list.
+ *
+ * @param integer $id Number of the rule to delete.
+ *
+ * @return boolean True if the rule has been found and deleted.
+ */
+ public function deleteRule($id)
+ {
+ if (!isset($this->_filters[$id])) {
+ return false;
+ }
+
+ $query = sprintf('DELETE FROM %s WHERE rule_id = ? AND rule_owner = ?',
+ $this->_params['table_rules']);
+ $values = array($this->_filters[$id]['id'], Ingo::getUser());
+ Horde::logMessage('Ingo_Storage_Filters_Sql::deleteRule(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ unset($this->_filters[$id]);
+
+ $query = sprintf('UPDATE %s SET rule_order = rule_order - 1 WHERE rule_owner = ? AND rule_order > ?',
+ $this->_params['table_rules']);
+ $values = array(Ingo::getUser(), $id);
+ Horde::logMessage('Ingo_Storage_Filters_Sql::deleteRule(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Creates a copy of an existing rule.
+ *
+ * The created copy is added to the filters list right after the original
+ * rule.
+ *
+ * @param integer $id Number of the rule to copy.
+ *
+ * @return boolean True if the rule has been found and copied.
+ */
+ public function copyRule($id)
+ {
+ if (isset($this->_filters[$id])) {
+ $newrule = $this->_filters[$id];
+ $newrule['name'] = sprintf(_("Copy of %s"), $this->_filters[$id]['name']);
+ $this->addRule($newrule, false);
+ $this->ruleUp(count($this->_filters) - 1, count($this->_filters) - $id - 2);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Moves a rule up in the filters list.
+ *
+ * @param integer $id Number of the rule to move.
+ * @param integer $steps Number of positions to move the rule up.
+ */
+ public function ruleUp($id, $steps = 1)
+ {
+ return $this->_ruleMove($id, -$steps);
+ }
+
+ /**
+ * Moves a rule down in the filters list.
+ *
+ * @param integer $id Number of the rule to move.
+ * @param integer $steps Number of positions to move the rule down.
+ */
+ public function ruleDown($id, $steps = 1)
+ {
+ return $this->_ruleMove($id, $steps);
+ }
+
+ /**
+ * Moves a rule in the filters list.
+ *
+ * @param integer $id Number of the rule to move.
+ * @param integer $steps Number of positions and direction to move the
+ * rule.
+ */
+ protected function _ruleMove($id, $steps)
+ {
+ $query = sprintf('UPDATE %s SET rule_order = rule_order %s 1 WHERE rule_owner = ? AND rule_order %s ? AND rule_order %s ?',
+ $this->_params['table_rules'],
+ $steps > 0 ? '-' : '+',
+ $steps > 0 ? '>' : '>=',
+ $steps > 0 ? '<=' : '<');
+ $values = array(Ingo::getUser());
+ if ($steps < 0) {
+ $values[] = (int)($id + $steps);
+ $values[] = (int)$id;
+ } else {
+ $values[] = (int)$id;
+ $values[] = (int)($id + $steps);
+ }
+ Horde::logMessage('Ingo_Storage_Filters_Sql::ruleUp(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ $query = sprintf('UPDATE %s SET rule_order = ? WHERE rule_owner = ? AND rule_id = ?',
+ $this->_params['table_rules']);
+ $values = array((int)($id + $steps),
+ Ingo::getUser(),
+ $this->_filters[$id]['id']);
+ Horde::logMessage('Ingo_Storage_Filters_Sql::ruleUp(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $this->init();
+ }
+
+ /**
+ * Disables a rule.
+ *
+ * @param integer $id Number of the rule to disable.
+ */
+ public function ruleDisable($id)
+ {
+ $rule = $this->_filters[$id];
+ $rule['disable'] = true;
+ $this->updateRule($rule, $id);
+ }
+
+ /**
+ * Enables a rule.
+ *
+ * @param integer $id Number of the rule to enable.
+ */
+ public function ruleEnable($id)
+ {
+ $rule = $this->_filters[$id];
+ $rule['disable'] = false;
+ $this->updateRule($rule, $id);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Forward is the object used to hold mail forwarding rule
+ * information.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Forward extends Ingo_Storage_Rule
+{
+ protected $_addr = array();
+ protected $_keep = true;
+ protected $_obtype = Ingo_Storage::ACTION_FORWARD;
+
+ public function setForwardAddresses($data, $sort = true)
+ {
+ $this->_addr = &$this->_addressList($data, $sort);
+ }
+
+ public function setForwardKeep($data)
+ {
+ $this->_keep = $data;
+ }
+
+ public function getForwardAddresses()
+ {
+ if (is_array($this->_addr)) {
+ foreach ($this->_addr as $key => $val) {
+ if (empty($val)) {
+ unset($this->_addr[$key]);
+ }
+ }
+ }
+ return $this->_addr;
+ }
+
+ public function getForwardKeep()
+ {
+ return $this->_keep;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Mock:: is used for testing purposes. It just keeps the
+ * data local and doesn't put it anywhere.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Jason M. Felice <jason.m.felice@gmail.com>
+ * @package Ingo
+ */
+
+class Ingo_Storage_Mock extends Ingo_Storage
+{
+ protected $_data = array();
+
+ protected function &_retrieve($field)
+ {
+ if (empty($this->_data[$field])) {
+ switch ($field) {
+ case self::ACTION_BLACKLIST:
+ return new Ingo_Storage_Blacklist();
+
+ case self::ACTION_FILTERS:
+ $ob = new Ingo_Storage_Filters();
+ include INGO_BASE . '/config/prefs.php.dist';
+ $ob->setFilterList(unserialize($_prefs['rules']['value']));
+ return $ob;
+
+ case self::ACTION_FORWARD:
+ return new Ingo_Storage_Forward();
+
+ case self::ACTION_VACATION:
+ return new Ingo_Storage_Vacation();
+
+ case self::ACTION_WHITELIST:
+ return new Ingo_Storage_Whitelist();
+
+ case self::ACTION_SPAM:
+ return new Ingo_Storage_Spam();
+
+ default:
+ return false;
+ }
+ }
+
+ return $this->_data[$field];
+ }
+
+ protected function _store(&$ob)
+ {
+ $this->_data[$ob->obType()] = $ob;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Prefs:: implements the Ingo_Storage:: API to save Ingo data
+ * via the Horde preferences system.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Prefs extends Ingo_Storage
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params Additional parameters for the subclass.
+ */
+ public function __construct($params = array())
+ {
+ $this->_params = $params;
+ }
+
+ /**
+ * Retrieves the specified data from the storage backend.
+ *
+ * @param integer $field The field name of the desired data.
+ * See lib/Storage.php for the available fields.
+ * @param boolean $readonly Whether to disable any write operations.
+ *
+ * @return Ingo_Storage_Rule|Ingo_Storage_Filters The specified data.
+ */
+ protected function _retrieve($field, $readonly = false)
+ {
+ $prefs = &Prefs::singleton($GLOBALS['conf']['prefs']['driver'],
+ $GLOBALS['registry']->getApp(),
+ Ingo::getUser(), '', null, false);
+ $prefs->retrieve();
+
+ switch ($field) {
+ case self::ACTION_BLACKLIST:
+ $ob = new Ingo_Storage_Blacklist();
+ $data = @unserialize($prefs->getValue('blacklist'));
+ if ($data) {
+ $ob->setBlacklist($data['a'], false);
+ $ob->setBlacklistFolder($data['f']);
+ }
+ break;
+
+ case self::ACTION_WHITELIST:
+ $ob = new Ingo_Storage_Whitelist();
+ $data = @unserialize($prefs->getValue('whitelist'));
+ if ($data) {
+ $ob->setWhitelist($data, false);
+ }
+ break;
+
+ case self::ACTION_FILTERS:
+ $ob = new Ingo_Storage_Filters();
+ $data = @unserialize($prefs->getValue('rules', false));
+ if ($data === false) {
+ /* Convert rules from the old format. */
+ $data = @unserialize($prefs->getValue('rules'));
+ } else {
+ $data = Horde_String::convertCharset($data, $prefs->getCharset(), Horde_Nls::getCharset());
+ }
+ if ($data) {
+ $ob->setFilterlist($data);
+ }
+ break;
+
+ case self::ACTION_FORWARD:
+ $ob = new Ingo_Storage_Forward();
+ $data = @unserialize($prefs->getValue('forward'));
+ if ($data) {
+ $ob->setForwardAddresses($data['a'], false);
+ $ob->setForwardKeep($data['k']);
+ }
+ break;
+
+ case self::ACTION_VACATION:
+ $ob = new Ingo_Storage_Vacation();
+ $data = @unserialize($prefs->getValue('vacation', false));
+ if ($data === false) {
+ /* Convert vacation from the old format. */
+ $data = unserialize($prefs->getValue('vacation'));
+ } elseif (is_array($data)) {
+ $data = $prefs->convertFromDriver($data, Horde_Nls::getCharset());
+ }
+ if ($data) {
+ $ob->setVacationAddresses($data['addresses'], false);
+ $ob->setVacationDays($data['days']);
+ $ob->setVacationExcludes($data['excludes'], false);
+ $ob->setVacationIgnorelist($data['ignorelist']);
+ $ob->setVacationReason($data['reason']);
+ $ob->setVacationSubject($data['subject']);
+ if (isset($data['start'])) {
+ $ob->setVacationStart($data['start']);
+ }
+ if (isset($data['end'])) {
+ $ob->setVacationEnd($data['end']);
+ }
+ }
+ break;
+
+ case self::ACTION_SPAM:
+ $ob = new Ingo_Storage_Spam();
+ $data = @unserialize($prefs->getValue('spam'));
+ if ($data) {
+ $ob->setSpamFolder($data['folder']);
+ $ob->setSpamLevel($data['level']);
+ }
+ break;
+
+ default:
+ $ob = false;
+ break;
+ }
+
+ return $ob;
+ }
+
+ /**
+ * Stores the specified data in the storage backend.
+ *
+ * @param Ingo_Storage_Rule|Ingo_Storage_Filters $ob The object to store.
+ *
+ * @return boolean True on success.
+ */
+ protected function _store($ob)
+ {
+ $prefs = &Prefs::singleton($GLOBALS['conf']['prefs']['driver'],
+ $GLOBALS['registry']->getApp(),
+ Ingo::getUser(), '', null, false);
+ $prefs->retrieve();
+
+ switch ($ob->obType()) {
+ case self::ACTION_BLACKLIST:
+ $data = array(
+ 'a' => $ob->getBlacklist(),
+ 'f' => $ob->getBlacklistFolder(),
+ );
+ return $prefs->setValue('blacklist', serialize($data));
+
+ case self::ACTION_FILTERS:
+ return $prefs->setValue('rules', serialize(Horde_String::convertCharset($ob->getFilterList(), Horde_Nls::getCharset(), $prefs->getCharset())), false);
+
+ case self::ACTION_FORWARD:
+ $data = array(
+ 'a' => $ob->getForwardAddresses(),
+ 'k' => $ob->getForwardKeep(),
+ );
+ return $prefs->setValue('forward', serialize($data));
+
+ case self::ACTION_VACATION:
+ $data = array(
+ 'addresses' => $ob->getVacationAddresses(),
+ 'days' => $ob->getVacationDays(),
+ 'excludes' => $ob->getVacationExcludes(),
+ 'ignorelist' => $ob->getVacationIgnorelist(),
+ 'reason' => $ob->getVacationReason(),
+ 'subject' => $ob->getVacationSubject(),
+ 'start' => $ob->getVacationStart(),
+ 'end' => $ob->getVacationEnd(),
+ );
+ return $prefs->setValue('vacation', serialize($prefs->convertToDriver($data, Horde_Nls::getCharset())), false);
+
+ case self::ACTION_WHITELIST:
+ return $prefs->setValue('whitelist', serialize($ob->getWhitelist()));
+
+ case self::ACTION_SPAM:
+ $data = array(
+ 'folder' => $ob->getSpamFolder(),
+ 'level' => $ob->getSpamLevel(),
+ );
+ return $prefs->setValue('spam', serialize($data));
+ }
+
+ return false;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Rule:: is the base class for the various action objects
+ * used by Ingo_Storage.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Rule
+{
+ /**
+ * The object type.
+ *
+ * @var integer
+ */
+ protected $_obtype;
+
+ /**
+ * Whether the rule has been saved (if being saved separately).
+ *
+ * @var boolean
+ */
+ protected $_saved = false;
+
+ /**
+ * Returns the object rule type.
+ *
+ * @return integer The object rule type.
+ */
+ public function obType()
+ {
+ return $this->_obtype;
+ }
+
+ /**
+ * Marks the rule as saved or unsaved.
+ *
+ * @param boolean $data Whether the rule has been saved.
+ */
+ public function setSaved($data)
+ {
+ $this->_saved = $data;
+ }
+
+ /**
+ * Returns whether the rule has been saved.
+ *
+ * @return boolean True if the rule has been saved.
+ */
+ public function isSaved()
+ {
+ return $this->_saved;
+ }
+
+ /**
+ * Function to manage an internal address list.
+ *
+ * @param mixed $data The incoming data (array or string).
+ * @param boolean $sort Sort the list?
+ *
+ * @return array The address list.
+ */
+ protected function _addressList($data, $sort)
+ {
+ $output = array();
+
+ if (is_array($data)) {
+ $output = $data;
+ } else {
+ $data = trim($data);
+ $output = (empty($data)) ? array() : preg_split("/\s+/", $data);
+ }
+
+ if ($sort) {
+ $output = Horde_Array::prepareAddressList($output);
+ }
+
+ return $output;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Spam is an object used to hold default spam-rule filtering
+ * information.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Jason M. Felice <jason.m.felice@gmail.com>
+ * @package Ingo
+ */
+class Ingo_Storage_Spam extends Ingo_Storage_Rule
+{
+ /**
+ * The object type.
+ *
+ * @var integer
+ */
+ protected $_obtype = Ingo_Storage::ACTION_SPAM;
+
+ protected $_folder = null;
+ protected $_level = 5;
+
+ public function setSpamFolder($folder)
+ {
+ $this->_folder = $folder;
+ }
+
+ public function setSpamLevel($level)
+ {
+ $this->_level = $level;
+ }
+
+ public function getSpamFolder()
+ {
+ return $this->_folder;
+ }
+
+ public function getSpamLevel()
+ {
+ return $this->_level;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Sql implements the Ingo_Storage API to save Ingo data via
+ * PHP's PEAR database abstraction layer.
+ *
+ * Required values for $params:<pre>
+ * 'phptype' - The database type (e.g. 'pgsql', 'mysql', etc.).
+ * 'charset' - The database's internal charset.</pre>
+ *
+ * Required by some database implementations:<pre>
+ * 'database' - The name of the database.
+ * 'hostspec' - The hostname of the database server.
+ * 'protocol' - The communication protocol ('tcp', 'unix', etc.).
+ * 'username' - The username with which to connect to the database.
+ * 'password' - The password associated with 'username'.
+ * 'options' - Additional options to pass to the database.
+ * 'tty' - The TTY on which to connect to the database.
+ * 'port' - The port on which to connect to the database.</pre>
+ *
+ * The table structure can be created by the scripts/drivers/sql/ingo.sql
+ * script.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Sql extends Ingo_Storage
+{
+ /**
+ * Handle for the current database connection.
+ *
+ * @var DB
+ */
+ protected $_db;
+
+ /**
+ * Handle for the current database connection, used for writing.
+ *
+ * Defaults to the same handle as $_db if a separate write database is not
+ * required.
+ *
+ * @var DB
+ */
+ protected $_write_db;
+
+ /**
+ * Boolean indicating whether or not we're connected to the SQL server.
+ *
+ * @var boolean
+ */
+ protected $_connected = false;
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Additional parameters for the subclass.
+ */
+ public function __construct($params = array())
+ {
+ $this->_params = $params;
+
+ Horde::assertDriverConfig($this->_params, 'storage',
+ array('phptype', 'charset'));
+
+ if (!isset($this->_params['database'])) {
+ $this->_params['database'] = '';
+ }
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = '';
+ }
+ if (!isset($this->_params['hostspec'])) {
+ $this->_params['hostspec'] = '';
+ }
+ $this->_params['table_rules'] = 'ingo_rules';
+ $this->_params['table_lists'] = 'ingo_lists';
+ $this->_params['table_vacations'] = 'ingo_vacations';
+ $this->_params['table_forwards'] = 'ingo_forwards';
+ $this->_params['table_spam'] = 'ingo_spam';
+
+ /* Connect to the SQL server using the supplied parameters. */
+ $this->_write_db = &DB::connect($this->_params,
+ array('persistent' => !empty($this->_params['persistent']),
+ 'ssl' => !empty($this->_params['ssl'])));
+ if (is_a($this->_write_db, 'PEAR_Error')) {
+ Horde::fatal($this->_write_db, __FILE__, __LINE__);
+ }
+ /* Set DB portability options. */
+ switch ($this->_write_db->phptype) {
+ case 'mssql':
+ $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+ break;
+ default:
+ $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+ }
+
+
+ /* Check if we need to set up the read DB connection seperately. */
+ if (!empty($this->_params['splitread'])) {
+ $params = array_merge($this->_params, $this->_params['read']);
+ $this->_db = &DB::connect($params,
+ array('persistent' => !empty($params['persistent']),
+ 'ssl' => !empty($params['ssl'])));
+ if (is_a($this->_db, 'PEAR_Error')) {
+ Horde::fatal($this->_db, __FILE__, __LINE__);
+ }
+
+ switch ($this->_db->phptype) {
+ case 'mssql':
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+ break;
+ default:
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+ }
+ } else {
+ /* Default to the same DB handle for the writer too. */
+ $this->_db =& $this->_write_db;
+ }
+ }
+
+ /**
+ * Retrieves the specified data from the storage backend.
+ *
+ * @param integer $field The field name of the desired data.
+ * See lib/Storage.php for the available fields.
+ * @param boolean $readonly Whether to disable any write operations.
+ *
+ * @return Ingo_Storage_Rule|Ingo_Storage_Filters The specified data.
+ */
+ protected function _retrieve($field, $readonly = false)
+ {
+ switch ($field) {
+ case self::ACTION_BLACKLIST:
+ case self::ACTION_WHITELIST:
+ if ($field == self::ACTION_BLACKLIST) {
+ $ob = new Ingo_Storage_Blacklist();
+ $filters = &$this->retrieve(self::ACTION_FILTERS);
+ if (is_a($filters, 'PEAR_Error')) {
+ return $filters;
+ }
+ $rule = $filters->findRule($field);
+ if (isset($rule['action-value'])) {
+ $ob->setBlacklistFolder($rule['action-value']);
+ }
+ } else {
+ $ob = new Ingo_Storage_Whitelist();
+ }
+ $query = sprintf('SELECT list_address FROM %s WHERE list_owner = ? AND list_blacklist = ?',
+ $this->_params['table_lists']);
+ $values = array(Ingo::getUser(),
+ (int)($field == self::ACTION_BLACKLIST));
+ Horde::logMessage('Ingo_Storage_Sql::_retrieve(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $addresses = $this->_db->getCol($query, 0, $values);
+ if (is_a($addresses, 'PEAR_Error')) {
+ Horde::logMessage($addresses, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $addresses;
+ }
+ if ($field == self::ACTION_BLACKLIST) {
+ $ob->setBlacklist($addresses, false);
+ } else {
+ $ob->setWhitelist($addresses, false);
+ }
+ break;
+
+ case self::ACTION_FILTERS:
+ $ob = new Ingo_Storage_Filters_Sql($this->_db, $this->_write_db, $this->_params);
+ if (is_a($result = $ob->init($readonly), 'PEAR_Error')) {
+ return $result;
+ }
+ break;
+
+ case self::ACTION_FORWARD:
+ $query = sprintf('SELECT * FROM %s WHERE forward_owner = ?',
+ $this->_params['table_forwards']);
+ Horde::logMessage('Ingo_Storage_Sql::_retrieve(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($query, Ingo::getUser());
+ $data = $result->fetchRow(DB_FETCHMODE_ASSOC);
+
+ $ob = new Ingo_Storage_Forward();
+ if ($data && !is_a($data, 'PEAR_Error')) {
+ $ob->setForwardAddresses(explode("\n", $data['forward_addresses']), false);
+ $ob->setForwardKeep((bool)$data['forward_keep']);
+ $ob->setSaved(true);
+ } elseif ($data = @unserialize($GLOBALS['prefs']->getDefault('vacation'))) {
+ $ob->setForwardAddresses($data['a'], false);
+ $ob->setForwardKeep($data['k']);
+ }
+ break;
+
+ case self::ACTION_VACATION:
+ $query = sprintf('SELECT * FROM %s WHERE vacation_owner = ?',
+ $this->_params['table_vacations']);
+ Horde::logMessage('Ingo_Storage_Sql::_retrieve(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($query, Ingo::getUser());
+ $data = $result->fetchRow(DB_FETCHMODE_ASSOC);
+
+ $ob = new Ingo_Storage_Vacation();
+ if ($data && !is_a($data, 'PEAR_Error')) {
+ $ob->setVacationAddresses(explode("\n", $data['vacation_addresses']), false);
+ $ob->setVacationDays((int)$data['vacation_days']);
+ $ob->setVacationStart((int)$data['vacation_start']);
+ $ob->setVacationEnd((int)$data['vacation_end']);
+ $ob->setVacationExcludes(explode("\n", $data['vacation_excludes']), false);
+ $ob->setVacationIgnorelist((bool)$data['vacation_ignorelists']);
+ $ob->setVacationReason(Horde_String::convertCharset($data['vacation_reason'], $this->_params['charset']));
+ $ob->setVacationSubject(Horde_String::convertCharset($data['vacation_subject'], $this->_params['charset']));
+ $ob->setSaved(true);
+ } elseif ($data = @unserialize($GLOBALS['prefs']->getDefault('vacation'))) {
+ $ob->setVacationAddresses($data['addresses'], false);
+ $ob->setVacationDays($data['days']);
+ $ob->setVacationExcludes($data['excludes'], false);
+ $ob->setVacationIgnorelist($data['ignorelist']);
+ $ob->setVacationReason($data['reason']);
+ $ob->setVacationSubject($data['subject']);
+ if (isset($data['start'])) {
+ $ob->setVacationStart($data['start']);
+ }
+ if (isset($data['end'])) {
+ $ob->setVacationEnd($data['end']);
+ }
+ }
+ break;
+
+ case self::ACTION_SPAM:
+ $query = sprintf('SELECT * FROM %s WHERE spam_owner = ?',
+ $this->_params['table_spam']);
+ Horde::logMessage('Ingo_Storage_Sql::_retrieve(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($query, Ingo::getUser());
+ $data = $result->fetchRow(DB_FETCHMODE_ASSOC);
+
+ $ob = new Ingo_Storage_Spam();
+ if ($data && !is_a($data, 'PEAR_Error')) {
+ $ob->setSpamFolder($data['spam_folder']);
+ $ob->setSpamLevel((int)$data['spam_level']);
+ $ob->setSaved(true);
+ } elseif ($data = @unserialize($GLOBALS['prefs']->getDefault('spam'))) {
+ $ob->setSpamFolder($data['folder']);
+ $ob->setSpamLevel($data['level']);
+ }
+ break;
+
+ default:
+ $ob = false;
+ }
+
+ return $ob;
+ }
+
+ /**
+ * Stores the specified data in the storage backend.
+ *
+ * @access private
+ *
+ * @param Ingo_Storage_Rule|Ingo_Storage_Filters $ob The object to store.
+ *
+ * @return boolean True on success.
+ */
+ protected function _store(&$ob)
+ {
+ switch ($ob->obType()) {
+ case self::ACTION_BLACKLIST:
+ case self::ACTION_WHITELIST:
+ $is_blacklist = (int)($ob->obType() == self::ACTION_BLACKLIST);
+ if ($is_blacklist) {
+ $filters = &$this->retrieve(self::ACTION_FILTERS);
+ if (is_a($filters, 'PEAR_Error')) {
+ return $filters;
+ }
+ $id = $filters->findRuleId(self::ACTION_BLACKLIST);
+ if ($id !== null) {
+ $rule = $filters->getRule($id);
+ if (!isset($rule['action-value']) ||
+ $rule['action-value'] != $ob->getBlacklistFolder()) {
+ $rule['action-value'] = $ob->getBlacklistFolder();
+ $filters->updateRule($rule, $id);
+ }
+ }
+ }
+ $query = sprintf('DELETE FROM %s WHERE list_owner = ? AND list_blacklist = ?',
+ $this->_params['table_lists']);
+ $values = array(Ingo::getUser(), $is_blacklist);
+ Horde::logMessage('Ingo_Storage_Sql::_store(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ $query = sprintf('INSERT INTO %s (list_owner, list_blacklist, list_address) VALUES (?, ?, ?)',
+ $this->_params['table_lists']);
+ Horde::logMessage('Ingo_Storage_Sql::_store(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $addresses = $is_blacklist ? $ob->getBlacklist() : $ob->getWhitelist();
+ foreach ($addresses as $address) {
+ $result = $this->_write_db->query($query,
+ array(Ingo::getUser(),
+ $is_blacklist,
+ $address));
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ }
+ $ob->setSaved(true);
+ $ret = true;
+ break;
+
+ case self::ACTION_FILTERS:
+ $ret = true;
+ break;
+
+ case self::ACTION_FORWARD:
+ if ($ob->isSaved()) {
+ $query = 'UPDATE %s SET forward_addresses = ?, forward_keep = ? WHERE forward_owner = ?';
+ } else {
+ $query = 'INSERT INTO %s (forward_addresses, forward_keep, forward_owner) VALUES (?, ?, ?)';
+ }
+ $query = sprintf($query, $this->_params['table_forwards']);
+ $values = array(
+ implode("\n", $ob->getForwardAddresses()),
+ (int)(bool)$ob->getForwardKeep(),
+ Ingo::getUser());
+ Horde::logMessage('Ingo_Storage_Sql::_store(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $ret = $this->_write_db->query($query, $values);
+ if (!is_a($ret, 'PEAR_Error')) {
+ $ob->setSaved(true);
+ }
+ break;
+
+ case self::ACTION_VACATION:
+ if ($ob->isSaved()) {
+ $query = 'UPDATE %s SET vacation_addresses = ?, vacation_subject = ?, vacation_reason = ?, vacation_days = ?, vacation_start = ?, vacation_end = ?, vacation_excludes = ?, vacation_ignorelists = ? WHERE vacation_owner = ?';
+ } else {
+ $query = 'INSERT INTO %s (vacation_addresses, vacation_subject, vacation_reason, vacation_days, vacation_start, vacation_end, vacation_excludes, vacation_ignorelists, vacation_owner) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)';
+ }
+ $query = sprintf($query, $this->_params['table_vacations']);
+ $values = array(
+ implode("\n", $ob->getVacationAddresses()),
+ Horde_String::convertCharset($ob->getVacationSubject(),
+ Horde_Nls::getCharset(),
+ $this->_params['charset']),
+ Horde_String::convertCharset($ob->getVacationReason(),
+ Horde_Nls::getCharset(),
+ $this->_params['charset']),
+ (int)$ob->getVacationDays(),
+ (int)$ob->getVacationStart(),
+ (int)$ob->getVacationEnd(),
+ implode("\n", $ob->getVacationExcludes()),
+ (int)(bool)$ob->getVacationIgnorelist(),
+ Ingo::getUser());
+ Horde::logMessage('Ingo_Storage_Sql::_store(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $ret = $this->_write_db->query($query, $values);
+ if (!is_a($ret, 'PEAR_Error')) {
+ $ob->setSaved(true);
+ }
+ break;
+
+ case self::ACTION_SPAM:
+ if ($ob->isSaved()) {
+ $query = 'UPDATE %s SET spam_level = ?, spam_folder = ? WHERE spam_owner = ?';
+ } else {
+ $query = 'INSERT INTO %s (spam_level, spam_folder, spam_owner) VALUES (?, ?, ?)';
+ }
+ $query = sprintf($query, $this->_params['table_spam']);
+ $values = array(
+ (int)$ob->getSpamLevel(),
+ $ob->getSpamFolder(),
+ Ingo::getUser());
+ Horde::logMessage('Ingo_Storage_Sql::_store(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $ret = $this->_write_db->query($query, $values);
+ if (!is_a($ret, 'PEAR_Error')) {
+ $ob->setSaved(true);
+ }
+ break;
+
+ default:
+ $ret = false;
+ break;
+ }
+
+ if (is_a($ret, 'PEAR_Error')) {
+ Horde::logMessage($ret, __FILE__, __LINE__);
+ }
+
+ return $ret;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Vacation is the object used to hold vacation rule
+ * information.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Vacation extends Ingo_Storage_Rule
+{
+ protected $_addr = array();
+ protected $_days = 7;
+ protected $_excludes = array();
+ protected $_ignorelist = true;
+ protected $_reason = '';
+ protected $_subject = '';
+ protected $_start;
+ protected $_end;
+ protected $_obtype = Ingo_Storage::ACTION_VACATION;
+
+ public function setVacationAddresses($data, $sort = true)
+ {
+ $this->_addr = &$this->_addressList($data, $sort);
+ }
+
+ public function setVacationDays($data)
+ {
+ $this->_days = $data;
+ }
+
+ public function setVacationExcludes($data, $sort = true)
+ {
+ $this->_excludes = &$this->_addressList($data, $sort);
+ }
+
+ public function setVacationIgnorelist($data)
+ {
+ $this->_ignorelist = $data;
+ }
+
+ public function setVacationReason($data)
+ {
+ $this->_reason = $data;
+ }
+
+ public function setVacationSubject($data)
+ {
+ $this->_subject = $data;
+ }
+
+ public function setVacationStart($data)
+ {
+ $this->_start = $data;
+ }
+
+ public function setVacationEnd($data)
+ {
+ $this->_end = $data;
+ }
+
+ public function getVacationAddresses()
+ {
+ if (empty($GLOBALS['conf']['hooks']['vacation_addresses'])) {
+ return $this->_addr;
+ }
+
+ try {
+ return Horde::callHook('_ingo_hook_vacation_addresses', array(Ingo::getUser()), 'ingo');
+ } catch (Horde_Exception $e) {
+ return array();
+ }
+ }
+
+ public function getVacationDays()
+ {
+ return $this->_days;
+ }
+
+ public function getVacationExcludes()
+ {
+ return $this->_excludes;
+ }
+
+ public function getVacationIgnorelist()
+ {
+ return $this->_ignorelist;
+ }
+
+ public function getVacationReason()
+ {
+ return $this->_reason;
+ }
+
+ public function getVacationSubject()
+ {
+ return $this->_subject;
+ }
+
+ public function getVacationStart()
+ {
+ return $this->_start;
+ }
+
+ public function getVacationStartYear()
+ {
+ return date('Y', $this->_start);
+ }
+
+ public function getVacationStartMonth()
+ {
+ return date('n', $this->_start);
+ }
+
+ public function getVacationStartDay()
+ {
+ return date('j', $this->_start);
+ }
+
+ public function getVacationEnd()
+ {
+ return $this->_end;
+ }
+
+ public function getVacationEndYear()
+ {
+ return date('Y', $this->_end);
+ }
+
+ public function getVacationEndMonth()
+ {
+ return date('n', $this->_end);
+ }
+
+ public function getVacationEndDay()
+ {
+ return date('j', $this->_end);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Storage_Whitelist is the object used to hold whitelist rule
+ * information.
+ *
+ * See the enclosed file LICENSE for license information (ASL). If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Storage_Whitelist extends Ingo_Storage_Rule
+{
+ protected $_addr = array();
+ protected $_obtype = Ingo_Storage::ACTION_WHITELIST;
+
+ /**
+ * Sets the list of whitelisted addresses.
+ *
+ * @param mixed $data The list of addresses (array or string).
+ * @param boolean $sort Sort the list?
+ *
+ * @return mixed PEAR_Error on error, true on success.
+ */
+ public function setWhitelist($data, $sort = true)
+ {
+ $addr = &$this->_addressList($data, $sort);
+ $addr = array_filter($addr, array('Ingo', 'filterEmptyAddress'));
+ if (!empty($GLOBALS['conf']['storage']['maxwhitelist'])) {
+ $addr_count = count($addr);
+ if ($addr_count > $GLOBALS['conf']['storage']['maxwhitelist']) {
+ return PEAR::raiseError(sprintf(_("Maximum number of whitelisted addresses exceeded (Total addresses: %s, Maximum addresses: %s). Could not add new addresses to whitelist."), $addr_count, $GLOBALS['conf']['storage']['maxwhitelist']), 'horde.error');
+ }
+ }
+
+ $this->_addr = $addr;
+ return true;
+ }
+
+ public function getWhitelist()
+ {
+ return empty($this->_addr)
+ ? array()
+ : array_filter($this->_addr, array('Ingo', 'filterEmptyAddress'));
+ }
+
+}
+++ /dev/null
-<?php
-/**
- * Ingo_Storage_mock:: is used for testing purposes. It just keeps the
- * data local and doesn't put it anywhere.
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Jason M. Felice <jason.m.felice@gmail.com>
- * @package Ingo
- */
-
-class Ingo_Storage_mock extends Ingo_Storage
-{
- protected $_data = array();
-
- protected function &_retrieve($field)
- {
- if (empty($this->_data[$field])) {
- switch ($field) {
- case self::ACTION_BLACKLIST:
- return new Ingo_Storage_blacklist();
-
- case self::ACTION_FILTERS:
- $ob = new Ingo_Storage_filters();
- include INGO_BASE . '/config/prefs.php.dist';
- $ob->setFilterList(unserialize($_prefs['rules']['value']));
- return $ob;
-
- case self::ACTION_FORWARD:
- return new Ingo_Storage_forward();
-
- case self::ACTION_VACATION:
- return new Ingo_Storage_vacation();
-
- case self::ACTION_WHITELIST:
- return new Ingo_Storage_whitelist();
-
- case self::ACTION_SPAM:
- return new Ingo_Storage_spam();
-
- default:
- return false;
- }
- }
-
- return $this->_data[$field];
- }
-
- protected function _store(&$ob)
- {
- $this->_data[$ob->obType()] = $ob;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Ingo_Storage_prefs:: implements the Ingo_Storage:: API to save Ingo data
- * via the Horde preferences system.
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @author Jan Schneider <jan@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_prefs extends Ingo_Storage
-{
- /**
- * Constructor.
- *
- * @param array $params Additional parameters for the subclass.
- */
- public function __construct($params = array())
- {
- $this->_params = $params;
- }
-
- /**
- * Retrieves the specified data from the storage backend.
- *
- * @param integer $field The field name of the desired data.
- * See lib/Storage.php for the available fields.
- * @param boolean $readonly Whether to disable any write operations.
- *
- * @return Ingo_Storage_rule|Ingo_Storage_filters The specified data.
- */
- protected function _retrieve($field, $readonly = false)
- {
- $prefs = &Prefs::singleton($GLOBALS['conf']['prefs']['driver'],
- $GLOBALS['registry']->getApp(),
- Ingo::getUser(), '', null, false);
- $prefs->retrieve();
-
- switch ($field) {
- case self::ACTION_BLACKLIST:
- $ob = new Ingo_Storage_blacklist();
- $data = @unserialize($prefs->getValue('blacklist'));
- if ($data) {
- $ob->setBlacklist($data['a'], false);
- $ob->setBlacklistFolder($data['f']);
- }
- break;
-
- case self::ACTION_WHITELIST:
- $ob = new Ingo_Storage_whitelist();
- $data = @unserialize($prefs->getValue('whitelist'));
- if ($data) {
- $ob->setWhitelist($data, false);
- }
- break;
-
- case self::ACTION_FILTERS:
- $ob = new Ingo_Storage_filters();
- $data = @unserialize($prefs->getValue('rules', false));
- if ($data === false) {
- /* Convert rules from the old format. */
- $data = @unserialize($prefs->getValue('rules'));
- } else {
- $data = Horde_String::convertCharset($data, $prefs->getCharset(), Horde_Nls::getCharset());
- }
- if ($data) {
- $ob->setFilterlist($data);
- }
- break;
-
- case self::ACTION_FORWARD:
- $ob = new Ingo_Storage_forward();
- $data = @unserialize($prefs->getValue('forward'));
- if ($data) {
- $ob->setForwardAddresses($data['a'], false);
- $ob->setForwardKeep($data['k']);
- }
- break;
-
- case self::ACTION_VACATION:
- $ob = new Ingo_Storage_vacation();
- $data = @unserialize($prefs->getValue('vacation', false));
- if ($data === false) {
- /* Convert vacation from the old format. */
- $data = unserialize($prefs->getValue('vacation'));
- } elseif (is_array($data)) {
- $data = $prefs->convertFromDriver($data, Horde_Nls::getCharset());
- }
- if ($data) {
- $ob->setVacationAddresses($data['addresses'], false);
- $ob->setVacationDays($data['days']);
- $ob->setVacationExcludes($data['excludes'], false);
- $ob->setVacationIgnorelist($data['ignorelist']);
- $ob->setVacationReason($data['reason']);
- $ob->setVacationSubject($data['subject']);
- if (isset($data['start'])) {
- $ob->setVacationStart($data['start']);
- }
- if (isset($data['end'])) {
- $ob->setVacationEnd($data['end']);
- }
- }
- break;
-
- case self::ACTION_SPAM:
- $ob = new Ingo_Storage_spam();
- $data = @unserialize($prefs->getValue('spam'));
- if ($data) {
- $ob->setSpamFolder($data['folder']);
- $ob->setSpamLevel($data['level']);
- }
- break;
-
- default:
- $ob = false;
- break;
- }
-
- return $ob;
- }
-
- /**
- * Stores the specified data in the storage backend.
- *
- * @param Ingo_Storage_rule|Ingo_Storage_filters $ob The object to store.
- *
- * @return boolean True on success.
- */
- protected function _store($ob)
- {
- $prefs = &Prefs::singleton($GLOBALS['conf']['prefs']['driver'],
- $GLOBALS['registry']->getApp(),
- Ingo::getUser(), '', null, false);
- $prefs->retrieve();
-
- switch ($ob->obType()) {
- case self::ACTION_BLACKLIST:
- $data = array(
- 'a' => $ob->getBlacklist(),
- 'f' => $ob->getBlacklistFolder(),
- );
- return $prefs->setValue('blacklist', serialize($data));
-
- case self::ACTION_FILTERS:
- return $prefs->setValue('rules', serialize(Horde_String::convertCharset($ob->getFilterList(), Horde_Nls::getCharset(), $prefs->getCharset())), false);
-
- case self::ACTION_FORWARD:
- $data = array(
- 'a' => $ob->getForwardAddresses(),
- 'k' => $ob->getForwardKeep(),
- );
- return $prefs->setValue('forward', serialize($data));
-
- case self::ACTION_VACATION:
- $data = array(
- 'addresses' => $ob->getVacationAddresses(),
- 'days' => $ob->getVacationDays(),
- 'excludes' => $ob->getVacationExcludes(),
- 'ignorelist' => $ob->getVacationIgnorelist(),
- 'reason' => $ob->getVacationReason(),
- 'subject' => $ob->getVacationSubject(),
- 'start' => $ob->getVacationStart(),
- 'end' => $ob->getVacationEnd(),
- );
- return $prefs->setValue('vacation', serialize($prefs->convertToDriver($data, Horde_Nls::getCharset())), false);
-
- case self::ACTION_WHITELIST:
- return $prefs->setValue('whitelist', serialize($ob->getWhitelist()));
-
- case self::ACTION_SPAM:
- $data = array(
- 'folder' => $ob->getSpamFolder(),
- 'level' => $ob->getSpamLevel(),
- );
- return $prefs->setValue('spam', serialize($data));
- }
-
- return false;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Ingo_Storage_sql implements the Ingo_Storage API to save Ingo data via
- * PHP's PEAR database abstraction layer.
- *
- * Required values for $params:<pre>
- * 'phptype' - The database type (e.g. 'pgsql', 'mysql', etc.).
- * 'charset' - The database's internal charset.</pre>
- *
- * Required by some database implementations:<pre>
- * 'database' - The name of the database.
- * 'hostspec' - The hostname of the database server.
- * 'protocol' - The communication protocol ('tcp', 'unix', etc.).
- * 'username' - The username with which to connect to the database.
- * 'password' - The password associated with 'username'.
- * 'options' - Additional options to pass to the database.
- * 'tty' - The TTY on which to connect to the database.
- * 'port' - The port on which to connect to the database.</pre>
- *
- * The table structure can be created by the scripts/drivers/sql/ingo.sql
- * script.
- *
- * See the enclosed file LICENSE for license information (ASL). If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author Jan Schneider <jan@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_sql extends Ingo_Storage
-{
- /**
- * Handle for the current database connection.
- *
- * @var DB
- */
- protected $_db;
-
- /**
- * Handle for the current database connection, used for writing.
- *
- * Defaults to the same handle as $_db if a separate write database is not
- * required.
- *
- * @var DB
- */
- protected $_write_db;
-
- /**
- * Boolean indicating whether or not we're connected to the SQL server.
- *
- * @var boolean
- */
- protected $_connected = false;
-
- /**
- * Constructor.
- *
- * @param array $params Additional parameters for the subclass.
- */
- public function __construct($params = array())
- {
- $this->_params = $params;
-
- Horde::assertDriverConfig($this->_params, 'storage',
- array('phptype', 'charset'));
-
- if (!isset($this->_params['database'])) {
- $this->_params['database'] = '';
- }
- if (!isset($this->_params['username'])) {
- $this->_params['username'] = '';
- }
- if (!isset($this->_params['hostspec'])) {
- $this->_params['hostspec'] = '';
- }
- $this->_params['table_rules'] = 'ingo_rules';
- $this->_params['table_lists'] = 'ingo_lists';
- $this->_params['table_vacations'] = 'ingo_vacations';
- $this->_params['table_forwards'] = 'ingo_forwards';
- $this->_params['table_spam'] = 'ingo_spam';
-
- /* Connect to the SQL server using the supplied parameters. */
- $this->_write_db = &DB::connect($this->_params,
- array('persistent' => !empty($this->_params['persistent']),
- 'ssl' => !empty($this->_params['ssl'])));
- if (is_a($this->_write_db, 'PEAR_Error')) {
- Horde::fatal($this->_write_db, __FILE__, __LINE__);
- }
- /* Set DB portability options. */
- switch ($this->_write_db->phptype) {
- case 'mssql':
- $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
- break;
- default:
- $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
- }
-
-
- /* Check if we need to set up the read DB connection seperately. */
- if (!empty($this->_params['splitread'])) {
- $params = array_merge($this->_params, $this->_params['read']);
- $this->_db = &DB::connect($params,
- array('persistent' => !empty($params['persistent']),
- 'ssl' => !empty($params['ssl'])));
- if (is_a($this->_db, 'PEAR_Error')) {
- Horde::fatal($this->_db, __FILE__, __LINE__);
- }
-
- switch ($this->_db->phptype) {
- case 'mssql':
- $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
- break;
- default:
- $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
- }
- } else {
- /* Default to the same DB handle for the writer too. */
- $this->_db =& $this->_write_db;
- }
- }
-
- /**
- * Retrieves the specified data from the storage backend.
- *
- * @param integer $field The field name of the desired data.
- * See lib/Storage.php for the available fields.
- * @param boolean $readonly Whether to disable any write operations.
- *
- * @return Ingo_Storage_rule|Ingo_Storage_filters The specified data.
- */
- protected function _retrieve($field, $readonly = false)
- {
- switch ($field) {
- case self::ACTION_BLACKLIST:
- case self::ACTION_WHITELIST:
- if ($field == self::ACTION_BLACKLIST) {
- $ob = new Ingo_Storage_blacklist();
- $filters = &$this->retrieve(self::ACTION_FILTERS);
- if (is_a($filters, 'PEAR_Error')) {
- return $filters;
- }
- $rule = $filters->findRule($field);
- if (isset($rule['action-value'])) {
- $ob->setBlacklistFolder($rule['action-value']);
- }
- } else {
- $ob = new Ingo_Storage_whitelist();
- }
- $query = sprintf('SELECT list_address FROM %s WHERE list_owner = ? AND list_blacklist = ?',
- $this->_params['table_lists']);
- $values = array(Ingo::getUser(),
- (int)($field == self::ACTION_BLACKLIST));
- Horde::logMessage('Ingo_Storage_sql::_retrieve(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $addresses = $this->_db->getCol($query, 0, $values);
- if (is_a($addresses, 'PEAR_Error')) {
- Horde::logMessage($addresses, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $addresses;
- }
- if ($field == self::ACTION_BLACKLIST) {
- $ob->setBlacklist($addresses, false);
- } else {
- $ob->setWhitelist($addresses, false);
- }
- break;
-
- case self::ACTION_FILTERS:
- $ob = new Ingo_Storage_filters_sql($this->_db, $this->_write_db, $this->_params);
- if (is_a($result = $ob->init($readonly), 'PEAR_Error')) {
- return $result;
- }
- break;
-
- case self::ACTION_FORWARD:
- $query = sprintf('SELECT * FROM %s WHERE forward_owner = ?',
- $this->_params['table_forwards']);
- Horde::logMessage('Ingo_Storage_sql::_retrieve(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_db->query($query, Ingo::getUser());
- $data = $result->fetchRow(DB_FETCHMODE_ASSOC);
-
- $ob = new Ingo_Storage_forward();
- if ($data && !is_a($data, 'PEAR_Error')) {
- $ob->setForwardAddresses(explode("\n", $data['forward_addresses']), false);
- $ob->setForwardKeep((bool)$data['forward_keep']);
- $ob->setSaved(true);
- } elseif ($data = @unserialize($GLOBALS['prefs']->getDefault('vacation'))) {
- $ob->setForwardAddresses($data['a'], false);
- $ob->setForwardKeep($data['k']);
- }
- break;
-
- case self::ACTION_VACATION:
- $query = sprintf('SELECT * FROM %s WHERE vacation_owner = ?',
- $this->_params['table_vacations']);
- Horde::logMessage('Ingo_Storage_sql::_retrieve(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_db->query($query, Ingo::getUser());
- $data = $result->fetchRow(DB_FETCHMODE_ASSOC);
-
- $ob = new Ingo_Storage_vacation();
- if ($data && !is_a($data, 'PEAR_Error')) {
- $ob->setVacationAddresses(explode("\n", $data['vacation_addresses']), false);
- $ob->setVacationDays((int)$data['vacation_days']);
- $ob->setVacationStart((int)$data['vacation_start']);
- $ob->setVacationEnd((int)$data['vacation_end']);
- $ob->setVacationExcludes(explode("\n", $data['vacation_excludes']), false);
- $ob->setVacationIgnorelist((bool)$data['vacation_ignorelists']);
- $ob->setVacationReason(Horde_String::convertCharset($data['vacation_reason'], $this->_params['charset']));
- $ob->setVacationSubject(Horde_String::convertCharset($data['vacation_subject'], $this->_params['charset']));
- $ob->setSaved(true);
- } elseif ($data = @unserialize($GLOBALS['prefs']->getDefault('vacation'))) {
- $ob->setVacationAddresses($data['addresses'], false);
- $ob->setVacationDays($data['days']);
- $ob->setVacationExcludes($data['excludes'], false);
- $ob->setVacationIgnorelist($data['ignorelist']);
- $ob->setVacationReason($data['reason']);
- $ob->setVacationSubject($data['subject']);
- if (isset($data['start'])) {
- $ob->setVacationStart($data['start']);
- }
- if (isset($data['end'])) {
- $ob->setVacationEnd($data['end']);
- }
- }
- break;
-
- case self::ACTION_SPAM:
- $query = sprintf('SELECT * FROM %s WHERE spam_owner = ?',
- $this->_params['table_spam']);
- Horde::logMessage('Ingo_Storage_sql::_retrieve(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_db->query($query, Ingo::getUser());
- $data = $result->fetchRow(DB_FETCHMODE_ASSOC);
-
- $ob = new Ingo_Storage_spam();
- if ($data && !is_a($data, 'PEAR_Error')) {
- $ob->setSpamFolder($data['spam_folder']);
- $ob->setSpamLevel((int)$data['spam_level']);
- $ob->setSaved(true);
- } elseif ($data = @unserialize($GLOBALS['prefs']->getDefault('spam'))) {
- $ob->setSpamFolder($data['folder']);
- $ob->setSpamLevel($data['level']);
- }
- break;
-
- default:
- $ob = false;
- }
-
- return $ob;
- }
-
- /**
- * Stores the specified data in the storage backend.
- *
- * @access private
- *
- * @param Ingo_Storage_rule|Ingo_Storage_filters $ob The object to store.
- *
- * @return boolean True on success.
- */
- protected function _store(&$ob)
- {
- switch ($ob->obType()) {
- case self::ACTION_BLACKLIST:
- case self::ACTION_WHITELIST:
- $is_blacklist = (int)($ob->obType() == self::ACTION_BLACKLIST);
- if ($is_blacklist) {
- $filters = &$this->retrieve(self::ACTION_FILTERS);
- if (is_a($filters, 'PEAR_Error')) {
- return $filters;
- }
- $id = $filters->findRuleId(self::ACTION_BLACKLIST);
- if ($id !== null) {
- $rule = $filters->getRule($id);
- if (!isset($rule['action-value']) ||
- $rule['action-value'] != $ob->getBlacklistFolder()) {
- $rule['action-value'] = $ob->getBlacklistFolder();
- $filters->updateRule($rule, $id);
- }
- }
- }
- $query = sprintf('DELETE FROM %s WHERE list_owner = ? AND list_blacklist = ?',
- $this->_params['table_lists']);
- $values = array(Ingo::getUser(), $is_blacklist);
- Horde::logMessage('Ingo_Storage_sql::_store(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
- $query = sprintf('INSERT INTO %s (list_owner, list_blacklist, list_address) VALUES (?, ?, ?)',
- $this->_params['table_lists']);
- Horde::logMessage('Ingo_Storage_sql::_store(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $addresses = $is_blacklist ? $ob->getBlacklist() : $ob->getWhitelist();
- foreach ($addresses as $address) {
- $result = $this->_write_db->query($query,
- array(Ingo::getUser(),
- $is_blacklist,
- $address));
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
- }
- $ob->setSaved(true);
- $ret = true;
- break;
-
- case self::ACTION_FILTERS:
- $ret = true;
- break;
-
- case self::ACTION_FORWARD:
- if ($ob->isSaved()) {
- $query = 'UPDATE %s SET forward_addresses = ?, forward_keep = ? WHERE forward_owner = ?';
- } else {
- $query = 'INSERT INTO %s (forward_addresses, forward_keep, forward_owner) VALUES (?, ?, ?)';
- }
- $query = sprintf($query, $this->_params['table_forwards']);
- $values = array(
- implode("\n", $ob->getForwardAddresses()),
- (int)(bool)$ob->getForwardKeep(),
- Ingo::getUser());
- Horde::logMessage('Ingo_Storage_sql::_store(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $ret = $this->_write_db->query($query, $values);
- if (!is_a($ret, 'PEAR_Error')) {
- $ob->setSaved(true);
- }
- break;
-
- case self::ACTION_VACATION:
- if ($ob->isSaved()) {
- $query = 'UPDATE %s SET vacation_addresses = ?, vacation_subject = ?, vacation_reason = ?, vacation_days = ?, vacation_start = ?, vacation_end = ?, vacation_excludes = ?, vacation_ignorelists = ? WHERE vacation_owner = ?';
- } else {
- $query = 'INSERT INTO %s (vacation_addresses, vacation_subject, vacation_reason, vacation_days, vacation_start, vacation_end, vacation_excludes, vacation_ignorelists, vacation_owner) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)';
- }
- $query = sprintf($query, $this->_params['table_vacations']);
- $values = array(
- implode("\n", $ob->getVacationAddresses()),
- Horde_String::convertCharset($ob->getVacationSubject(),
- Horde_Nls::getCharset(),
- $this->_params['charset']),
- Horde_String::convertCharset($ob->getVacationReason(),
- Horde_Nls::getCharset(),
- $this->_params['charset']),
- (int)$ob->getVacationDays(),
- (int)$ob->getVacationStart(),
- (int)$ob->getVacationEnd(),
- implode("\n", $ob->getVacationExcludes()),
- (int)(bool)$ob->getVacationIgnorelist(),
- Ingo::getUser());
- Horde::logMessage('Ingo_Storage_sql::_store(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $ret = $this->_write_db->query($query, $values);
- if (!is_a($ret, 'PEAR_Error')) {
- $ob->setSaved(true);
- }
- break;
-
- case self::ACTION_SPAM:
- if ($ob->isSaved()) {
- $query = 'UPDATE %s SET spam_level = ?, spam_folder = ? WHERE spam_owner = ?';
- } else {
- $query = 'INSERT INTO %s (spam_level, spam_folder, spam_owner) VALUES (?, ?, ?)';
- }
- $query = sprintf($query, $this->_params['table_spam']);
- $values = array(
- (int)$ob->getSpamLevel(),
- $ob->getSpamFolder(),
- Ingo::getUser());
- Horde::logMessage('Ingo_Storage_sql::_store(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $ret = $this->_write_db->query($query, $values);
- if (!is_a($ret, 'PEAR_Error')) {
- $ob->setSaved(true);
- }
- break;
-
- default:
- $ret = false;
- break;
- }
-
- if (is_a($ret, 'PEAR_Error')) {
- Horde::logMessage($ret, __FILE__, __LINE__);
- }
-
- return $ret;
- }
-
-}
-
-/**
- * Ingo_Storage_filters_sql is the object used to hold user-defined filtering
- * rule information.
- *
- * @author Jan Schneider <jan@horde.org>
- * @package Ingo
- */
-class Ingo_Storage_filters_sql extends Ingo_Storage_filters {
-
- /**
- * Handle for the current database connection.
- *
- * @var DB
- */
- protected $_db;
-
- /**
- * Handle for the current database connection, used for writing.
- *
- * Defaults to the same handle as $_db if a separate write database is not
- * required.
- *
- * @var DB
- */
- protected $_write_db;
-
- /**
- * Driver specific parameters.
- *
- * @var array
- */
- protected $_params;
-
- /**
- * Constructor.
- *
- * @param DB $db Handle for the database connection.
- * @param DB $write_db Handle for the database connection, used for
- * writing.
- * @param array $params Driver specific parameters.
- */
- public function __construct($db, $write_db, $params)
- {
- $this->_db = $db;
- $this->_write_db = $write_db;
- $this->_params = $params;
- }
-
- /**
- * Loads all rules from the DB backend.
- *
- * @param boolean $readonly Whether to disable any write operations.
- */
- public function init($readonly = false)
- {
- $query = sprintf('SELECT * FROM %s WHERE rule_owner = ? ORDER BY rule_order',
- $this->_params['table_rules']);
- $values = array(Ingo::getUser());
- Horde::logMessage('Ingo_Storage_filters_sql(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $data = array();
- while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
- $data[$row['rule_order']] = array(
- 'id' => (int)$row['rule_id'],
- 'name' => Horde_String::convertCharset($row['rule_name'], $this->_params['charset']),
- 'action' => (int)$row['rule_action'],
- 'action-value' => Horde_String::convertCharset($row['rule_value'], $this->_params['charset']),
- 'flags' => (int)$row['rule_flags'],
- 'conditions' => empty($row['rule_conditions']) ? null : Horde_String::convertCharset(unserialize($row['rule_conditions']), $this->_params['charset']),
- 'combine' => (int)$row['rule_combine'],
- 'stop' => (bool)$row['rule_stop'],
- 'disable' => !(bool)$row['rule_active']);
- }
- $this->setFilterlist($data);
-
- if (empty($data) && !$readonly) {
- $data = @unserialize($GLOBALS['prefs']->getDefault('rules'));
- if ($data) {
- foreach ($data as $val) {
- $this->addRule($val, false);
- }
- } else {
- $this->addRule(
- array('name' => 'Whitelist',
- 'action' => Ingo_Storage::ACTION_WHITELIST),
- false);
- $this->addRule(
- array('name' => 'Vacation',
- 'action' => Ingo_Storage::ACTION_VACATION,
- 'disable' => true),
- false);
- $this->addRule(
- array('name' => 'Blacklist',
- 'action' => Ingo_Storage::ACTION_BLACKLIST),
- false);
- $this->addRule(
- array('name' => 'Spam Filter',
- 'action' => Ingo_Storage::ACTION_SPAM,
- 'disable' => true),
- false);
- $this->addRule(
- array('name' => 'Forward',
- 'action' => Ingo_Storage::ACTION_FORWARD),
- false);
- }
- }
- }
-
- /**
- * Converts a rule hash from Ingo's internal format to the database
- * format.
- *
- * @param array $rule Rule hash in Ingo's format.
- *
- * @return array Rule hash in DB's format.
- */
- protected function _ruleToBackend($rule)
- {
- return array(Horde_String::convertCharset($rule['name'], Horde_Nls::getCharset(), $this->_params['charset']),
- (int)$rule['action'],
- isset($rule['action-value']) ? Horde_String::convertCharset($rule['action-value'], Horde_Nls::getCharset(), $this->_params['charset']) : null,
- isset($rule['flags']) ? (int)$rule['flags'] : null,
- isset($rule['conditions']) ? serialize(Horde_String::convertCharset($rule['conditions'], Horde_Nls::getCharset(), $this->_params['charset'])) : null,
- isset($rule['combine']) ? (int)$rule['combine'] : null,
- isset($rule['stop']) ? (int)$rule['stop'] : null,
- isset($rule['disable']) ? (int)(!$rule['disable']) : 1);
- }
-
- /**
- * Adds a rule hash to the filters list.
- *
- * @param array $rule A rule hash.
- * @param boolean $default If true merge the rule hash with default rule
- * values.
- */
- public function addRule($rule, $default = true)
- {
- if ($default) {
- $rule = array_merge($this->getDefaultRule(), $rule);
- }
-
- $query = sprintf('INSERT INTO %s (rule_id, rule_owner, rule_name, rule_action, rule_value, rule_flags, rule_conditions, rule_combine, rule_stop, rule_active, rule_order) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
- $this->_params['table_rules']);
- $id = $this->_write_db->nextId($this->_params['table_rules']);
- if (is_a($id, 'PEAR_Error')) {
- return $id;
- }
- $order = key(array_reverse($this->_filters, true)) + 1;
- $values = array_merge(array($id, Ingo::getUser()),
- $this->_ruleToBackend($rule),
- array($order));
- Horde::logMessage('Ingo_Storage_filters_sql::addRule(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- $rule['id'] = $id;
- $this->_filters[$order] = $rule;
- }
-
- /**
- * Updates an existing rule with a rule hash.
- *
- * @param array $rule A rule hash
- * @param integer $id A rule number
- */
- public function updateRule($rule, $id)
- {
- $query = sprintf('UPDATE %s SET rule_name = ?, rule_action = ?, rule_value = ?, rule_flags = ?, rule_conditions = ?, rule_combine = ?, rule_stop = ?, rule_active = ?, rule_order = ? WHERE rule_id = ? AND rule_owner = ?',
- $this->_params['table_rules']);
- $values = array_merge($this->_ruleToBackend($rule),
- array($id, $rule['id'], Ingo::getUser()));
- Horde::logMessage('Ingo_Storage_filters_sql::updateRule(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- $this->_filters[$id] = $rule;
- }
-
- /**
- * Deletes a rule from the filters list.
- *
- * @param integer $id Number of the rule to delete.
- *
- * @return boolean True if the rule has been found and deleted.
- */
- public function deleteRule($id)
- {
- if (!isset($this->_filters[$id])) {
- return false;
- }
-
- $query = sprintf('DELETE FROM %s WHERE rule_id = ? AND rule_owner = ?',
- $this->_params['table_rules']);
- $values = array($this->_filters[$id]['id'], Ingo::getUser());
- Horde::logMessage('Ingo_Storage_filters_sql::deleteRule(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
- unset($this->_filters[$id]);
-
- $query = sprintf('UPDATE %s SET rule_order = rule_order - 1 WHERE rule_owner = ? AND rule_order > ?',
- $this->_params['table_rules']);
- $values = array(Ingo::getUser(), $id);
- Horde::logMessage('Ingo_Storage_filters_sql::deleteRule(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- return true;
- }
-
- /**
- * Creates a copy of an existing rule.
- *
- * The created copy is added to the filters list right after the original
- * rule.
- *
- * @param integer $id Number of the rule to copy.
- *
- * @return boolean True if the rule has been found and copied.
- */
- public function copyRule($id)
- {
- if (isset($this->_filters[$id])) {
- $newrule = $this->_filters[$id];
- $newrule['name'] = sprintf(_("Copy of %s"), $this->_filters[$id]['name']);
- $this->addRule($newrule, false);
- $this->ruleUp(count($this->_filters) - 1, count($this->_filters) - $id - 2);
- return true;
- }
-
- return false;
- }
-
- /**
- * Moves a rule up in the filters list.
- *
- * @param integer $id Number of the rule to move.
- * @param integer $steps Number of positions to move the rule up.
- */
- public function ruleUp($id, $steps = 1)
- {
- return $this->_ruleMove($id, -$steps);
- }
-
- /**
- * Moves a rule down in the filters list.
- *
- * @param integer $id Number of the rule to move.
- * @param integer $steps Number of positions to move the rule down.
- */
- public function ruleDown($id, $steps = 1)
- {
- return $this->_ruleMove($id, $steps);
- }
-
- /**
- * Moves a rule in the filters list.
- *
- * @param integer $id Number of the rule to move.
- * @param integer $steps Number of positions and direction to move the
- * rule.
- */
- protected function _ruleMove($id, $steps)
- {
- $query = sprintf('UPDATE %s SET rule_order = rule_order %s 1 WHERE rule_owner = ? AND rule_order %s ? AND rule_order %s ?',
- $this->_params['table_rules'],
- $steps > 0 ? '-' : '+',
- $steps > 0 ? '>' : '>=',
- $steps > 0 ? '<=' : '<');
- $values = array(Ingo::getUser());
- if ($steps < 0) {
- $values[] = (int)($id + $steps);
- $values[] = (int)$id;
- } else {
- $values[] = (int)$id;
- $values[] = (int)($id + $steps);
- }
- Horde::logMessage('Ingo_Storage_filters_sql::ruleUp(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
- $query = sprintf('UPDATE %s SET rule_order = ? WHERE rule_owner = ? AND rule_id = ?',
- $this->_params['table_rules']);
- $values = array((int)($id + $steps),
- Ingo::getUser(),
- $this->_filters[$id]['id']);
- Horde::logMessage('Ingo_Storage_filters_sql::ruleUp(): ' . $query,
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- $this->init();
- }
-
- /**
- * Disables a rule.
- *
- * @param integer $id Number of the rule to disable.
- */
- public function ruleDisable($id)
- {
- $rule = $this->_filters[$id];
- $rule['disable'] = true;
- $this->updateRule($rule, $id);
- }
-
- /**
- * Enables a rule.
- *
- * @param integer $id Number of the rule to enable.
- */
- public function ruleEnable($id)
- {
- $rule = $this->_filters[$id];
- $rule['disable'] = false;
- $this->updateRule($rule, $id);
- }
-
-}