| 42 | aurelien | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | /**
 | 
        
           |  |  | 4 |  * This file supplies a Memcached store backend for OpenID servers and
 | 
        
           |  |  | 5 |  * consumers.
 | 
        
           |  |  | 6 |  *
 | 
        
           |  |  | 7 |  * PHP versions 4 and 5
 | 
        
           |  |  | 8 |  *
 | 
        
           |  |  | 9 |  * LICENSE: See the COPYING file included in this distribution.
 | 
        
           |  |  | 10 |  *
 | 
        
           |  |  | 11 |  * @package OpenID
 | 
        
           |  |  | 12 |  * @author JanRain, Inc. <openid@janrain.com>
 | 
        
           |  |  | 13 |  * @copyright 2005 Janrain, Inc.
 | 
        
           |  |  | 14 |  * @license http://www.gnu.org/copyleft/lesser.html LGPL
 | 
        
           |  |  | 15 |  *
 | 
        
           |  |  | 16 |  */
 | 
        
           |  |  | 17 |   | 
        
           |  |  | 18 | /**
 | 
        
           |  |  | 19 |  * Require base class for creating a new interface.
 | 
        
           |  |  | 20 |  */
 | 
        
           |  |  | 21 | require_once 'Auth/OpenID.php';
 | 
        
           |  |  | 22 | require_once 'Auth/OpenID/Interface.php';
 | 
        
           |  |  | 23 | require_once 'Auth/OpenID/HMACSHA1.php';
 | 
        
           |  |  | 24 |   | 
        
           |  |  | 25 | /**
 | 
        
           |  |  | 26 |  * This is a filesystem-based store for OpenID associations and
 | 
        
           |  |  | 27 |  * nonces.  This store should be safe for use in concurrent systems on
 | 
        
           |  |  | 28 |  * both windows and unix (excluding NFS filesystems).  There are a
 | 
        
           |  |  | 29 |  * couple race conditions in the system, but those failure cases have
 | 
        
           |  |  | 30 |  * been set up in such a way that the worst-case behavior is someone
 | 
        
           |  |  | 31 |  * having to try to log in a second time.
 | 
        
           |  |  | 32 |  *
 | 
        
           |  |  | 33 |  * Most of the methods of this class are implementation details.
 | 
        
           |  |  | 34 |  * People wishing to just use this store need only pay attention to
 | 
        
           |  |  | 35 |  * the constructor.
 | 
        
           |  |  | 36 |  *
 | 
        
           |  |  | 37 |  * @package OpenID
 | 
        
           |  |  | 38 |  */
 | 
        
           |  |  | 39 | class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
 | 
        
           |  |  | 40 |   | 
        
           |  |  | 41 |     /**
 | 
        
           |  |  | 42 |      * Initializes a new {@link Auth_OpenID_FileStore}.  This
 | 
        
           |  |  | 43 |      * initializes the nonce and association directories, which are
 | 
        
           |  |  | 44 |      * subdirectories of the directory passed in.
 | 
        
           |  |  | 45 |      *
 | 
        
           |  |  | 46 |      * @param string $directory This is the directory to put the store
 | 
        
           |  |  | 47 |      * directories in.
 | 
        
           |  |  | 48 |      */
 | 
        
           |  |  | 49 |     function Auth_OpenID_FileStore($directory)
 | 
        
           |  |  | 50 |     {
 | 
        
           |  |  | 51 |         if (!Auth_OpenID::ensureDir($directory)) {
 | 
        
           |  |  | 52 |             trigger_error('Not a directory and failed to create: '
 | 
        
           |  |  | 53 |                           . $directory, E_USER_ERROR);
 | 
        
           |  |  | 54 |         }
 | 
        
           |  |  | 55 |         $directory = realpath($directory);
 | 
        
           |  |  | 56 |   | 
        
           |  |  | 57 |         $this->directory = $directory;
 | 
        
           |  |  | 58 |         $this->active = true;
 | 
        
           |  |  | 59 |   | 
        
           |  |  | 60 |         $this->nonce_dir = $directory . DIRECTORY_SEPARATOR . 'nonces';
 | 
        
           |  |  | 61 |   | 
        
           |  |  | 62 |         $this->association_dir = $directory . DIRECTORY_SEPARATOR .
 | 
        
           |  |  | 63 |             'associations';
 | 
        
           |  |  | 64 |   | 
        
           |  |  | 65 |         // Temp dir must be on the same filesystem as the assciations
 | 
        
           |  |  | 66 |         // $directory and the $directory containing the auth key file.
 | 
        
           |  |  | 67 |         $this->temp_dir = $directory . DIRECTORY_SEPARATOR . 'temp';
 | 
        
           |  |  | 68 |   | 
        
           |  |  | 69 |         $this->auth_key_name = $directory . DIRECTORY_SEPARATOR . 'auth_key';
 | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 |         $this->max_nonce_age = 6 * 60 * 60; // Six hours, in seconds
 | 
        
           |  |  | 72 |   | 
        
           |  |  | 73 |         if (!$this->_setup()) {
 | 
        
           |  |  | 74 |             trigger_error('Failed to initialize OpenID file store in ' .
 | 
        
           |  |  | 75 |                           $directory, E_USER_ERROR);
 | 
        
           |  |  | 76 |         }
 | 
        
           |  |  | 77 |     }
 | 
        
           |  |  | 78 |   | 
        
           |  |  | 79 |     function destroy()
 | 
        
           |  |  | 80 |     {
 | 
        
           |  |  | 81 |         Auth_OpenID_FileStore::_rmtree($this->directory);
 | 
        
           |  |  | 82 |         $this->active = false;
 | 
        
           |  |  | 83 |     }
 | 
        
           |  |  | 84 |   | 
        
           |  |  | 85 |     /**
 | 
        
           |  |  | 86 |      * Make sure that the directories in which we store our data
 | 
        
           |  |  | 87 |      * exist.
 | 
        
           |  |  | 88 |      *
 | 
        
           |  |  | 89 |      * @access private
 | 
        
           |  |  | 90 |      */
 | 
        
           |  |  | 91 |     function _setup()
 | 
        
           |  |  | 92 |     {
 | 
        
           |  |  | 93 |         return (Auth_OpenID::ensureDir(dirname($this->auth_key_name)) &&
 | 
        
           |  |  | 94 |                 Auth_OpenID::ensureDir($this->nonce_dir) &&
 | 
        
           |  |  | 95 |                 Auth_OpenID::ensureDir($this->association_dir) &&
 | 
        
           |  |  | 96 |                 Auth_OpenID::ensureDir($this->temp_dir));
 | 
        
           |  |  | 97 |     }
 | 
        
           |  |  | 98 |   | 
        
           |  |  | 99 |     /**
 | 
        
           |  |  | 100 |      * Create a temporary file on the same filesystem as
 | 
        
           |  |  | 101 |      * $this->auth_key_name and $this->association_dir.
 | 
        
           |  |  | 102 |      *
 | 
        
           |  |  | 103 |      * The temporary directory should not be cleaned if there are any
 | 
        
           |  |  | 104 |      * processes using the store. If there is no active process using
 | 
        
           |  |  | 105 |      * the store, it is safe to remove all of the files in the
 | 
        
           |  |  | 106 |      * temporary directory.
 | 
        
           |  |  | 107 |      *
 | 
        
           |  |  | 108 |      * @return array ($fd, $filename)
 | 
        
           |  |  | 109 |      * @access private
 | 
        
           |  |  | 110 |      */
 | 
        
           |  |  | 111 |     function _mktemp()
 | 
        
           |  |  | 112 |     {
 | 
        
           |  |  | 113 |         $name = Auth_OpenID_FileStore::_mkstemp($dir = $this->temp_dir);
 | 
        
           |  |  | 114 |         $file_obj = @fopen($name, 'wb');
 | 
        
           |  |  | 115 |         if ($file_obj !== false) {
 | 
        
           |  |  | 116 |             return array($file_obj, $name);
 | 
        
           |  |  | 117 |         } else {
 | 
        
           |  |  | 118 |             Auth_OpenID_FileStore::_removeIfPresent($name);
 | 
        
           |  |  | 119 |         }
 | 
        
           |  |  | 120 |     }
 | 
        
           |  |  | 121 |   | 
        
           |  |  | 122 |     /**
 | 
        
           |  |  | 123 |      * Read the auth key from the auth key file. Will return None if
 | 
        
           |  |  | 124 |      * there is currently no key.
 | 
        
           |  |  | 125 |      *
 | 
        
           |  |  | 126 |      * @return mixed
 | 
        
           |  |  | 127 |      */
 | 
        
           |  |  | 128 |     function readAuthKey()
 | 
        
           |  |  | 129 |     {
 | 
        
           |  |  | 130 |         if (!$this->active) {
 | 
        
           |  |  | 131 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 132 |             return null;
 | 
        
           |  |  | 133 |         }
 | 
        
           |  |  | 134 |   | 
        
           |  |  | 135 |         $auth_key_file = @fopen($this->auth_key_name, 'rb');
 | 
        
           |  |  | 136 |         if ($auth_key_file === false) {
 | 
        
           |  |  | 137 |             return null;
 | 
        
           |  |  | 138 |         }
 | 
        
           |  |  | 139 |   | 
        
           |  |  | 140 |         $key = fread($auth_key_file, filesize($this->auth_key_name));
 | 
        
           |  |  | 141 |         fclose($auth_key_file);
 | 
        
           |  |  | 142 |   | 
        
           |  |  | 143 |         return $key;
 | 
        
           |  |  | 144 |     }
 | 
        
           |  |  | 145 |   | 
        
           |  |  | 146 |     /**
 | 
        
           |  |  | 147 |      * Generate a new random auth key and safely store it in the
 | 
        
           |  |  | 148 |      * location specified by $this->auth_key_name.
 | 
        
           |  |  | 149 |      *
 | 
        
           |  |  | 150 |      * @return string $key
 | 
        
           |  |  | 151 |      */
 | 
        
           |  |  | 152 |     function createAuthKey()
 | 
        
           |  |  | 153 |     {
 | 
        
           |  |  | 154 |         if (!$this->active) {
 | 
        
           |  |  | 155 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 156 |             return null;
 | 
        
           |  |  | 157 |         }
 | 
        
           |  |  | 158 |   | 
        
           |  |  | 159 |         $auth_key = Auth_OpenID_CryptUtil::randomString($this->AUTH_KEY_LEN);
 | 
        
           |  |  | 160 |   | 
        
           |  |  | 161 |         list($file_obj, $tmp) = $this->_mktemp();
 | 
        
           |  |  | 162 |   | 
        
           |  |  | 163 |         fwrite($file_obj, $auth_key);
 | 
        
           |  |  | 164 |         fflush($file_obj);
 | 
        
           |  |  | 165 |         fclose($file_obj);
 | 
        
           |  |  | 166 |   | 
        
           |  |  | 167 |         if (function_exists('link')) {
 | 
        
           |  |  | 168 |             // Posix filesystem
 | 
        
           |  |  | 169 |             $saved = link($tmp, $this->auth_key_name);
 | 
        
           |  |  | 170 |             Auth_OpenID_FileStore::_removeIfPresent($tmp);
 | 
        
           |  |  | 171 |         } else {
 | 
        
           |  |  | 172 |             // Windows filesystem
 | 
        
           |  |  | 173 |             $saved = rename($tmp, $this->auth_key_name);
 | 
        
           |  |  | 174 |         }
 | 
        
           |  |  | 175 |   | 
        
           |  |  | 176 |         if (!$saved) {
 | 
        
           |  |  | 177 |             // The link failed, either because we lack the permission,
 | 
        
           |  |  | 178 |             // or because the file already exists; try to read the key
 | 
        
           |  |  | 179 |             // in case the file already existed.
 | 
        
           |  |  | 180 |             $auth_key = $this->readAuthKey();
 | 
        
           |  |  | 181 |         }
 | 
        
           |  |  | 182 |   | 
        
           |  |  | 183 |         return $auth_key;
 | 
        
           |  |  | 184 |     }
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 |     /**
 | 
        
           |  |  | 187 |      * Retrieve the auth key from the file specified by
 | 
        
           |  |  | 188 |      * $this->auth_key_name, creating it if it does not exist.
 | 
        
           |  |  | 189 |      *
 | 
        
           |  |  | 190 |      * @return string $key
 | 
        
           |  |  | 191 |      */
 | 
        
           |  |  | 192 |     function getAuthKey()
 | 
        
           |  |  | 193 |     {
 | 
        
           |  |  | 194 |         if (!$this->active) {
 | 
        
           |  |  | 195 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 196 |             return null;
 | 
        
           |  |  | 197 |         }
 | 
        
           |  |  | 198 |   | 
        
           |  |  | 199 |         $auth_key = $this->readAuthKey();
 | 
        
           |  |  | 200 |         if ($auth_key === null) {
 | 
        
           |  |  | 201 |             $auth_key = $this->createAuthKey();
 | 
        
           |  |  | 202 |   | 
        
           |  |  | 203 |             if (strlen($auth_key) != $this->AUTH_KEY_LEN) {
 | 
        
           |  |  | 204 |                 $fmt = 'Got an invalid auth key from %s. Expected '.
 | 
        
           |  |  | 205 |                     '%d-byte string. Got: %s';
 | 
        
           |  |  | 206 |                 $msg = sprintf($fmt, $this->auth_key_name, $this->AUTH_KEY_LEN,
 | 
        
           |  |  | 207 |                                $auth_key);
 | 
        
           |  |  | 208 |                 trigger_error($msg, E_USER_WARNING);
 | 
        
           |  |  | 209 |                 return null;
 | 
        
           |  |  | 210 |             }
 | 
        
           |  |  | 211 |         }
 | 
        
           |  |  | 212 |         return $auth_key;
 | 
        
           |  |  | 213 |     }
 | 
        
           |  |  | 214 |   | 
        
           |  |  | 215 |     /**
 | 
        
           |  |  | 216 |      * Create a unique filename for a given server url and
 | 
        
           |  |  | 217 |      * handle. This implementation does not assume anything about the
 | 
        
           |  |  | 218 |      * format of the handle. The filename that is returned will
 | 
        
           |  |  | 219 |      * contain the domain name from the server URL for ease of human
 | 
        
           |  |  | 220 |      * inspection of the data directory.
 | 
        
           |  |  | 221 |      *
 | 
        
           |  |  | 222 |      * @return string $filename
 | 
        
           |  |  | 223 |      */
 | 
        
           |  |  | 224 |     function getAssociationFilename($server_url, $handle)
 | 
        
           |  |  | 225 |     {
 | 
        
           |  |  | 226 |         if (!$this->active) {
 | 
        
           |  |  | 227 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 228 |             return null;
 | 
        
           |  |  | 229 |         }
 | 
        
           |  |  | 230 |   | 
        
           |  |  | 231 |         if (strpos($server_url, '://') === false) {
 | 
        
           |  |  | 232 |             trigger_error(sprintf("Bad server URL: %s", $server_url),
 | 
        
           |  |  | 233 |                           E_USER_WARNING);
 | 
        
           |  |  | 234 |             return null;
 | 
        
           |  |  | 235 |         }
 | 
        
           |  |  | 236 |   | 
        
           |  |  | 237 |         list($proto, $rest) = explode('://', $server_url, 2);
 | 
        
           |  |  | 238 |         $parts = explode('/', $rest);
 | 
        
           |  |  | 239 |         $domain = Auth_OpenID_FileStore::_filenameEscape($parts[0]);
 | 
        
           |  |  | 240 |         $url_hash = Auth_OpenID_FileStore::_safe64($server_url);
 | 
        
           |  |  | 241 |         if ($handle) {
 | 
        
           |  |  | 242 |             $handle_hash = Auth_OpenID_FileStore::_safe64($handle);
 | 
        
           |  |  | 243 |         } else {
 | 
        
           |  |  | 244 |             $handle_hash = '';
 | 
        
           |  |  | 245 |         }
 | 
        
           |  |  | 246 |   | 
        
           |  |  | 247 |         $filename = sprintf('%s-%s-%s-%s', $proto, $domain, $url_hash,
 | 
        
           |  |  | 248 |                             $handle_hash);
 | 
        
           |  |  | 249 |   | 
        
           |  |  | 250 |         return $this->association_dir. DIRECTORY_SEPARATOR . $filename;
 | 
        
           |  |  | 251 |     }
 | 
        
           |  |  | 252 |   | 
        
           |  |  | 253 |     /**
 | 
        
           |  |  | 254 |      * Store an association in the association directory.
 | 
        
           |  |  | 255 |      */
 | 
        
           |  |  | 256 |     function storeAssociation($server_url, $association)
 | 
        
           |  |  | 257 |     {
 | 
        
           |  |  | 258 |         if (!$this->active) {
 | 
        
           |  |  | 259 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 260 |             return false;
 | 
        
           |  |  | 261 |         }
 | 
        
           |  |  | 262 |   | 
        
           |  |  | 263 |         $association_s = $association->serialize();
 | 
        
           |  |  | 264 |         $filename = $this->getAssociationFilename($server_url,
 | 
        
           |  |  | 265 |                                                   $association->handle);
 | 
        
           |  |  | 266 |         list($tmp_file, $tmp) = $this->_mktemp();
 | 
        
           |  |  | 267 |   | 
        
           |  |  | 268 |         if (!$tmp_file) {
 | 
        
           |  |  | 269 |             trigger_error("_mktemp didn't return a valid file descriptor",
 | 
        
           |  |  | 270 |                           E_USER_WARNING);
 | 
        
           |  |  | 271 |             return false;
 | 
        
           |  |  | 272 |         }
 | 
        
           |  |  | 273 |   | 
        
           |  |  | 274 |         fwrite($tmp_file, $association_s);
 | 
        
           |  |  | 275 |   | 
        
           |  |  | 276 |         fflush($tmp_file);
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |         fclose($tmp_file);
 | 
        
           |  |  | 279 |   | 
        
           |  |  | 280 |         if (@rename($tmp, $filename)) {
 | 
        
           |  |  | 281 |             return true;
 | 
        
           |  |  | 282 |         } else {
 | 
        
           |  |  | 283 |             // In case we are running on Windows, try unlinking the
 | 
        
           |  |  | 284 |             // file in case it exists.
 | 
        
           |  |  | 285 |             @unlink($filename);
 | 
        
           |  |  | 286 |   | 
        
           |  |  | 287 |             // Now the target should not exist. Try renaming again,
 | 
        
           |  |  | 288 |             // giving up if it fails.
 | 
        
           |  |  | 289 |             if (@rename($tmp, $filename)) {
 | 
        
           |  |  | 290 |                 return true;
 | 
        
           |  |  | 291 |             }
 | 
        
           |  |  | 292 |         }
 | 
        
           |  |  | 293 |   | 
        
           |  |  | 294 |         // If there was an error, don't leave the temporary file
 | 
        
           |  |  | 295 |         // around.
 | 
        
           |  |  | 296 |         Auth_OpenID_FileStore::_removeIfPresent($tmp);
 | 
        
           |  |  | 297 |         return false;
 | 
        
           |  |  | 298 |     }
 | 
        
           |  |  | 299 |   | 
        
           |  |  | 300 |     /**
 | 
        
           |  |  | 301 |      * Retrieve an association. If no handle is specified, return the
 | 
        
           |  |  | 302 |      * association with the most recent issue time.
 | 
        
           |  |  | 303 |      *
 | 
        
           |  |  | 304 |      * @return mixed $association
 | 
        
           |  |  | 305 |      */
 | 
        
           |  |  | 306 |     function getAssociation($server_url, $handle = null)
 | 
        
           |  |  | 307 |     {
 | 
        
           |  |  | 308 |         if (!$this->active) {
 | 
        
           |  |  | 309 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 310 |             return null;
 | 
        
           |  |  | 311 |         }
 | 
        
           |  |  | 312 |   | 
        
           |  |  | 313 |         if ($handle === null) {
 | 
        
           |  |  | 314 |             $handle = '';
 | 
        
           |  |  | 315 |         }
 | 
        
           |  |  | 316 |   | 
        
           |  |  | 317 |         // The filename with the empty handle is a prefix of all other
 | 
        
           |  |  | 318 |         // associations for the given server URL.
 | 
        
           |  |  | 319 |         $filename = $this->getAssociationFilename($server_url, $handle);
 | 
        
           |  |  | 320 |   | 
        
           |  |  | 321 |         if ($handle) {
 | 
        
           |  |  | 322 |             return $this->_getAssociation($filename);
 | 
        
           |  |  | 323 |         } else {
 | 
        
           |  |  | 324 |             $association_files =
 | 
        
           |  |  | 325 |                 Auth_OpenID_FileStore::_listdir($this->association_dir);
 | 
        
           |  |  | 326 |             $matching_files = array();
 | 
        
           |  |  | 327 |   | 
        
           |  |  | 328 |             // strip off the path to do the comparison
 | 
        
           |  |  | 329 |             $name = basename($filename);
 | 
        
           |  |  | 330 |             foreach ($association_files as $association_file) {
 | 
        
           |  |  | 331 |                 if (strpos($association_file, $name) === 0) {
 | 
        
           |  |  | 332 |                     $matching_files[] = $association_file;
 | 
        
           |  |  | 333 |                 }
 | 
        
           |  |  | 334 |             }
 | 
        
           |  |  | 335 |   | 
        
           |  |  | 336 |             $matching_associations = array();
 | 
        
           |  |  | 337 |             // read the matching files and sort by time issued
 | 
        
           |  |  | 338 |             foreach ($matching_files as $name) {
 | 
        
           |  |  | 339 |                 $full_name = $this->association_dir . DIRECTORY_SEPARATOR .
 | 
        
           |  |  | 340 |                     $name;
 | 
        
           |  |  | 341 |                 $association = $this->_getAssociation($full_name);
 | 
        
           |  |  | 342 |                 if ($association !== null) {
 | 
        
           |  |  | 343 |                     $matching_associations[] = array($association->issued,
 | 
        
           |  |  | 344 |                                                      $association);
 | 
        
           |  |  | 345 |                 }
 | 
        
           |  |  | 346 |             }
 | 
        
           |  |  | 347 |   | 
        
           |  |  | 348 |             $issued = array();
 | 
        
           |  |  | 349 |             $assocs = array();
 | 
        
           |  |  | 350 |             foreach ($matching_associations as $key => $assoc) {
 | 
        
           |  |  | 351 |                 $issued[$key] = $assoc[0];
 | 
        
           |  |  | 352 |                 $assocs[$key] = $assoc[1];
 | 
        
           |  |  | 353 |             }
 | 
        
           |  |  | 354 |   | 
        
           |  |  | 355 |             array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
 | 
        
           |  |  | 356 |                             $matching_associations);
 | 
        
           |  |  | 357 |   | 
        
           |  |  | 358 |             // return the most recently issued one.
 | 
        
           |  |  | 359 |             if ($matching_associations) {
 | 
        
           |  |  | 360 |                 list($issued, $assoc) = $matching_associations[0];
 | 
        
           |  |  | 361 |                 return $assoc;
 | 
        
           |  |  | 362 |             } else {
 | 
        
           |  |  | 363 |                 return null;
 | 
        
           |  |  | 364 |             }
 | 
        
           |  |  | 365 |         }
 | 
        
           |  |  | 366 |     }
 | 
        
           |  |  | 367 |   | 
        
           |  |  | 368 |     /**
 | 
        
           |  |  | 369 |      * @access private
 | 
        
           |  |  | 370 |      */
 | 
        
           |  |  | 371 |     function _getAssociation($filename)
 | 
        
           |  |  | 372 |     {
 | 
        
           |  |  | 373 |         if (!$this->active) {
 | 
        
           |  |  | 374 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 375 |             return null;
 | 
        
           |  |  | 376 |         }
 | 
        
           |  |  | 377 |   | 
        
           |  |  | 378 |         $assoc_file = @fopen($filename, 'rb');
 | 
        
           |  |  | 379 |   | 
        
           |  |  | 380 |         if ($assoc_file === false) {
 | 
        
           |  |  | 381 |             return null;
 | 
        
           |  |  | 382 |         }
 | 
        
           |  |  | 383 |   | 
        
           |  |  | 384 |         $assoc_s = fread($assoc_file, filesize($filename));
 | 
        
           |  |  | 385 |         fclose($assoc_file);
 | 
        
           |  |  | 386 |   | 
        
           |  |  | 387 |         if (!$assoc_s) {
 | 
        
           |  |  | 388 |             return null;
 | 
        
           |  |  | 389 |         }
 | 
        
           |  |  | 390 |   | 
        
           |  |  | 391 |         $association =
 | 
        
           |  |  | 392 |             Auth_OpenID_Association::deserialize('Auth_OpenID_Association',
 | 
        
           |  |  | 393 |                                                 $assoc_s);
 | 
        
           |  |  | 394 |   | 
        
           |  |  | 395 |         if (!$association) {
 | 
        
           |  |  | 396 |             Auth_OpenID_FileStore::_removeIfPresent($filename);
 | 
        
           |  |  | 397 |             return null;
 | 
        
           |  |  | 398 |         }
 | 
        
           |  |  | 399 |   | 
        
           |  |  | 400 |         if ($association->getExpiresIn() == 0) {
 | 
        
           |  |  | 401 |             Auth_OpenID_FileStore::_removeIfPresent($filename);
 | 
        
           |  |  | 402 |             return null;
 | 
        
           |  |  | 403 |         } else {
 | 
        
           |  |  | 404 |             return $association;
 | 
        
           |  |  | 405 |         }
 | 
        
           |  |  | 406 |     }
 | 
        
           |  |  | 407 |   | 
        
           |  |  | 408 |     /**
 | 
        
           |  |  | 409 |      * Remove an association if it exists. Do nothing if it does not.
 | 
        
           |  |  | 410 |      *
 | 
        
           |  |  | 411 |      * @return bool $success
 | 
        
           |  |  | 412 |      */
 | 
        
           |  |  | 413 |     function removeAssociation($server_url, $handle)
 | 
        
           |  |  | 414 |     {
 | 
        
           |  |  | 415 |         if (!$this->active) {
 | 
        
           |  |  | 416 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 417 |             return null;
 | 
        
           |  |  | 418 |         }
 | 
        
           |  |  | 419 |   | 
        
           |  |  | 420 |         $assoc = $this->getAssociation($server_url, $handle);
 | 
        
           |  |  | 421 |         if ($assoc === null) {
 | 
        
           |  |  | 422 |             return false;
 | 
        
           |  |  | 423 |         } else {
 | 
        
           |  |  | 424 |             $filename = $this->getAssociationFilename($server_url, $handle);
 | 
        
           |  |  | 425 |             return Auth_OpenID_FileStore::_removeIfPresent($filename);
 | 
        
           |  |  | 426 |         }
 | 
        
           |  |  | 427 |     }
 | 
        
           |  |  | 428 |   | 
        
           |  |  | 429 |     /**
 | 
        
           |  |  | 430 |      * Mark this nonce as present.
 | 
        
           |  |  | 431 |      */
 | 
        
           |  |  | 432 |     function storeNonce($nonce)
 | 
        
           |  |  | 433 |     {
 | 
        
           |  |  | 434 |         if (!$this->active) {
 | 
        
           |  |  | 435 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 436 |             return null;
 | 
        
           |  |  | 437 |         }
 | 
        
           |  |  | 438 |   | 
        
           |  |  | 439 |         $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
 | 
        
           |  |  | 440 |         $nonce_file = fopen($filename, 'w');
 | 
        
           |  |  | 441 |         if ($nonce_file === false) {
 | 
        
           |  |  | 442 |             return false;
 | 
        
           |  |  | 443 |         }
 | 
        
           |  |  | 444 |         fclose($nonce_file);
 | 
        
           |  |  | 445 |         return true;
 | 
        
           |  |  | 446 |     }
 | 
        
           |  |  | 447 |   | 
        
           |  |  | 448 |     /**
 | 
        
           |  |  | 449 |      * Return whether this nonce is present. As a side effect, mark it
 | 
        
           |  |  | 450 |      * as no longer present.
 | 
        
           |  |  | 451 |      *
 | 
        
           |  |  | 452 |      * @return bool $present
 | 
        
           |  |  | 453 |      */
 | 
        
           |  |  | 454 |     function useNonce($nonce)
 | 
        
           |  |  | 455 |     {
 | 
        
           |  |  | 456 |         if (!$this->active) {
 | 
        
           |  |  | 457 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 458 |             return null;
 | 
        
           |  |  | 459 |         }
 | 
        
           |  |  | 460 |   | 
        
           |  |  | 461 |         $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
 | 
        
           |  |  | 462 |         $st = @stat($filename);
 | 
        
           |  |  | 463 |   | 
        
           |  |  | 464 |         if ($st === false) {
 | 
        
           |  |  | 465 |             return false;
 | 
        
           |  |  | 466 |         }
 | 
        
           |  |  | 467 |   | 
        
           |  |  | 468 |         // Either it is too old or we are using it. Either way, we
 | 
        
           |  |  | 469 |         // must remove the file.
 | 
        
           |  |  | 470 |         if (!unlink($filename)) {
 | 
        
           |  |  | 471 |             return false;
 | 
        
           |  |  | 472 |         }
 | 
        
           |  |  | 473 |   | 
        
           |  |  | 474 |         $now = time();
 | 
        
           |  |  | 475 |         $nonce_age = $now - $st[9];
 | 
        
           |  |  | 476 |   | 
        
           |  |  | 477 |         // We can us it if the age of the file is less than the
 | 
        
           |  |  | 478 |         // expiration time.
 | 
        
           |  |  | 479 |         return $nonce_age <= $this->max_nonce_age;
 | 
        
           |  |  | 480 |     }
 | 
        
           |  |  | 481 |   | 
        
           |  |  | 482 |     /**
 | 
        
           |  |  | 483 |      * Remove expired entries from the database. This is potentially
 | 
        
           |  |  | 484 |      * expensive, so only run when it is acceptable to take time.
 | 
        
           |  |  | 485 |      */
 | 
        
           |  |  | 486 |     function clean()
 | 
        
           |  |  | 487 |     {
 | 
        
           |  |  | 488 |         if (!$this->active) {
 | 
        
           |  |  | 489 |             trigger_error("FileStore no longer active", E_USER_ERROR);
 | 
        
           |  |  | 490 |             return null;
 | 
        
           |  |  | 491 |         }
 | 
        
           |  |  | 492 |   | 
        
           |  |  | 493 |         $nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
 | 
        
           |  |  | 494 |         $now = time();
 | 
        
           |  |  | 495 |   | 
        
           |  |  | 496 |         // Check all nonces for expiry
 | 
        
           |  |  | 497 |         foreach ($nonces as $nonce) {
 | 
        
           |  |  | 498 |             $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
 | 
        
           |  |  | 499 |             $st = @stat($filename);
 | 
        
           |  |  | 500 |   | 
        
           |  |  | 501 |             if ($st !== false) {
 | 
        
           |  |  | 502 |                 // Remove the nonce if it has expired
 | 
        
           |  |  | 503 |                 $nonce_age = $now - $st[9];
 | 
        
           |  |  | 504 |                 if ($nonce_age > $this->max_nonce_age) {
 | 
        
           |  |  | 505 |                     Auth_OpenID_FileStore::_removeIfPresent($filename);
 | 
        
           |  |  | 506 |                 }
 | 
        
           |  |  | 507 |             }
 | 
        
           |  |  | 508 |         }
 | 
        
           |  |  | 509 |   | 
        
           |  |  | 510 |         $association_filenames =
 | 
        
           |  |  | 511 |             Auth_OpenID_FileStore::_listdir($this->association_dir);
 | 
        
           |  |  | 512 |   | 
        
           |  |  | 513 |         foreach ($association_filenames as $association_filename) {
 | 
        
           |  |  | 514 |             $association_file = fopen($association_filename, 'rb');
 | 
        
           |  |  | 515 |   | 
        
           |  |  | 516 |             if ($association_file !== false) {
 | 
        
           |  |  | 517 |                 $assoc_s = fread($association_file,
 | 
        
           |  |  | 518 |                                  filesize($association_filename));
 | 
        
           |  |  | 519 |                 fclose($association_file);
 | 
        
           |  |  | 520 |   | 
        
           |  |  | 521 |                 // Remove expired or corrupted associations
 | 
        
           |  |  | 522 |                 $association =
 | 
        
           |  |  | 523 |                   Auth_OpenID_Association::deserialize(
 | 
        
           |  |  | 524 |                          'Auth_OpenID_Association', $assoc_s);
 | 
        
           |  |  | 525 |   | 
        
           |  |  | 526 |                 if ($association === null) {
 | 
        
           |  |  | 527 |                     Auth_OpenID_FileStore::_removeIfPresent(
 | 
        
           |  |  | 528 |                                                  $association_filename);
 | 
        
           |  |  | 529 |                 } else {
 | 
        
           |  |  | 530 |                     if ($association->getExpiresIn() == 0) {
 | 
        
           |  |  | 531 |                         Auth_OpenID_FileStore::_removeIfPresent(
 | 
        
           |  |  | 532 |                                                  $association_filename);
 | 
        
           |  |  | 533 |                     }
 | 
        
           |  |  | 534 |                 }
 | 
        
           |  |  | 535 |             }
 | 
        
           |  |  | 536 |         }
 | 
        
           |  |  | 537 |     }
 | 
        
           |  |  | 538 |   | 
        
           |  |  | 539 |     /**
 | 
        
           |  |  | 540 |      * @access private
 | 
        
           |  |  | 541 |      */
 | 
        
           |  |  | 542 |     function _rmtree($dir)
 | 
        
           |  |  | 543 |     {
 | 
        
           |  |  | 544 |         if ($dir[strlen($dir) - 1] != DIRECTORY_SEPARATOR) {
 | 
        
           |  |  | 545 |             $dir .= DIRECTORY_SEPARATOR;
 | 
        
           |  |  | 546 |         }
 | 
        
           |  |  | 547 |   | 
        
           |  |  | 548 |         if ($handle = opendir($dir)) {
 | 
        
           |  |  | 549 |             while ($item = readdir($handle)) {
 | 
        
           |  |  | 550 |                 if (!in_array($item, array('.', '..'))) {
 | 
        
           |  |  | 551 |                     if (is_dir($dir . $item)) {
 | 
        
           |  |  | 552 |   | 
        
           |  |  | 553 |                         if (!Auth_OpenID_FileStore::_rmtree($dir . $item)) {
 | 
        
           |  |  | 554 |                             return false;
 | 
        
           |  |  | 555 |                         }
 | 
        
           |  |  | 556 |                     } else if (is_file($dir . $item)) {
 | 
        
           |  |  | 557 |                         if (!unlink($dir . $item)) {
 | 
        
           |  |  | 558 |                             return false;
 | 
        
           |  |  | 559 |                         }
 | 
        
           |  |  | 560 |                     }
 | 
        
           |  |  | 561 |                 }
 | 
        
           |  |  | 562 |             }
 | 
        
           |  |  | 563 |   | 
        
           |  |  | 564 |             closedir($handle);
 | 
        
           |  |  | 565 |   | 
        
           |  |  | 566 |             if (!@rmdir($dir)) {
 | 
        
           |  |  | 567 |                 return false;
 | 
        
           |  |  | 568 |             }
 | 
        
           |  |  | 569 |   | 
        
           |  |  | 570 |             return true;
 | 
        
           |  |  | 571 |         } else {
 | 
        
           |  |  | 572 |             // Couldn't open directory.
 | 
        
           |  |  | 573 |             return false;
 | 
        
           |  |  | 574 |         }
 | 
        
           |  |  | 575 |     }
 | 
        
           |  |  | 576 |   | 
        
           |  |  | 577 |     /**
 | 
        
           |  |  | 578 |      * @access private
 | 
        
           |  |  | 579 |      */
 | 
        
           |  |  | 580 |     function _mkstemp($dir)
 | 
        
           |  |  | 581 |     {
 | 
        
           |  |  | 582 |         foreach (range(0, 4) as $i) {
 | 
        
           |  |  | 583 |             $name = tempnam($dir, "php_openid_filestore_");
 | 
        
           |  |  | 584 |   | 
        
           |  |  | 585 |             if ($name !== false) {
 | 
        
           |  |  | 586 |                 return $name;
 | 
        
           |  |  | 587 |             }
 | 
        
           |  |  | 588 |         }
 | 
        
           |  |  | 589 |         return false;
 | 
        
           |  |  | 590 |     }
 | 
        
           |  |  | 591 |   | 
        
           |  |  | 592 |     /**
 | 
        
           |  |  | 593 |      * @access private
 | 
        
           |  |  | 594 |      */
 | 
        
           |  |  | 595 |     function _mkdtemp($dir)
 | 
        
           |  |  | 596 |     {
 | 
        
           |  |  | 597 |         foreach (range(0, 4) as $i) {
 | 
        
           |  |  | 598 |             $name = $dir . strval(DIRECTORY_SEPARATOR) . strval(getmypid()) .
 | 
        
           |  |  | 599 |                 "-" . strval(rand(1, time()));
 | 
        
           |  |  | 600 |             if (!mkdir($name, 0700)) {
 | 
        
           |  |  | 601 |                 return false;
 | 
        
           |  |  | 602 |             } else {
 | 
        
           |  |  | 603 |                 return $name;
 | 
        
           |  |  | 604 |             }
 | 
        
           |  |  | 605 |         }
 | 
        
           |  |  | 606 |         return false;
 | 
        
           |  |  | 607 |     }
 | 
        
           |  |  | 608 |   | 
        
           |  |  | 609 |     /**
 | 
        
           |  |  | 610 |      * @access private
 | 
        
           |  |  | 611 |      */
 | 
        
           |  |  | 612 |     function _listdir($dir)
 | 
        
           |  |  | 613 |     {
 | 
        
           |  |  | 614 |         $handle = opendir($dir);
 | 
        
           |  |  | 615 |         $files = array();
 | 
        
           |  |  | 616 |         while (false !== ($filename = readdir($handle))) {
 | 
        
           |  |  | 617 |             $files[] = $filename;
 | 
        
           |  |  | 618 |         }
 | 
        
           |  |  | 619 |         return $files;
 | 
        
           |  |  | 620 |     }
 | 
        
           |  |  | 621 |   | 
        
           |  |  | 622 |     /**
 | 
        
           |  |  | 623 |      * @access private
 | 
        
           |  |  | 624 |      */
 | 
        
           |  |  | 625 |     function _isFilenameSafe($char)
 | 
        
           |  |  | 626 |     {
 | 
        
           |  |  | 627 |         $_Auth_OpenID_filename_allowed = Auth_OpenID_letters .
 | 
        
           |  |  | 628 |             Auth_OpenID_digits . ".";
 | 
        
           |  |  | 629 |         return (strpos($_Auth_OpenID_filename_allowed, $char) !== false);
 | 
        
           |  |  | 630 |     }
 | 
        
           |  |  | 631 |   | 
        
           |  |  | 632 |     /**
 | 
        
           |  |  | 633 |      * @access private
 | 
        
           |  |  | 634 |      */
 | 
        
           |  |  | 635 |     function _safe64($str)
 | 
        
           |  |  | 636 |     {
 | 
        
           |  |  | 637 |         $h64 = base64_encode(Auth_OpenID_SHA1($str));
 | 
        
           |  |  | 638 |         $h64 = str_replace('+', '_', $h64);
 | 
        
           |  |  | 639 |         $h64 = str_replace('/', '.', $h64);
 | 
        
           |  |  | 640 |         $h64 = str_replace('=', '', $h64);
 | 
        
           |  |  | 641 |         return $h64;
 | 
        
           |  |  | 642 |     }
 | 
        
           |  |  | 643 |   | 
        
           |  |  | 644 |     /**
 | 
        
           |  |  | 645 |      * @access private
 | 
        
           |  |  | 646 |      */
 | 
        
           |  |  | 647 |     function _filenameEscape($str)
 | 
        
           |  |  | 648 |     {
 | 
        
           |  |  | 649 |         $filename = "";
 | 
        
           |  |  | 650 |         for ($i = 0; $i < strlen($str); $i++) {
 | 
        
           |  |  | 651 |             $c = $str[$i];
 | 
        
           |  |  | 652 |             if (Auth_OpenID_FileStore::_isFilenameSafe($c)) {
 | 
        
           |  |  | 653 |                 $filename .= $c;
 | 
        
           |  |  | 654 |             } else {
 | 
        
           |  |  | 655 |                 $filename .= sprintf("_%02X", ord($c));
 | 
        
           |  |  | 656 |             }
 | 
        
           |  |  | 657 |         }
 | 
        
           |  |  | 658 |         return $filename;
 | 
        
           |  |  | 659 |     }
 | 
        
           |  |  | 660 |   | 
        
           |  |  | 661 |     /**
 | 
        
           |  |  | 662 |      * Attempt to remove a file, returning whether the file existed at
 | 
        
           |  |  | 663 |      * the time of the call.
 | 
        
           |  |  | 664 |      *
 | 
        
           |  |  | 665 |      * @access private
 | 
        
           |  |  | 666 |      * @return bool $result True if the file was present, false if not.
 | 
        
           |  |  | 667 |      */
 | 
        
           |  |  | 668 |     function _removeIfPresent($filename)
 | 
        
           |  |  | 669 |     {
 | 
        
           |  |  | 670 |         return @unlink($filename);
 | 
        
           |  |  | 671 |     }
 | 
        
           |  |  | 672 | }
 | 
        
           |  |  | 673 |   | 
        
           |  |  | 674 | ?>
 |