Seditio Source
Root |
./othercms/dotclear-2.22/inc/core/class.dc.store.reader.php
<?php
/**
 * @brief Repository modules XML feed reader
 *
 * Provides an object to parse XML feed of modules from repository.
 *
 * @package Dotclear
 * @subpackage Core
 *
 * @copyright Olivier Meunier & Association Dotclear
 * @copyright GPL-2.0-only
 *
 * @since 2.6
 */
if (!defined('DC_RC_PATH')) {
    return;
}

class
dcStoreReader extends netHttp
{
   
/** @var    string    User agent used to query repository */
   
protected $user_agent = 'DotClear.org RepoBrowser/0.1';
   
/** @var    array     HTTP Cache validators */
   
protected $validators = null;
   
/** @var    mixed     Cache temporary directory */
   
protected $cache_dir = null;
   
/** @var    string    Cache file prefix */
   
protected $cache_file_prefix = 'dcrepo';
   
/** @var    string    Cache TTL */
   
protected $cache_ttl = '-1440 minutes';
   
/** @var    boolean    'Cache' TTL on server failed */
   
protected $cache_touch_on_fail = true;
   
/** @var    boolean    Force query server */
   
protected $force = false;

   
/**
     * Constructor.
     *
     * Bypass first argument of clearbricks netHttp constructor.
     */
   
public function __construct()
    {
       
parent::__construct('');
       
$this->setUserAgent(sprintf('Dotclear/%s', DC_VERSION));
       
$this->setTimeout(DC_QUERY_TIMEOUT);
    }

   
/**
     * Parse modules feed.
     *
     * @param    string    $url        XML feed URL
     * @return   mixed     dcStore instance
     */
   
public function parse($url)
    {
       
$this->validators = [];

        if (
$this->cache_dir) {
            return
$this->withCache($url);
        } elseif (!
$this->getModulesXML($url) || $this->getStatus() != '200') {
            return
false;
        }

        return new
dcStoreParser($this->getContent());
    }

   
/**
     * Quick parse modules feed.
     *
     * @param    string    $url        XML feed URL
     * @param    string    $cache_dir    Cache directoy or null for no cache
     * @param    boolean    $force        Force query repository
     * @return    object    Self instance
     */
   
public static function quickParse($url, $cache_dir = null, $force = false)
    {
       
$parser = new self();
        if (
$cache_dir) {
           
$parser->setCacheDir($cache_dir);
        }
        if (
$force) {
           
$parser->setForce($force);
        }

        return
$parser->parse($url);
    }

   
/**
     * Set cache directory.
     *
     * @param    string    $dir        Cache directory
     * @return    boolean    True if cache dierctory is useable
     */
   
public function setCacheDir($dir)
    {
       
$this->cache_dir = null;

        if (!empty(
$dir) && is_dir($dir) && is_writeable($dir)) {
           
$this->cache_dir = $dir;

            return
true;
        }

        return
false;
    }

   
/**
     * Set cache TTL.
     *
     * @param    string    $str        Cache TTL
     */
   
public function setCacheTTL($str)
    {
       
$str = trim((string) $str);

        if (!empty(
$str)) {
           
$this->cache_ttl = substr($str, 0, 1) == '-' ? $str : '-' . $str;
        }
    }

   
/**
     * Set force query repository.
     *
     * @param    boolean    $force    True to force query
     */
   
public function setForce($force)
    {
       
$this->force = $force;
    }

   
/**
     * Get repository XML feed URL content.
     *
     * @param    string    $url        XML feed URL
     * @return   mixed     Feed content
     */
   
protected function getModulesXML($url)
    {
        if (!
self::readURL($url, $ssl, $host, $port, $path, $user, $pass)) {
            return
false;
        }
       
$this->setHost($host, $port);
       
$this->useSSL($ssl);
       
$this->setAuthorization($user, $pass);

        try {
            return
$this->get($path);
        } catch (
Exception $e) {
           
// @todo Log error when repository query fail
           
return false;
        }
    }

   
/**
     * Get repository modules list using cache.
     *
     * @param    string    $url        XML feed URL
     * @return   mixed     Feed content or False on fail
     */
   
protected function withCache($url)
    {
       
$url_md5     = md5($url);
       
$cached_file = sprintf(
           
'%s/%s/%s/%s/%s.ser',
           
$this->cache_dir,
           
$this->cache_file_prefix,
           
substr($url_md5, 0, 2),
           
substr($url_md5, 2, 2),
           
$url_md5
       
);

       
$may_use_cached = false;

       
# Use cache file ?
       
if (@file_exists($cached_file) && !$this->force) {
           
$may_use_cached = true;
           
$ts             = @filemtime($cached_file);
            if (
$ts > strtotime($this->cache_ttl)) {
               
# Direct cache
               
return unserialize(file_get_contents($cached_file));
            }
           
$this->setValidator('IfModifiedSince', $ts);
        }

       
# Query repository
       
if (!$this->getModulesXML($url)) {
            if (
$may_use_cached) {
               
# Touch cache TTL even if query failed ?
               
if ($this->cache_touch_on_fail) {
                    @
files::touch($cached_file);
                }
               
# Connection failed - fetched from cache
               
return unserialize(file_get_contents($cached_file));
            }

            return
false;
        }

       
# Parse response
       
switch ($this->getStatus()) {
           
# Not modified, use cache
           
case '304':
                @
files::touch($cached_file);

                return
unserialize(file_get_contents($cached_file));
           
# Ok, parse feed
           
case '200':
               
$modules = new dcStoreParser($this->getContent());

                try {
                   
files::makeDir(dirname($cached_file), true);
                } catch (
Exception $e) {
                    return
$modules;
                }

                if ((
$fp = @fopen($cached_file, 'wb'))) {
                   
fwrite($fp, serialize($modules));
                   
fclose($fp);
                   
files::inheritChmod($cached_file);
                }

                return
$modules;
        }

        return
false;
    }

   
/**
     * Prepare query.
     *
     * @return    array    Query headers
     */
   
protected function buildRequest()
    {
       
$headers = parent::buildRequest();

       
# Cache validators
       
if (!empty($this->validators)) {
            if (isset(
$this->validators['IfModifiedSince'])) {
               
$headers[] = 'If-Modified-Since: ' . $this->validators['IfModifiedSince'];
            }
            if (isset(
$this->validators['IfNoneMatch'])) {
                if (
is_array($this->validators['IfNoneMatch'])) {
                   
$etags = implode(',', $this->validators['IfNoneMatch']);
                } else {
                   
$etags = $this->validators['IfNoneMatch'];
                }
               
$headers[] = '';
            }
        }

        return
$headers;
    }

   
/**
     * Tweak query cache validator.
     *
     * @param    string    $key        Validator key
     * @param    mixed     $value      Validator value
     */
   
private function setValidator($key, $value)
    {
        if (
$key == 'IfModifiedSince') {
           
$value = gmdate('D, d M Y H:i:s', $value) . ' GMT';
        }

       
$this->validators[$key] = $value;
    }
}