--- /dev/null
+<?php
+/**
+ * The Horde_Token:: class provides a common abstracted interface into the
+ * various token generation mediums. It also includes all of the
+ * functions for retrieving, storing, and checking tokens.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Max Kalika <max@horde.org>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Horde_Token
+ */
+class Horde_Token
+{
+ /**
+ * Singleton instances.
+ *
+ * @var array
+ */
+ static protected $_instances = array();
+
+ /**
+ * Hash of parameters necessary to use the chosen backend.
+ *
+ * @var array
+ */
+ protected $_params = array();
+
+ /**
+ * Attempts to return a concrete Horde_Token instance based on $driver.
+ *
+ * @param mixed $driver The type of concrete Horde_Token subclass to
+ * return. If $driver is an array, then we will look
+ * in $driver[0]/lib/Token/ for the subclass
+ * implementation named $driver[1].php.
+ * @param array $params A hash containing any additional configuration or
+ * connection parameters a subclass might need.
+ *
+ * @return Horde_Token The newly created concrete Horde_Token instance, or
+ * false an error.
+ */
+ static public function getInstance($driver, $params = array())
+ {
+ if (is_array($driver)) {
+ list($app, $driver) = $driver;
+ }
+
+ $driver = basename($driver);
+ $class = ($driver == 'none')
+ ? 'Horde_Token';
+ : 'Horde_Token_' . ucfirst($driver);
+
+ if (!class_exists($class)) {
+ /* If driver class doesn't exist or the driver is not
+ * available just default to the parent class, and it is
+ * not necessary to warn about degraded service. */
+ $class = 'Horde_Token';
+ }
+
+ return new $class($params);
+ }
+
+ /**
+ * Attempts to return a reference to a concrete Horde_Token instance based
+ * on $driver.
+ *
+ * It will only create a new instance if no Horde_Token instance with the
+ * same parameters currently exists.
+ *
+ * This should be used if multiple types of token generators (and, thus,
+ * multiple Horde_Token instances) are required.
+ *
+ * This method must be invoked as:
+ * <code>$var = Horde_Token::singleton();</code>
+ *
+ * @param mixed $driver The type of concrete Horde_Token subclass to
+ * return. If $driver is an array, then we will look
+ * in $driver[0]/lib/Token/ for the subclass
+ * implementation named $driver[1].php.
+ * @param array $params A hash containing any additional configuration or
+ * connection parameters a subclass might need.
+ *
+ * @return Horde_Token The concrete Horde_Token reference, or false on
+ * error.
+ */
+ static public function singleton($driver, $params = array())
+ {
+ ksort($params);
+ $sig = hash('md5', serialize(array($driver, $params)));
+
+ if (!isset(self::$_instances[$sig])) {
+ self::$_instances[$sig] = Horde_Token::getInstance($driver, $params);
+ }
+
+ return self::$_instances[$sig];
+ }
+
+ /**
+ * Constructor.
+ */
+ protected function __construct($params)
+ {
+ $this->_params = $params;
+ }
+
+ /**
+ * TODO
+ */
+ public function encodeRemoteAddress()
+ {
+ return isset($_SERVER['REMOTE_ADDR'])
+ ? base64_encode($_SERVER['REMOTE_ADDR'])
+ : '';
+ }
+
+ /**
+ * Generates a connection id and returns it.
+ *
+ * @param string $seed A unique ID to be included in the token.
+ *
+ * @return string The generated id string.
+ */
+ public function generateId($seed = '')
+ {
+ require_once 'Horde/Util.php';
+ return Util::uriB64Encode(pack('H*', sha1(uniqid(mt_rand(), true) . $seed . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''))));
+ }
+
+ /**
+ * Checks if the given token has been previously used. First
+ * purges all expired tokens. Then retrieves current tokens for
+ * the given ip address. If the specified token was not found,
+ * adds it.
+ *
+ * @param string $token The value of the token to check.
+ *
+ * @return boolean True if the token has not been used, false otherwise.
+ */
+ public function verify($token)
+ {
+ $this->purge();
+ $exists = $this->exists($token);
+ if (is_a($exists, 'PEAR_Error')) {
+ return $exists;
+ } elseif ($exists) {
+ return false;
+ } else {
+ return $this->add($token);
+ }
+ }
+
+ /**
+ * This is an abstract method that should be overridden by a
+ * subclass implementation. The base implementation allows all
+ * token values.
+ */
+ public function exists()
+ {
+ return false;
+ }
+
+ /**
+ * This is an abstract method that should be overridden by a
+ * subclass implementation. The base implementation allows all
+ * token values.
+ */
+ public function add()
+ {
+ return true;
+ }
+
+ /**
+ * This is an abstract method that should be overridden by a
+ * subclass implementation. The base implementation allows all
+ * token values.
+ */
+ public function purge()
+ {
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Token tracking implementation for local files.
+ *
+ * Optional parameters:<pre>
+ * 'token_dir' The directory where to keep token files.
+ * 'timeout' The period (in seconds) after which an id is purged.
+ * Defaults to 86400 (i.e. 24 hours).</pre>
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Max Kalika <max@horde.org>
+ * @package Horde_Token
+ */
+class Horde_Token_file extends Horde_Token
+{
+ /**
+ * Handle for the open file descriptor.
+ *
+ * @var resource
+ */
+ protected $_fd = false;
+
+ /**
+ * Boolean indicating whether or not we have an open file descriptor.
+ *
+ * @var boolean
+ */
+ protected $_connected = false;
+
+ /**
+ * Create a new file based token-tracking container.
+ *
+ * @param array $params A hash containing storage parameters.
+ */
+ protected function __construct($params = array())
+ {
+ parent::__construct($params);
+
+ /* Choose the directory to save the stub files. */
+ if (!isset($this->_params['token_dir'])) {
+ $this->_params['token_dir'] = Util::getTempDir();
+ }
+
+ /* Set timeout to 24 hours if not specified. */
+ if (!isset($this->_params['timeout'])) {
+ $this->_params['timeout'] = 86400;
+ }
+ }
+
+ /**
+ * Destructor.
+ */
+ protected function __destruct()
+ {
+ $this->_disconnect();
+ }
+
+ /**
+ * Deletes all expired connection id's from the SQL server.
+ *
+ * @return boolean True on success, a PEAR_Error object on failure.
+ */
+ public function purge()
+ {
+ // Make sure we have no open file descriptors before unlinking
+ // files.
+ if (!$this->_disconnect()) {
+ return PEAR::raiseError('Unable to close file descriptors');
+ }
+
+ /* Build stub file list. */
+ if (!($dir = opendir($this->_params['token_dir']))) {
+ return PEAR::raiseError('Unable to open token directory');
+ }
+
+ /* Find expired stub files */
+ while (($dirEntry = readdir($dir)) != '') {
+ if (preg_match('|^conn_\w{8}$|', $dirEntry) && (time() - filemtime($this->_params['token_dir'] . '/' . $dirEntry) >= $this->_params['timeout'])) {
+ if (!@unlink($this->_params['token_dir'] . '/' . $dirEntry)) {
+ return PEAR::raiseError('Unable to purge token file.');
+ }
+ }
+ }
+
+ closedir($dir);
+ return true;
+ }
+
+ /**
+ * TODO
+ */
+ public function exists($tokenID)
+ {
+ if (is_a(($result = $this->_connect($tokenID)), 'PEAR_Error')) {
+ return $result;
+ }
+
+ /* Find already used IDs. */
+ $fileContents = file($this->_params['token_dir'] . '/conn_' . $this->encodeRemoteAddress());
+ if ($fileContents) {
+ $iMax = count($fileContents);
+ for ($i = 0; $i < $iMax; $i++) {
+ if (chop($fileContents[$i]) == $tokenID) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * TODO
+ */
+ public function add($tokenID)
+ {
+ if (is_a(($result = $this->_connect($tokenID)), 'PEAR_Error')) {
+ return $result;
+ }
+
+ /* Write the entry. */
+ fwrite($this->_fd, "$tokenID\n");
+
+ /* Return an error if the update fails, too. */
+ if (!$this->_disconnect()) {
+ return PEAR::raiseError('Failed to close token file cleanly.');
+ }
+
+ return true;
+ }
+
+ /**
+ * Opens a file descriptor to a new or existing file.
+ *
+ * @return boolean True on success, a PEAR_Error object on failure.
+ */
+ protected function _connect($tokenID)
+ {
+ if (!$this->_connected) {
+
+ // Open a file descriptor to the token stub file.
+ $this->_fd = @fopen($this->_params['token_dir'] . '/conn_' . $this->encodeRemoteAddress(), 'a');
+ if (!$this->_fd) {
+ return PEAR::raiseError('Failed to open token file.');
+ }
+
+ $this->_connected = true;
+ }
+
+ return true;
+ }
+
+ /**
+ * Closes the file descriptor.
+ *
+ * @return boolean True on success, false on failure.
+ */
+ protected function _disconnect()
+ {
+ if ($this->_connected) {
+ $this->_connected = false;
+ return fclose($this->_fd);
+ }
+
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Token tracking implementation for PHP's PEAR database abstraction layer.
+ *
+ * Required parameters:<pre>
+ * 'phptype' The database type (ie. 'pgsql', 'mysql', etc.).</pre>
+ *
+ * Required by some database implementations:<pre>
+ * 'database' The name of the database.
+ * 'hostspec' The hostname of the database server.
+ * '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>
+ *
+ * Optional parameters:<pre>
+ * 'table' The name of the tokens table in 'database'.
+ * Defaults to 'horde_tokens'.
+ * 'timeout' The period (in seconds) after which an id is purged.
+ * Defaults to 86400 (i.e. 24 hours).</pre>
+ *
+ * Optional values when using separate reading and writing servers, for example
+ * in replication settings:<pre>
+ * 'splitread' Boolean, whether to implement the separation or not.
+ * 'read' Array containing the parameters which are different for
+ * the read database connection, currently supported
+ * only 'hostspec' and 'port' parameters.</pre>
+ *
+ * The table structure for the tokens is as follows:
+ *
+ * <pre>
+ * CREATE TABLE horde_tokens (
+ * token_address VARCHAR(100) NOT NULL,
+ * token_id VARCHAR(32) NOT NULL,
+ * token_timestamp BIGINT NOT NULL,
+ *
+ * PRIMARY KEY (token_address, token_id)
+ * );
+ * </pre>
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Max Kalika <max@horde.org>
+ * @package Horde_Token
+ */
+class Horde_Token_sql extends Horde_Token
+{
+ /**
+ * 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;
+
+ /**
+ * Constructs a new SQL connection object.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ protected function __construct($params = array())
+ {
+ parent::__construct($params);
+
+ /* Set timeout to 24 hours if not specified. */
+ if (!isset($this->_params['timeout'])) {
+ $this->_params['timeout'] = 86400;
+ }
+ }
+
+ /**
+ * Deletes all expired connection id's from the SQL server.
+ *
+ * @return boolean True on success, a PEAR_Error object on failure.
+ */
+ public function purge()
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ /* Build SQL query. */
+ $query = 'DELETE FROM ' . $this->_params['table']
+ . ' WHERE token_timestamp < ?';
+
+ $values = array(time() - $this->_params['timeout']);
+
+ /* Return an error if the update fails. */
+ $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;
+ }
+
+ /**
+ * TODO
+ */
+ public function exists($tokenID)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return false;
+ }
+
+ /* Build SQL query. */
+ $query = 'SELECT token_id FROM ' . $this->_params['table']
+ . ' WHERE token_address = ? AND token_id = ?';
+
+ $values = array($this->encodeRemoteAddress(), $tokenID);
+
+ $result = $this->_db->getOne($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return false;
+ } else {
+ return !empty($result);
+ }
+ }
+
+ /**
+ * TODO
+ */
+ public function add($tokenID)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ /* Build SQL query. */
+ $query = 'INSERT INTO ' . $this->_params['table']
+ . ' (token_address, token_id, token_timestamp)'
+ . ' VALUES (?, ?, ?)';
+
+ $values = array($this->encodeRemoteAddress(), $tokenID, time());
+
+ $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;
+ }
+
+ /**
+ * Opens a connection to the SQL server.
+ *
+ * @return boolean True on success, a PEAR_Error object on failure.
+ */
+ protected function _connect()
+ {
+ if ($this->_connected) {
+ return true;
+ }
+
+ $result = Util::assertDriverConfig($this->_params, array('phptype'),
+ 'token SQL', array('driver' => 'token'));
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (!isset($this->_params['database'])) {
+ $this->_params['database'] = '';
+ }
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = '';
+ }
+ if (!isset($this->_params['password'])) {
+ $this->_params['password'] = '';
+ }
+ if (!isset($this->_params['hostspec'])) {
+ $this->_params['hostspec'] = '';
+ }
+ if (!isset($this->_params['table'])) {
+ $this->_params['table'] = 'horde_tokens';
+ }
+
+ /* Connect to the SQL server using the supplied parameters. */
+ require_once 'DB.php';
+ $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')) {
+ return $this->_write_db;
+ }
+
+ // 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($this->_params['ssl'])));
+ if (is_a($this->_db, 'PEAR_Error')) {
+ return $this->_db;
+ }
+
+ // Set DB portability options.
+ 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 read. */
+ $this->_db = $this->_write_db;
+ }
+
+ $this->_connected = true;
+ return true;
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Token</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde Token API</summary>
+ <description>The Horde_Token:: class provides a common abstracted interface
+ into the various token generation mediums. It also includes all of the
+ functions for retrieving, storing, and checking tokens.
+ </description>
+ <lead>
+ <name>Chuck Hagenbuch</name>
+ <user>chuck</user>
+ <email>chuck@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <date>2009-02-21</date>
+ <version>
+ <release>0.1.0</release>
+ <api>0.1.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Initial Horde 4 package.
+ </notes>
+ <contents>
+ <dir name="/">
+ <dir name="/lib">
+ <dir name="/lib/Horde">
+ <dir name="/lib/Horde/Token">
+ <file name="File.php" role="php" />
+ <file name="Sql.php" role="php" />
+ </dir> <!-- /lib/Horde/Token -->
+ <file name="Token.php" role="php" />
+ </dir> <!-- /lib/Horde -->
+ </dir> <!-- /lib -->
+ </dir> <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.5.0</min>
+ </pearinstaller>
+ <package>
+ <name>Util</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ </required>
+ </dependencies>
+ <phprelease>
+ <filelist>
+ <install name="lib/Horde/Token/File.php" as="Horde/Token/File.php" />
+ <install name="lib/Horde/Token/Sql.php" as="Horde/Token/Sql.php" />
+ <install name="lib/Horde/Token.php" as="Horde/Token.php" />
+ </filelist>
+ </phprelease>
+ <changelog>
+ <release>
+ <version>
+ <release>0.0.4</release>
+ <api>0.0.4</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <date>2006-05-08</date>
+ <time>23:48:27</time>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>Converted to package.xml 2.0 for pear.horde.org
+ </notes>
+ </release>
+ <release>
+ <version>
+ <release>0.0.3</release>
+ <api>0.0.3</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <date>2005-01-01</date>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Add support for separate read and write DB servers for the sql driver.
+ </notes>
+ </release>
+ <release>
+ <version>
+ <release>0.0.2</release>
+ <api>0.0.2</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <date>2004-11-23</date>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>Remove assumption of IPv4 IP address format
+ </notes>
+ </release>
+ <release>
+ <version>
+ <release>0.0.1</release>
+ <api>0.0.1</api>
+ </version>
+ <stability>
+ <release>alpha</release>
+ <api>alpha</api>
+ </stability>
+ <date>2003-07-03</date>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>Initial release as a PEAR package
+ </notes>
+ </release>
+ </changelog>
+</package>