FileStore.php

Go to the documentation of this file.
00001 <?php
00002 
00020 require_once 'Auth/OpenID.php';
00021 require_once 'Auth/OpenID/Interface.php';
00022 require_once 'Auth/OpenID/HMAC.php';
00023 require_once 'Auth/OpenID/Nonce.php';
00024 
00039 class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
00040 
00049     function Auth_OpenID_FileStore($directory)
00050     {
00051         if (!Auth_OpenID::ensureDir($directory)) {
00052             trigger_error('Not a directory and failed to create: '
00053                           . $directory, E_USER_ERROR);
00054         }
00055         $directory = realpath($directory);
00056 
00057         $this->directory = $directory;
00058         $this->active = true;
00059 
00060         $this->nonce_dir = $directory . DIRECTORY_SEPARATOR . 'nonces';
00061 
00062         $this->association_dir = $directory . DIRECTORY_SEPARATOR .
00063             'associations';
00064 
00065         // Temp dir must be on the same filesystem as the assciations
00066         // $directory.
00067         $this->temp_dir = $directory . DIRECTORY_SEPARATOR . 'temp';
00068 
00069         $this->max_nonce_age = 6 * 60 * 60; // Six hours, in seconds
00070 
00071         if (!$this->_setup()) {
00072             trigger_error('Failed to initialize OpenID file store in ' .
00073                           $directory, E_USER_ERROR);
00074         }
00075     }
00076 
00077     function destroy()
00078     {
00079         Auth_OpenID_FileStore::_rmtree($this->directory);
00080         $this->active = false;
00081     }
00082 
00089     function _setup()
00090     {
00091         return (Auth_OpenID::ensureDir($this->nonce_dir) &&
00092                 Auth_OpenID::ensureDir($this->association_dir) &&
00093                 Auth_OpenID::ensureDir($this->temp_dir));
00094     }
00095 
00108     function _mktemp()
00109     {
00110         $name = Auth_OpenID_FileStore::_mkstemp($dir = $this->temp_dir);
00111         $file_obj = @fopen($name, 'wb');
00112         if ($file_obj !== false) {
00113             return array($file_obj, $name);
00114         } else {
00115             Auth_OpenID_FileStore::_removeIfPresent($name);
00116         }
00117     }
00118 
00119     function cleanupNonces()
00120     {
00121         global $Auth_OpenID_SKEW;
00122 
00123         $nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
00124         $now = time();
00125 
00126         $removed = 0;
00127         // Check all nonces for expiry
00128         foreach ($nonces as $nonce_fname) {
00129             $base = basename($nonce_fname);
00130             $parts = explode('-', $base, 2);
00131             $timestamp = $parts[0];
00132             $timestamp = intval($timestamp, 16);
00133             if (abs($timestamp - $now) > $Auth_OpenID_SKEW) {
00134                 Auth_OpenID_FileStore::_removeIfPresent($nonce_fname);
00135                 $removed += 1;
00136             }
00137         }
00138         return $removed;
00139     }
00140 
00150     function getAssociationFilename($server_url, $handle)
00151     {
00152         if (!$this->active) {
00153             trigger_error("FileStore no longer active", E_USER_ERROR);
00154             return null;
00155         }
00156 
00157         if (strpos($server_url, '://') === false) {
00158             trigger_error(sprintf("Bad server URL: %s", $server_url),
00159                           E_USER_WARNING);
00160             return null;
00161         }
00162 
00163         list($proto, $rest) = explode('://', $server_url, 2);
00164         $parts = explode('/', $rest);
00165         $domain = Auth_OpenID_FileStore::_filenameEscape($parts[0]);
00166         $url_hash = Auth_OpenID_FileStore::_safe64($server_url);
00167         if ($handle) {
00168             $handle_hash = Auth_OpenID_FileStore::_safe64($handle);
00169         } else {
00170             $handle_hash = '';
00171         }
00172 
00173         $filename = sprintf('%s-%s-%s-%s', $proto, $domain, $url_hash,
00174                             $handle_hash);
00175 
00176         return $this->association_dir. DIRECTORY_SEPARATOR . $filename;
00177     }
00178 
00182     function storeAssociation($server_url, $association)
00183     {
00184         if (!$this->active) {
00185             trigger_error("FileStore no longer active", E_USER_ERROR);
00186             return false;
00187         }
00188 
00189         $association_s = $association->serialize();
00190         $filename = $this->getAssociationFilename($server_url,
00191                                                   $association->handle);
00192         list($tmp_file, $tmp) = $this->_mktemp();
00193 
00194         if (!$tmp_file) {
00195             trigger_error("_mktemp didn't return a valid file descriptor",
00196                           E_USER_WARNING);
00197             return false;
00198         }
00199 
00200         fwrite($tmp_file, $association_s);
00201 
00202         fflush($tmp_file);
00203 
00204         fclose($tmp_file);
00205 
00206         if (@rename($tmp, $filename)) {
00207             return true;
00208         } else {
00209             // In case we are running on Windows, try unlinking the
00210             // file in case it exists.
00211             @unlink($filename);
00212 
00213             // Now the target should not exist. Try renaming again,
00214             // giving up if it fails.
00215             if (@rename($tmp, $filename)) {
00216                 return true;
00217             }
00218         }
00219 
00220         // If there was an error, don't leave the temporary file
00221         // around.
00222         Auth_OpenID_FileStore::_removeIfPresent($tmp);
00223         return false;
00224     }
00225 
00232     function getAssociation($server_url, $handle = null)
00233     {
00234         if (!$this->active) {
00235             trigger_error("FileStore no longer active", E_USER_ERROR);
00236             return null;
00237         }
00238 
00239         if ($handle === null) {
00240             $handle = '';
00241         }
00242 
00243         // The filename with the empty handle is a prefix of all other
00244         // associations for the given server URL.
00245         $filename = $this->getAssociationFilename($server_url, $handle);
00246 
00247         if ($handle) {
00248             return $this->_getAssociation($filename);
00249         } else {
00250             $association_files =
00251                 Auth_OpenID_FileStore::_listdir($this->association_dir);
00252             $matching_files = array();
00253 
00254             // strip off the path to do the comparison
00255             $name = basename($filename);
00256             foreach ($association_files as $association_file) {
00257                 $base = basename($association_file);
00258                 if (strpos($base, $name) === 0) {
00259                     $matching_files[] = $association_file;
00260                 }
00261             }
00262 
00263             $matching_associations = array();
00264             // read the matching files and sort by time issued
00265             foreach ($matching_files as $full_name) {
00266                 $association = $this->_getAssociation($full_name);
00267                 if ($association !== null) {
00268                     $matching_associations[] = array($association->issued,
00269                                                      $association);
00270                 }
00271             }
00272 
00273             $issued = array();
00274             $assocs = array();
00275             foreach ($matching_associations as $key => $assoc) {
00276                 $issued[$key] = $assoc[0];
00277                 $assocs[$key] = $assoc[1];
00278             }
00279 
00280             array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
00281                             $matching_associations);
00282 
00283             // return the most recently issued one.
00284             if ($matching_associations) {
00285                 list($issued, $assoc) = $matching_associations[0];
00286                 return $assoc;
00287             } else {
00288                 return null;
00289             }
00290         }
00291     }
00292 
00296     function _getAssociation($filename)
00297     {
00298         if (!$this->active) {
00299             trigger_error("FileStore no longer active", E_USER_ERROR);
00300             return null;
00301         }
00302 
00303         $assoc_file = @fopen($filename, 'rb');
00304 
00305         if ($assoc_file === false) {
00306             return null;
00307         }
00308 
00309         $assoc_s = fread($assoc_file, filesize($filename));
00310         fclose($assoc_file);
00311 
00312         if (!$assoc_s) {
00313             return null;
00314         }
00315 
00316         $association =
00317             Auth_OpenID_Association::deserialize('Auth_OpenID_Association',
00318                                                 $assoc_s);
00319 
00320         if (!$association) {
00321             Auth_OpenID_FileStore::_removeIfPresent($filename);
00322             return null;
00323         }
00324 
00325         if ($association->getExpiresIn() == 0) {
00326             Auth_OpenID_FileStore::_removeIfPresent($filename);
00327             return null;
00328         } else {
00329             return $association;
00330         }
00331     }
00332 
00338     function removeAssociation($server_url, $handle)
00339     {
00340         if (!$this->active) {
00341             trigger_error("FileStore no longer active", E_USER_ERROR);
00342             return null;
00343         }
00344 
00345         $assoc = $this->getAssociation($server_url, $handle);
00346         if ($assoc === null) {
00347             return false;
00348         } else {
00349             $filename = $this->getAssociationFilename($server_url, $handle);
00350             return Auth_OpenID_FileStore::_removeIfPresent($filename);
00351         }
00352     }
00353 
00360     function useNonce($server_url, $timestamp, $salt)
00361     {
00362         global $Auth_OpenID_SKEW;
00363 
00364         if (!$this->active) {
00365             trigger_error("FileStore no longer active", E_USER_ERROR);
00366             return null;
00367         }
00368 
00369         if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
00370             return False;
00371         }
00372 
00373         if ($server_url) {
00374             list($proto, $rest) = explode('://', $server_url, 2);
00375         } else {
00376             $proto = '';
00377             $rest = '';
00378         }
00379 
00380         $parts = explode('/', $rest, 2);
00381         $domain = $this->_filenameEscape($parts[0]);
00382         $url_hash = $this->_safe64($server_url);
00383         $salt_hash = $this->_safe64($salt);
00384 
00385         $filename = sprintf('%08x-%s-%s-%s-%s', $timestamp, $proto,
00386                             $domain, $url_hash, $salt_hash);
00387         $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $filename;
00388 
00389         $result = @fopen($filename, 'x');
00390 
00391         if ($result === false) {
00392             return false;
00393         } else {
00394             fclose($result);
00395             return true;
00396         }
00397     }
00398 
00405     function _allAssocs()
00406     {
00407         $all_associations = array();
00408 
00409         $association_filenames =
00410             Auth_OpenID_FileStore::_listdir($this->association_dir);
00411 
00412         foreach ($association_filenames as $association_filename) {
00413             $association_file = fopen($association_filename, 'rb');
00414 
00415             if ($association_file !== false) {
00416                 $assoc_s = fread($association_file,
00417                                  filesize($association_filename));
00418                 fclose($association_file);
00419 
00420                 // Remove expired or corrupted associations
00421                 $association =
00422                   Auth_OpenID_Association::deserialize(
00423                          'Auth_OpenID_Association', $assoc_s);
00424 
00425                 if ($association === null) {
00426                     Auth_OpenID_FileStore::_removeIfPresent(
00427                                                  $association_filename);
00428                 } else {
00429                     if ($association->getExpiresIn() == 0) {
00430                         $all_associations[] = array($association_filename,
00431                                                     $association);
00432                     }
00433                 }
00434             }
00435         }
00436 
00437         return $all_associations;
00438     }
00439 
00440     function clean()
00441     {
00442         if (!$this->active) {
00443             trigger_error("FileStore no longer active", E_USER_ERROR);
00444             return null;
00445         }
00446 
00447         $nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
00448         $now = time();
00449 
00450         // Check all nonces for expiry
00451         foreach ($nonces as $nonce) {
00452             if (!Auth_OpenID_checkTimestamp($nonce, $now)) {
00453                 $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
00454                 Auth_OpenID_FileStore::_removeIfPresent($filename);
00455             }
00456         }
00457 
00458         foreach ($this->_allAssocs() as $pair) {
00459             list($assoc_filename, $assoc) = $pair;
00460             if ($assoc->getExpiresIn() == 0) {
00461                 Auth_OpenID_FileStore::_removeIfPresent($assoc_filename);
00462             }
00463         }
00464     }
00465 
00469     function _rmtree($dir)
00470     {
00471         if ($dir[strlen($dir) - 1] != DIRECTORY_SEPARATOR) {
00472             $dir .= DIRECTORY_SEPARATOR;
00473         }
00474 
00475         if ($handle = opendir($dir)) {
00476             while ($item = readdir($handle)) {
00477                 if (!in_array($item, array('.', '..'))) {
00478                     if (is_dir($dir . $item)) {
00479 
00480                         if (!Auth_OpenID_FileStore::_rmtree($dir . $item)) {
00481                             return false;
00482                         }
00483                     } else if (is_file($dir . $item)) {
00484                         if (!unlink($dir . $item)) {
00485                             return false;
00486                         }
00487                     }
00488                 }
00489             }
00490 
00491             closedir($handle);
00492 
00493             if (!@rmdir($dir)) {
00494                 return false;
00495             }
00496 
00497             return true;
00498         } else {
00499             // Couldn't open directory.
00500             return false;
00501         }
00502     }
00503 
00507     function _mkstemp($dir)
00508     {
00509         foreach (range(0, 4) as $i) {
00510             $name = tempnam($dir, "php_openid_filestore_");
00511 
00512             if ($name !== false) {
00513                 return $name;
00514             }
00515         }
00516         return false;
00517     }
00518 
00522     function _mkdtemp($dir)
00523     {
00524         foreach (range(0, 4) as $i) {
00525             $name = $dir . strval(DIRECTORY_SEPARATOR) . strval(getmypid()) .
00526                 "-" . strval(rand(1, time()));
00527             if (!mkdir($name, 0700)) {
00528                 return false;
00529             } else {
00530                 return $name;
00531             }
00532         }
00533         return false;
00534     }
00535 
00539     function _listdir($dir)
00540     {
00541         $handle = opendir($dir);
00542         $files = array();
00543         while (false !== ($filename = readdir($handle))) {
00544             if (!in_array($filename, array('.', '..'))) {
00545                 $files[] = $dir . DIRECTORY_SEPARATOR . $filename;
00546             }
00547         }
00548         return $files;
00549     }
00550 
00554     function _isFilenameSafe($char)
00555     {
00556         $_Auth_OpenID_filename_allowed = Auth_OpenID_letters .
00557             Auth_OpenID_digits . ".";
00558         return (strpos($_Auth_OpenID_filename_allowed, $char) !== false);
00559     }
00560 
00564     function _safe64($str)
00565     {
00566         $h64 = base64_encode(Auth_OpenID_SHA1($str));
00567         $h64 = str_replace('+', '_', $h64);
00568         $h64 = str_replace('/', '.', $h64);
00569         $h64 = str_replace('=', '', $h64);
00570         return $h64;
00571     }
00572 
00576     function _filenameEscape($str)
00577     {
00578         $filename = "";
00579         $b = Auth_OpenID::toBytes($str);
00580 
00581         for ($i = 0; $i < count($b); $i++) {
00582             $c = $b[$i];
00583             if (Auth_OpenID_FileStore::_isFilenameSafe($c)) {
00584                 $filename .= $c;
00585             } else {
00586                 $filename .= sprintf("_%02X", ord($c));
00587             }
00588         }
00589         return $filename;
00590     }
00591 
00599     function _removeIfPresent($filename)
00600     {
00601         return @unlink($filename);
00602     }
00603 
00604     function cleanupAssociations()
00605     {
00606         $removed = 0;
00607         foreach ($this->_allAssocs() as $pair) {
00608             list($assoc_filename, $assoc) = $pair;
00609             if ($assoc->getExpiresIn() == 0) {
00610                 $this->_removeIfPresent($assoc_filename);
00611                 $removed += 1;
00612             }
00613         }
00614         return $removed;
00615     }
00616 }
00617 
00618 ?>

Generated on Thu Feb 19 15:02:21 2009 for OXID eShop CE by  doxygen 1.5.5