* Ingo works purely on a preferred mechanism for server selection. There are
* a number of properties that you can set for each backend:
*
- * driver: (string) The Ingo_Driver:: driver to use to store the script on
- * the backend server. Valid options:
- * 'ldap' - LDAP server
- * 'null' - No backend server
- * 'timsieved' - Cyrus timsieved server
- * 'vfs' - Use Horde VFS
+ * transport: (string) The Ingo_Transport driver to use to store the script on
+ * the backend server. Valid options:
+ * - 'ldap': LDAP server
+ * - 'null': No backend server
+ * - 'timsieved': Timsieved (managesieve) server
+ * - 'vfs': Use Horde VFS
*
* preferred: (string) This is the field that is used to choose which server
* is used. The value for this field may be a single string or an
* server.
*
* hordeauth: (mixed) One of the following:
- * true - Ingo will attempt to use the user's existing credentials
- * (the username/password they used to log in to Horde) to
- * login to this source. (DEFAULT)
- * 'full' - The username will be used unmodified.
+ * - true: Ingo will attempt to use the user's existing
+ * credentials (the username/password they used to log in
+ * to Horde) to login to this source. (DEFAULT)
+ * - 'full': The username will be used unmodified.
*
* params: (array) An array containing any additional information that the
- * Ingo_Driver:: class needs.
+ * Ingo_Transport class needs.
*
* script: (string) The type of Ingo_Script driver this server uses.
* Valid options:
- * 'imap' - IMAP client side filtering (POP3 servers NOT
- supported)
- * 'maildrop' - Maildrop scripts
- * 'procmail' - Procmail scripts
- * 'sieve' - Sieve scripts
+ * - 'imap': IMAP client side filtering (POP3 servers NOT
+ * supported)
+ * - 'maildrop': Maildrop scripts
+ * - 'procmail': Procmail scripts
+ * - 'sieve': Sieve scripts
*
* scriptparams: (array) An array containing any additional information that
- * the Ingo_Script:: driver needs.
+ * the Ingo_Script driver needs.
*
* shares: (boolean) Some drivers support sharing filter rules with other
* users. Users can then configure filters for each other if they
/* IMAP Example */
$backends['imap'] = array(
- 'driver' => 'null',
+ 'transport' => 'null',
'preferred' => 'example.com',
'hordeauth' => true,
'params' => array(),
/* Maildrop Example */
$backends['maildrop'] = array(
- 'driver' => 'vfs',
+ 'transport' => 'vfs',
'preferred' => 'example.com',
'hordeauth' => true,
'params' => array(
/* Procmail Example */
$backends['procmail'] = array(
- 'driver' => 'vfs',
+ 'transport' => 'vfs',
'preferred' => 'example.com',
'hordeauth' => true,
'params' => array(
/* Sieve Example */
$backends['sieve'] = array(
- 'driver' => 'timsieved',
+ 'transport' => 'timsieved',
'preferred' => 'example.com',
'hordeauth' => true,
'params' => array(
/* sivtest Example */
$backends['sivtest'] = array(
- 'driver' => 'sivtest',
+ 'transport' => 'sivtest',
'preferred' => 'example.com',
'hordeauth' => true,
'params' => array(
/* Sun ONE/JES Example (LDAP/Sieve) */
$backends['ldapsieve'] = array(
- 'driver' => 'ldap',
+ 'transport' => 'ldap',
'preferred' => 'example.com',
'hordeauth' => false,
'params' => array(
}
$backends['kolab'] = array(
- 'driver' => 'timsieved',
+ 'transport' => 'timsieved',
'preferred' => '',
'hordeauth' => 'full',
'params' => array(
v2.0-git
--------
+[jan] Rename 'driver' configuration and classes to 'transport' to avoid
+ confusion with script and storage drivers.
[mms] Use IMP API for IMAP actions.
-[mms] Convert to Horde 5 standards.
+[mms] Convert to Horde 4 standards.
----------
Ingo::createSession();
// Create shares if necessary.
- $driver = Ingo::getDriver();
- if ($driver->supportShares()) {
+ $transport = Ingo::getTransport();
+ if ($transport->supportShares()) {
$GLOBALS['ingo_shares'] = $GLOBALS['injector']->getInstance('Horde_Share')->getScope();
$GLOBALS['all_rulesets'] = Ingo::listRulesets();
+++ /dev/null
-<?php
-/**
- * Ingo_Driver:: defines an API to activate filter scripts on a 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 Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Ingo_Driver
-{
- /**
- * Driver specific parameters
- *
- * @var array
- */
- protected $_params = array(
- 'username' => null,
- 'password' => null
- );
-
- /**
- * Whether this driver allows managing other users' rules.
- *
- * @var boolean
- */
- protected $_support_shares = false;
-
- /**
- * Attempts to return a concrete instance based on $driver.
- *
- * @param string $driver The type of concrete subclass to return.
- * @param array $params A hash containing any additional configuration
- * or connection parameters a subclass might need.
- *
- * @return Ingo_Driver The newly created concrete instance.
- * @throws Ingo_Exception
- */
- static public function factory($driver, $params = array())
- {
- $class = __CLASS__ . '_' . ucfirst(basename($driver));
-
- if (class_exists($class)) {
- return new $class($params);
- }
-
- throw new Ingo_Exception('Could not load driver.');
- }
-
- /**
- * Constructor.
- */
- public function __construct($params = array())
- {
- $this->_params = array_merge($this->_params, $params);
- }
-
- /**
- * Sets a script running on the backend.
- *
- * @param string $script The filter script.
- *
- * @return boolean True on success, false if script can't be activated.
- * @throws Ingo_Exception
- */
- public function setScriptActive($script)
- {
- return false;
- }
-
- /**
- * Returns whether the driver supports managing other users' rules.
- *
- * @return boolean True if the driver supports shares.
- */
- public function supportShares()
- {
- return ($this->_support_shares &&
- !empty($_SESSION['ingo']['backend']['shares']));
- }
-
-}
+++ /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.
- *
- * @throws Ingo_Exception
- */
- public function __construct($params = array())
- {
- if (!Horde_Util::extensionExists('ldap')) {
- throw new Ingo_Exception(_("LDAP support is required but the LDAP module is not available or not loaded."));
- }
-
- $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.
- *
- * @throws Ingo_Exception
- */
- protected function _connect()
- {
- if (!($ldapcn = @ldap_connect($this->_params['hostspec'],
- $this->_params['port']))) {
- throw new Ingo_Exception(_("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']) &&
- !@ldap_start_tls($ldapcn)) {
- throw new Ingo_Exception(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']);
-
- $password = isset($this->_params['bind_password'])
- ? $this->_params['bind_password']
- : $this->_params['password'];
-
- $bind_success = @ldap_bind($ldapcn, $bind_dn, $password);
- } else {
- $bind_success = @ldap_bind($ldapcn);
- }
-
- if ($bind_success) {
- return $ldapcn;
- }
-
-
- throw new Ingo_Exception(sprintf(_("Bind failed: (%s) %s"),
- ldap_errno($ldapcn),
- ldap_error($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 array Script sources list.
- * @throws Ingo_Exception
- */
- 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) {
- throw new Ingo_Exception(sprintf(_("Error retrieving current script: (%d) %s"),
- ldap_errno($ldapcn),
- ldap_error($ldapcn)));
- }
-
- if (@ldap_count_entries($ldapcn, $sr) != 1) {
- throw new Ingo_Exception(sprintf(_("Expected 1 object, got %d."),
- ldap_count_entries($ldapcn, $sr)));
- }
-
- $ent = @ldap_first_entry($ldapcn, $sr);
- if ($ent === false) {
- throw new Ingo_Exception(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);
- throw new Ingo_Exception(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) {
- throw new Ingo_Exception(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.
- *
- * @throws Ingo_Exception
- */
- protected function setScriptActive($script)
- {
- $ldapcn = $this->_connect();
- $values = $this->_getScripts($ldapcn, $userDN);
-
- $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);
- $r = empty($values)
- ? @ldap_mod_del($ldapcn, $userDN, $replace)
- : @ldap_mod_replace($ldapcn, $userDN, $replace);
-
- if (!$r) {
- throw new Ingo_Exception(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.
- *
- * @throws Ingo_Exception
- */
- public function getScript()
- {
- $ldapcn = $this->_connect();
- $values = $this->_getScripts($ldapcn, $userDN);
-
- $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-2010 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.
- *
- * @throws Ingo_Exception;
- */
- protected function _connect()
- {
- if (!empty($this->_sieve)) {
- return;
- }
-
- $this->sivtestSocket($this->_params['username'],
- $this->_params['password'], $this->_params['hostspec']);
- $domain_socket = 'unix://' . $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 ($res instanceof PEAR_Error) {
- unset($this->_sieve);
- throw new Ingo_Exception($res);
- }
- }
-
- /**
- * Sets a script running on the backend.
- *
- * @param string $script The sieve script.
- *
- * @throws Ingo_Exception
- */
- public function setScriptActive($script)
- {
- $this->_connect();
-
- $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
- if ($res instanceof PEAR_Error) {
- throw new Ingo_Exception($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.
- * @throws Ingo Exception
- */
- public function getScript()
- {
- $this->_connect();
- 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
- * @throws Ingo_Exception
- */
- public function sivtestSocket($username, $password, $hostspec)
- {
- $command = '';
- $error_return = '';
-
- if (strtolower($this->_params['logintype']) == 'gssapi'
- && isset($_SERVER['KRB5CCNAME'])) {
- $command .= 'KRB5CCNAME=' . $_SERVER['KRB5CCNAME'];
- }
-
- $domain_socket = 'unix://' . $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 (!($error instanceof PEAR_Error)) {
- break;
- }
-
- // We failed, break this connection.
- unlink($this->_params['socket']);
- }
-
- if (!empty($error_return)) {
- throw new Ingo_Exception($error_return);
- }
-
- $status = $socket->getStatus();
- if ($status instanceof PEAR_Error || $status['eof']) {
- throw new Ingo_Exception(_("Failed to write to socket: (connection lost!)"));
- }
-
- $error = $socket->writeLine("CAPABILITY");
- if ($error instanceof PEAR_Error) {
- throw new Ingo_Exception(_("Failed to write to socket: " . $error->getMessage()));
- }
-
- $result = $socket->readLine();
- if ($result instanceof PEAR_Error) {
- throw new Ingo_Exception(_("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-2010 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));
- }
-
- /**
- * Connects to the sieve server.
- *
- * @throws Ingo_Exception
- */
- protected function _connect()
- {
- if (!empty($this->_sieve)) {
- return;
- }
-
- $auth = empty($this->_params['admin'])
- ? $this->_params['username']
- : $this->_params['admin'];
-
- $this->_sieve = new Net_Sieve($auth,
- $this->_params['password'],
- $this->_params['hostspec'],
- $this->_params['port'],
- $this->_params['logintype'],
- Ingo::getUser(false),
- $this->_params['debug'],
- false,
- $this->_params['usetls'],
- null,
- array($this, '_debug'));
-
- $res = $this->_sieve->getError();
- if ($res instanceof PEAR_Error) {
- unset($this->_sieve);
- throw new Ingo_Exception($res);
- }
-
- /* BC for older Net_Sieve versions that don't allow specify the debug
- * handler in the constructor. */
- if (!empty($this->_params['debug'])) {
- $this->_sieve->setDebug(true, array($this, '_debug'));
- }
- }
-
- /**
- * Routes the Sieve protocol log to the Horde log.
- *
- * @param Net_Sieve $sieve A Net_Sieve object.
- * @param string $message The tracked Sieve communication.
- */
- protected function _debug($sieve, $message)
- {
- Horde::logMessage($message, 'DEBUG');
- }
-
- /**
- * Sets a script running on the backend.
- *
- * @param string $script The sieve script.
- *
- * @return mixed True on success.
- * @throws Ingo_Exception
- */
- public function setScriptActive($script)
- {
- $res = $this->_connect();
-
- if (!strlen($script)) {
- return $this->_sieve->setActive('');
- }
-
- $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
- if ($res instanceof PEAR_Error) {
- throw new Ingo_Exception($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.
- * @throws Ingo_Exception
- */
- public function getScript()
- {
- $res = $this->_connect();
- $active = $this->_sieve->getActive();
-
- return empty($active)
- ? ''
- : $this->_sieve->getScript($active);
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Ingo_Driver_Vfs:: implements an Ingo storage driver using Horde VFS.
- *
- * Copyright 2003-2010 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.
- * @throws Ingo_Exception
- */
- public function setScriptActive($script)
- {
- $this->_connect();
-
- try {
- if (empty($script)) {
- $this->_vfs->deleteFile($this->_params['vfs_path'], $this->_params['filename']);
- } else {
- $this->_vfs->writeData($this->_params['vfs_path'], $this->_params['filename'], $script, true);
- }
- } catch (VFS_Exception $e) {
- throw new Ingo_Exception($e);
- }
-
- if (isset($this->_params['file_perms']) && !empty($script)) {
- try {
- $this->_vfs->changePermissions($this->_params['vfs_path'], $this->_params['filename'], $this->_params['file_perms']);
- } catch (VFS_Exception $e) {
- throw new Ingo_Exception($e);
- }
- }
-
- // 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'])) {
- try {
- if (empty($script)) {
- $this->_vfs->deleteFile($this->_params['vfs_forward_path'], $backend['params']['forward_file']);
- } else {
- $this->_vfs->writeData($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $backend['params']['forward_string'], true);
- }
- } catch (VFS_Exception $e) {
- throw new Ingo_Exception($e);
- }
-
- if (isset($this->_params['file_perms']) && !empty($script)) {
- try {
- $this->_vfs->changePermissions($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $this->_params['file_perms']);
- } catch (VFS_Exception $e) {
- throw new Ingo_Exception($e);
- }
- }
- }
-
- return true;
- }
-
- /**
- * Returns the content of the currently active script.
- *
- * @return string The complete ruleset of the specified user.
- * @throws Ingo_Exception
- */
- public function getScript()
- {
- $this->_connect();
- return $this->_vfs->read($this->_params['vfs_path'], $this->_params['filename']);
- }
-
- /**
- * Connect to the VFS server.
- *
- * @throws Ingo_Exception
- */
- 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;
- }
-
- try {
- $this->_vfs = VFS::singleton($this->_params['vfstype'], $this->_params);
- } catch (VFS_Exception $e) {
- $error = new Ingo_Exception($this->_vfs);
- unset($this->_vfs);
- throw $error;
- }
- }
-
-}
*/
static public function activateScript($script, $deactivate = false)
{
- $driver = self::getDriver();
+ $transport = self::getTransport();
try {
- $res = $driver->setScriptActive($script);
+ $res = $transport->setScriptActive($script);
} catch (Ingo_Exception $e) {
$msg = ($deactivate)
? _("There was an error deactivating the script.")
*/
static public function getScript()
{
- return self::getDriver()->getScript();
+ return self::getTransport()->getScript();
}
/**
if (empty($backend['script'])) {
throw new Ingo_Exception(sprintf(_("No \"%s\" element found in backend configuration."), 'script'));
- } elseif (empty($backend['driver'])) {
- throw new Ingo_Exception(sprintf(_("No \"%s\" element found in backend configuration."), 'driver'));
+ } elseif (empty($backend['transport'])) {
+ throw new Ingo_Exception(sprintf(_("No \"%s\" element found in backend configuration."), 'transport'));
}
/* Make sure the 'params' entry exists. */
}
/**
- * Returns an instance of the configured driver.
+ * Returns an instance of the configured transport driver.
*
- * @return Ingo_Driver The configured driver.
+ * @return Ingo_Transport The configured driver.
* @throws Ingo_Exception
*/
- static public function getDriver()
+ static public function getTransport()
{
$params = $_SESSION['ingo']['backend']['params'];
$params['password'] = $GLOBALS['registry']->getAuthCredential('password');
}
- return Ingo_Driver::factory($_SESSION['ingo']['backend']['driver'], $params);
+ return Ingo_Transport::factory($_SESSION['ingo']['backend']['transport'], $params);
}
/**
// Writing vacation.msg file
$reason = Horde_Mime::encode($params['action-value']['reason'], $scriptparams['charset']);
- $driver = Ingo::getDriver();
- $driver->_connect();
- $result = $driver->_vfs->writeData($driver->_params['vfs_path'], 'vacation.msg', $reason, true);
+ $transport = Ingo::getTransport();
+ $transport->_connect();
+ $result = $transport->_vfs->writeData($transport->_params['vfs_path'], 'vacation.msg', $reason, true);
// Rule : Do not send responses to bulk or list messages
if ($params['action-value']['ignorelist'] == 1) {
--- /dev/null
+<?php
+/**
+ * Ingo_Transport defines an API to activate filter scripts on a 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 Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Transport
+{
+ /**
+ * Driver specific parameters
+ *
+ * @var array
+ */
+ protected $_params = array(
+ 'username' => null,
+ 'password' => null
+ );
+
+ /**
+ * Whether this driver allows managing other users' rules.
+ *
+ * @var boolean
+ */
+ protected $_support_shares = false;
+
+ /**
+ * Attempts to return a concrete instance based on $driver.
+ *
+ * @param string $driver The type of concrete subclass to return.
+ * @param array $params A hash containing any additional configuration
+ * or connection parameters a subclass might need.
+ *
+ * @return Ingo_Transport The newly created concrete instance.
+ * @throws Ingo_Exception
+ */
+ static public function factory($driver, $params = array())
+ {
+ $class = __CLASS__ . '_' . ucfirst(basename($driver));
+
+ if (class_exists($class)) {
+ return new $class($params);
+ }
+
+ throw new Ingo_Exception('Could not load driver.');
+ }
+
+ /**
+ * Constructor.
+ */
+ public function __construct($params = array())
+ {
+ $this->_params = array_merge($this->_params, $params);
+ }
+
+ /**
+ * Sets a script running on the backend.
+ *
+ * @param string $script The filter script.
+ *
+ * @return boolean True on success, false if script can't be activated.
+ * @throws Ingo_Exception
+ */
+ public function setScriptActive($script)
+ {
+ return false;
+ }
+
+ /**
+ * Returns whether the driver supports managing other users' rules.
+ *
+ * @return boolean True if the driver supports shares.
+ */
+ public function supportShares()
+ {
+ return ($this->_support_shares &&
+ !empty($_SESSION['ingo']['backend']['shares']));
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Transport_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_Transport_Ldap extends Ingo_Transport
+{
+ /**
+ * Constructor.
+ *
+ * @throws Ingo_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('ldap')) {
+ throw new Ingo_Exception(_("LDAP support is required but the LDAP module is not available or not loaded."));
+ }
+
+ $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.
+ *
+ * @throws Ingo_Exception
+ */
+ protected function _connect()
+ {
+ if (!($ldapcn = @ldap_connect($this->_params['hostspec'],
+ $this->_params['port']))) {
+ throw new Ingo_Exception(_("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']) &&
+ !@ldap_start_tls($ldapcn)) {
+ throw new Ingo_Exception(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']);
+
+ $password = isset($this->_params['bind_password'])
+ ? $this->_params['bind_password']
+ : $this->_params['password'];
+
+ $bind_success = @ldap_bind($ldapcn, $bind_dn, $password);
+ } else {
+ $bind_success = @ldap_bind($ldapcn);
+ }
+
+ if ($bind_success) {
+ return $ldapcn;
+ }
+
+
+ throw new Ingo_Exception(sprintf(_("Bind failed: (%s) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($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 array Script sources list.
+ * @throws Ingo_Exception
+ */
+ 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) {
+ throw new Ingo_Exception(sprintf(_("Error retrieving current script: (%d) %s"),
+ ldap_errno($ldapcn),
+ ldap_error($ldapcn)));
+ }
+
+ if (@ldap_count_entries($ldapcn, $sr) != 1) {
+ throw new Ingo_Exception(sprintf(_("Expected 1 object, got %d."),
+ ldap_count_entries($ldapcn, $sr)));
+ }
+
+ $ent = @ldap_first_entry($ldapcn, $sr);
+ if ($ent === false) {
+ throw new Ingo_Exception(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);
+ throw new Ingo_Exception(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) {
+ throw new Ingo_Exception(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.
+ *
+ * @throws Ingo_Exception
+ */
+ protected function setScriptActive($script)
+ {
+ $ldapcn = $this->_connect();
+ $values = $this->_getScripts($ldapcn, $userDN);
+
+ $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);
+ $r = empty($values)
+ ? @ldap_mod_del($ldapcn, $userDN, $replace)
+ : @ldap_mod_replace($ldapcn, $userDN, $replace);
+
+ if (!$r) {
+ throw new Ingo_Exception(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.
+ *
+ * @throws Ingo_Exception
+ */
+ public function getScript()
+ {
+ $ldapcn = $this->_connect();
+ $values = $this->_getScripts($ldapcn, $userDN);
+
+ $script = '';
+ foreach ($values as $value) {
+ if (strpos($value, "# Sieve Filter\n") !== false) {
+ $script = $value;
+ break;
+ }
+ }
+
+ @ldap_close($ldapcn);
+ return $script;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Transport_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_Transport_Null extends Ingo_Transport
+{
+ /**
+ * Constructor.
+ */
+ public function __construct($params = array())
+ {
+ $this->_support_shares = true;
+ parent::__construct($params);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Transport_Sivtest implements the Sieve_Driver api to allow scripts to
+ * be installed and set active via the Cyrus sivtest command line utility.
+ *
+ * Copyright 2003-2010 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_Transport_Sivtest extends Ingo_Transport
+{
+ /**
+ * 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.
+ *
+ * @throws Ingo_Exception;
+ */
+ protected function _connect()
+ {
+ if (!empty($this->_sieve)) {
+ return;
+ }
+
+ $this->sivtestSocket($this->_params['username'],
+ $this->_params['password'], $this->_params['hostspec']);
+ $domain_socket = 'unix://' . $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 ($res instanceof PEAR_Error) {
+ unset($this->_sieve);
+ throw new Ingo_Exception($res);
+ }
+ }
+
+ /**
+ * Sets a script running on the backend.
+ *
+ * @param string $script The sieve script.
+ *
+ * @throws Ingo_Exception
+ */
+ public function setScriptActive($script)
+ {
+ $this->_connect();
+
+ $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
+ if ($res instanceof PEAR_Error) {
+ throw new Ingo_Exception($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.
+ * @throws Ingo Exception
+ */
+ public function getScript()
+ {
+ $this->_connect();
+ 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
+ * @throws Ingo_Exception
+ */
+ public function sivtestSocket($username, $password, $hostspec)
+ {
+ $command = '';
+ $error_return = '';
+
+ if (strtolower($this->_params['logintype']) == 'gssapi'
+ && isset($_SERVER['KRB5CCNAME'])) {
+ $command .= 'KRB5CCNAME=' . $_SERVER['KRB5CCNAME'];
+ }
+
+ $domain_socket = 'unix://' . $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 (!($error instanceof PEAR_Error)) {
+ break;
+ }
+
+ // We failed, break this connection.
+ unlink($this->_params['socket']);
+ }
+
+ if (!empty($error_return)) {
+ throw new Ingo_Exception($error_return);
+ }
+
+ $status = $socket->getStatus();
+ if ($status instanceof PEAR_Error || $status['eof']) {
+ throw new Ingo_Exception(_("Failed to write to socket: (connection lost!)"));
+ }
+
+ $error = $socket->writeLine("CAPABILITY");
+ if ($error instanceof PEAR_Error) {
+ throw new Ingo_Exception(_("Failed to write to socket: " . $error->getMessage()));
+ }
+
+ $result = $socket->readLine();
+ if ($result instanceof PEAR_Error) {
+ throw new Ingo_Exception(_("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_Transport_Timsieved implements the Sieve_Driver api to allow scripts
+ * to be installed and set active via a Cyrus timsieved server.
+ *
+ * Copyright 2003-2010 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_Transport_Timsieved extends Ingo_Transport
+{
+ /**
+ * 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));
+ }
+
+ /**
+ * Connects to the sieve server.
+ *
+ * @throws Ingo_Exception
+ */
+ protected function _connect()
+ {
+ if (!empty($this->_sieve)) {
+ return;
+ }
+
+ $auth = empty($this->_params['admin'])
+ ? $this->_params['username']
+ : $this->_params['admin'];
+
+ $this->_sieve = new Net_Sieve($auth,
+ $this->_params['password'],
+ $this->_params['hostspec'],
+ $this->_params['port'],
+ $this->_params['logintype'],
+ Ingo::getUser(false),
+ $this->_params['debug'],
+ false,
+ $this->_params['usetls'],
+ null,
+ array($this, '_debug'));
+
+ $res = $this->_sieve->getError();
+ if ($res instanceof PEAR_Error) {
+ unset($this->_sieve);
+ throw new Ingo_Exception($res);
+ }
+
+ /* BC for older Net_Sieve versions that don't allow specify the debug
+ * handler in the constructor. */
+ if (!empty($this->_params['debug'])) {
+ $this->_sieve->setDebug(true, array($this, '_debug'));
+ }
+ }
+
+ /**
+ * Routes the Sieve protocol log to the Horde log.
+ *
+ * @param Net_Sieve $sieve A Net_Sieve object.
+ * @param string $message The tracked Sieve communication.
+ */
+ protected function _debug($sieve, $message)
+ {
+ Horde::logMessage($message, 'DEBUG');
+ }
+
+ /**
+ * Sets a script running on the backend.
+ *
+ * @param string $script The sieve script.
+ *
+ * @return mixed True on success.
+ * @throws Ingo_Exception
+ */
+ public function setScriptActive($script)
+ {
+ $res = $this->_connect();
+
+ if (!strlen($script)) {
+ return $this->_sieve->setActive('');
+ }
+
+ $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
+ if ($res instanceof PEAR_Error) {
+ throw new Ingo_Exception($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.
+ * @throws Ingo_Exception
+ */
+ public function getScript()
+ {
+ $res = $this->_connect();
+ $active = $this->_sieve->getActive();
+
+ return empty($active)
+ ? ''
+ : $this->_sieve->getScript($active);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Ingo_Transport_Vfs implements an Ingo storage driver using Horde VFS.
+ *
+ * Copyright 2003-2010 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_Transport_Vfs extends Ingo_Transport
+{
+ /**
+ * 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.
+ * @throws Ingo_Exception
+ */
+ public function setScriptActive($script)
+ {
+ $this->_connect();
+
+ try {
+ if (empty($script)) {
+ $this->_vfs->deleteFile($this->_params['vfs_path'], $this->_params['filename']);
+ } else {
+ $this->_vfs->writeData($this->_params['vfs_path'], $this->_params['filename'], $script, true);
+ }
+ } catch (VFS_Exception $e) {
+ throw new Ingo_Exception($e);
+ }
+
+ if (isset($this->_params['file_perms']) && !empty($script)) {
+ try {
+ $this->_vfs->changePermissions($this->_params['vfs_path'], $this->_params['filename'], $this->_params['file_perms']);
+ } catch (VFS_Exception $e) {
+ throw new Ingo_Exception($e);
+ }
+ }
+
+ // 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'])) {
+ try {
+ if (empty($script)) {
+ $this->_vfs->deleteFile($this->_params['vfs_forward_path'], $backend['params']['forward_file']);
+ } else {
+ $this->_vfs->writeData($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $backend['params']['forward_string'], true);
+ }
+ } catch (VFS_Exception $e) {
+ throw new Ingo_Exception($e);
+ }
+
+ if (isset($this->_params['file_perms']) && !empty($script)) {
+ try {
+ $this->_vfs->changePermissions($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $this->_params['file_perms']);
+ } catch (VFS_Exception $e) {
+ throw new Ingo_Exception($e);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the content of the currently active script.
+ *
+ * @return string The complete ruleset of the specified user.
+ * @throws Ingo_Exception
+ */
+ public function getScript()
+ {
+ $this->_connect();
+ return $this->_vfs->read($this->_params['vfs_path'], $this->_params['filename']);
+ }
+
+ /**
+ * Connect to the VFS server.
+ *
+ * @throws Ingo_Exception
+ */
+ 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;
+ }
+
+ try {
+ $this->_vfs = VFS::singleton($this->_params['vfstype'], $this->_params);
+ } catch (VFS_Exception $e) {
+ $error = new Ingo_Exception($this->_vfs);
+ unset($this->_vfs);
+ throw $error;
+ }
+ }
+
+}