- reading_confirm ? ' checked="checked"' : ''; ?> />
+ reading_confirm ? ' checked="checked"' : ''; ?> data-leave-validation="reading_confirm; ?>"/>
—
@@ -118,30 +118,39 @@
+
+
diff --git a/sources/constants.php b/sources/constants.php
index 4ee9370..aa259f6 100755
--- a/sources/constants.php
+++ b/sources/constants.php
@@ -1,7 +1,6 @@
'production',
+
+ # Used to make crypto more unique. Generated during install.
'salt' => '',
+
+ # Specify address of the FreshRSS instance,
+ # used when building absolute URLs, e.g. for PubSubHubbub.
+ # Examples:
+ # https://example.net/FreshRSS/p/
+ # https://freshrss.example.net/
'base_url' => '',
+
+ # Specify address of the FreshRSS auto-update server.
+ 'auto_update_url' => 'https://update.freshrss.org',
+
+ # Natural language of the user interface, e.g. `en`, `fr`.
'language' => 'en',
+
+ # Title of this FreshRSS instance in the Web user interface.
'title' => 'FreshRSS',
+
+ # Name of the user that has administration rights.
'default_user' => '_',
+
+ # Allow or not visitors without login to see the articles
+ # of the default user.
'allow_anonymous' => false,
+
+ # Allow or not anonymous users to start the refresh process.
'allow_anonymous_refresh' => false,
+
+ # Login method:
+ # `none` is without password and shows only the default user;
+ # `form` is a conventional Web login form;
+ # `persona` is the email-based login by Mozilla;
+ # `http_auth` is an access controled by the HTTP Web server (e.g. `/FreshRSS/p/i/.htaccess` for Apache)
+ # if you use `http_auth`, remember to protect only `/FreshRSS/p/i/`,
+ # and in particular not protect `/FreshRSS/p/api/` if you would like to use the API (different login system).
'auth_type' => 'none',
+
+ # Allow or not the use of the API, used for mobile apps.
+ # End-point is http://example.net/FreshRSS/p/api/greader.php
+ # You need to set the user's API password.
'api_enabled' => false,
+
+ # Allow or not the use of an unsafe login,
+ # by providing username and password in the login URL:
+ # http://example.net/FreshRSS/p/i/?c=auth&a=login&u=alice&p=1234
'unsafe_autologin_enabled' => false,
+
+ # Enable or not the use of syslog to log the activity of
+ # SimplePie, which is retrieving RSS feeds via HTTP requests.
+ 'simplepie_syslog_enabled' => true,
+
+ # Enable or not support of PubSubHubbub.
+ # /!\ It should NOT be enabled if base_url is not reachable by an external server.
+ 'pubsubhubbub_enabled' => false,
+
+ # Allow or not Web robots (e.g. search engines) in HTML headers.
+ 'allow_robots' => false,
+
'limits' => array(
+
+ # Duration in seconds of the SimplePie cache,
+ # during which a query to the RSS feed will return the local cached version.
+ # Especially important for multi-user setups.
'cache_duration' => 800,
+
+ # SimplePie HTTP request timeout in seconds.
'timeout' => 10,
+
+ # If a user has not used FreshRSS for more than x seconds,
+ # then its feeds are not refreshed anymore.
'max_inactivity' => PHP_INT_MAX,
+
+ # Max number of feeds for a user.
'max_feeds' => 16384,
+
+ # Max number of categories for a user.
'max_categories' => 16384,
+
+ # Max number of accounts that anonymous users can create
+ # 0 for an unlimited number of accounts
+ # 1 is to not allow user registrations (1 is corresponding to the admin account)
+ 'max_registrations' => 1,
),
+
+ # Options used by cURL when making HTTP requests, e.g. when the SimplePie library retrieves feeds.
+ # http://php.net/manual/function.curl-setopt
+ 'curl_options' => array(
+ # Options to disable SSL/TLS certificate check (e.g. for self-signed HTTPS)
+ //CURLOPT_SSL_VERIFYHOST => 0,
+ //CURLOPT_SSL_VERIFYPEER => false,
+
+ # Options to use a proxy for retrieving feeds.
+ //CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
+ //CURLOPT_PROXY => '127.0.0.1',
+ //CURLOPT_PROXYPORT => 8080,
+ //CURLOPT_PROXYAUTH => CURLAUTH_BASIC,
+ //CURLOPT_PROXYUSERPWD => 'user:password',
+ ),
+
'db' => array(
+
+ # Type of database: `sqlite` or `mysql`.
'type' => 'sqlite',
- 'host' => '',
+
+ # MySQL host.
+ 'host' => 'localhost',
+
+ # MySQL user.
'user' => '',
+
+ # MySQL password.
'password' => '',
+
+ # MySQL database.
'base' => '',
- 'prefix' => '',
+
+ # MySQL table prefix.
+ 'prefix' => 'freshrss_',
+
+ 'pdo_options' => array(
+ //PDO::MYSQL_ATTR_SSL_KEY => '/path/to/client-key.pem',
+ //PDO::MYSQL_ATTR_SSL_CERT => '/path/to/client-cert.pem',
+ //PDO::MYSQL_ATTR_SSL_CA => '/path/to/ca-cert.pem',
+ ),
+
),
+
+ # List of enabled FreshRSS extensions.
'extensions_enabled' => array(),
);
diff --git a/sources/data/shares.php b/sources/data/shares.php
index 6e0e9ea..ed06007 100755
--- a/sources/data/shares.php
+++ b/sources/data/shares.php
@@ -44,6 +44,12 @@ return array(
'help' => 'https://diasporafoundation.org/',
'form' => 'advanced',
),
+ 'movim' => array(
+ 'url' => '~URL~/index.php/share/~LINK~',
+ 'transform' => array('rawurlencode', 'urlencode'),
+ 'help' => 'https://github.com/edhelas/movim',
+ 'form' => 'advanced',
+ ),
'twitter' => array(
'url' => 'https://twitter.com/share?url=~LINK~&text=~TITLE~',
'transform' => array('rawurlencode'),
diff --git a/sources/data/users/_/config.default.php b/sources/data/users/_/config.default.php
index 6d3f73a..8f8ff52 100755
--- a/sources/data/users/_/config.default.php
+++ b/sources/data/users/_/config.default.php
@@ -22,6 +22,11 @@ return array (
'sticky_post' => true,
'reading_confirm' => false,
'auto_remove_article' => false,
+
+ # In the case an article has changed (e.g. updated content):
+ # Set to `true` to mark it unread, or `false` to leave it as-is.
+ 'mark_updated_article_unread' => false, //TODO: -1 => ignore, 0 => update, 1 => update and mark as unread
+
'sort_order' => 'DESC',
'anon_access' => false,
'mark_when' => array (
diff --git a/sources/index.html b/sources/index.html
index 6ac0259..5414211 100755
--- a/sources/index.html
+++ b/sources/index.html
@@ -4,7 +4,7 @@
Redirection
-
+
diff --git a/sources/lib/Minz/Configuration.php b/sources/lib/Minz/Configuration.php
index ab5bb4f..d695d4a 100755
--- a/sources/lib/Minz/Configuration.php
+++ b/sources/lib/Minz/Configuration.php
@@ -39,7 +39,7 @@ class Minz_Configuration {
throw new Minz_FileNotExistException($filename);
}
- $data = @include($filename);
+ $data = include($filename);
if (is_array($data)) {
return $data;
} else {
@@ -84,11 +84,6 @@ class Minz_Configuration {
*/
private $data = array();
- /**
- * The default values, an empty array by default.
- */
- private $data_default = array();
-
/**
* An object which help to set good values in configuration.
*/
@@ -119,21 +114,22 @@ class Minz_Configuration {
$configuration_setter = null) {
$this->namespace = $namespace;
$this->config_filename = $config_filename;
+ $this->default_filename = $default_filename;
+ $this->_configurationSetter($configuration_setter);
+
+ if (!is_null($this->default_filename)) {
+ $this->data = self::load($this->default_filename);
+ }
try {
- $this->data = self::load($this->config_filename);
+ $this->data = array_replace_recursive(
+ $this->data, self::load($this->config_filename)
+ );
} catch (Minz_FileNotExistException $e) {
- if (is_null($default_filename)) {
+ if (is_null($this->default_filename)) {
throw $e;
}
}
-
- $this->default_filename = $default_filename;
- if (!is_null($this->default_filename)) {
- $this->data_default = self::load($this->default_filename);
- }
-
- $this->_configurationSetter($configuration_setter);
}
/**
@@ -160,8 +156,6 @@ class Minz_Configuration {
return $this->data[$key];
} elseif (!is_null($default)) {
return $default;
- } elseif (isset($this->data_default[$key])) {
- return $this->data_default[$key];
} else {
Minz_Log::warning($key . ' does not exist in configuration');
return null;
diff --git a/sources/lib/Minz/Extension.php b/sources/lib/Minz/Extension.php
index d7ee8fe..78b8a27 100755
--- a/sources/lib/Minz/Extension.php
+++ b/sources/lib/Minz/Extension.php
@@ -168,7 +168,7 @@ class Minz_Extension {
$url = '/ext.php?f=' . $file_name_url .
'&t=' . $type .
'&' . $mtime;
- return Minz_Url::display($url);
+ return Minz_Url::display($url, 'php');
}
/**
diff --git a/sources/lib/Minz/ModelPdo.php b/sources/lib/Minz/ModelPdo.php
index ac7a1be..25999f0 100755
--- a/sources/lib/Minz/ModelPdo.php
+++ b/sources/lib/Minz/ModelPdo.php
@@ -53,21 +53,19 @@ class Minz_ModelPdo {
$this->current_user = $currentUser;
self::$sharedCurrentUser = $currentUser;
+ $driver_options = isset($conf->db['pdo_options']) && is_array($conf->db['pdo_options']) ? $conf->db['pdo_options'] : array();
+
try {
$type = $db['type'];
if ($type === 'mysql') {
$string = 'mysql:host=' . $db['host']
. ';dbname=' . $db['base']
. ';charset=utf8';
- $driver_options = array(
- PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
- );
+ $driver_options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES utf8';
$this->prefix = $db['prefix'] . $currentUser . '_';
} elseif ($type === 'sqlite') {
$string = 'sqlite:' . join_path(DATA_PATH, 'users', $currentUser, 'db.sqlite');
- $driver_options = array(
- //PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
- );
+ //$driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
$this->prefix = '';
} else {
throw new Minz_PDOConnectionException(
@@ -134,4 +132,9 @@ class MinzPDO extends PDO {
MinzPDO::check($statement);
return parent::exec($statement);
}
+
+ public function query($statement) {
+ MinzPDO::check($statement);
+ return parent::query($statement);
+ }
}
diff --git a/sources/lib/Minz/Request.php b/sources/lib/Minz/Request.php
index 6db2e9c..effb994 100755
--- a/sources/lib/Minz/Request.php
+++ b/sources/lib/Minz/Request.php
@@ -85,44 +85,58 @@ class Minz_Request {
}
/**
- * Retourn le nom de domaine du site
+ * Try to guess the base URL from $_SERVER information
+ *
+ * @return the base url (e.g. http://example.com/)
*/
- public static function getDomainName() {
- return $_SERVER['HTTP_HOST'];
- }
+ public static function guessBaseUrl() {
+ $url = 'http';
- /**
- * Détermine la base de l'url
- * @return la base de l'url
- */
- public static function getBaseUrl() {
- $conf = Minz_Configuration::get('system');
- $defaultBaseUrl = $conf->base_url;
- if (!empty($defaultBaseUrl)) {
- return $defaultBaseUrl;
- } elseif (isset($_SERVER['REQUEST_URI'])) {
- return dirname($_SERVER['REQUEST_URI']) . '/';
+ if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+ $https = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https';
} else {
- return '/';
+ $https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';
}
- }
- /**
- * Récupère l'URI de la requête
- * @return l'URI
- */
- public static function getURI() {
+ if (!empty($_SERVER['HTTP_HOST'])) {
+ $host = $_SERVER['HTTP_HOST'];
+ } elseif (!empty($_SERVER['SERVER_NAME'])) {
+ $host = $_SERVER['SERVER_NAME'];
+ } else {
+ $host = 'localhost';
+ }
+
+ if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
+ $port = intval($_SERVER['HTTP_X_FORWARDED_PORT']);
+ } elseif (!empty($_SERVER['SERVER_PORT'])) {
+ $port = intval($_SERVER['SERVER_PORT']);
+ } else {
+ $port = $https ? 443 : 80;
+ }
+
+ if ($https) {
+ $url .= 's://' . $host . ($port == 443 ? '' : ':' . $port);
+ } else {
+ $url .= '://' . $host . ($port == 80 ? '' : ':' . $port);
+ }
if (isset($_SERVER['REQUEST_URI'])) {
- $base_url = self::getBaseUrl();
- $uri = $_SERVER['REQUEST_URI'];
-
- $len_base_url = strlen($base_url);
- $real_uri = substr($uri, $len_base_url);
- } else {
- $real_uri = '';
+ $path = $_SERVER['REQUEST_URI'];
+ $url .= substr($path, -1) === '/' ? substr($path, 0, -1) : dirname($path);
}
- return $real_uri;
+ return filter_var($url, FILTER_SANITIZE_URL);
+ }
+
+ /**
+ * Return the base_url from configuration and add a suffix if given.
+ *
+ * @param $base_url_suffix a string to add at base_url (default: empty string)
+ * @return the base_url with a suffix.
+ */
+ public static function getBaseUrl($base_url_suffix = '') {
+ $conf = Minz_Configuration::get('system');
+ $url = rtrim($conf->base_url, '/\\') . $base_url_suffix;
+ return filter_var($url, FILTER_SANITIZE_URL);
}
/**
diff --git a/sources/lib/Minz/Session.php b/sources/lib/Minz/Session.php
index 058685a..057e774 100755
--- a/sources/lib/Minz/Session.php
+++ b/sources/lib/Minz/Session.php
@@ -66,8 +66,10 @@ class Minz_Session {
*/
public static function keepCookie($l) {
// Get the script_name (e.g. /p/i/index.php) and keep only the path.
- $cookie_dir = empty($_SERVER['SCRIPT_NAME']) ? '' : $_SERVER['SCRIPT_NAME'];
- $cookie_dir = dirname($cookie_dir);
+ $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '/' : $_SERVER['REQUEST_URI'];
+ if (substr($cookie_dir, -1) !== '/') {
+ $cookie_dir = dirname($cookie_dir) . '/';
+ }
session_set_cookie_params($l, $cookie_dir, '', false, true);
}
diff --git a/sources/lib/Minz/Url.php b/sources/lib/Minz/Url.php
index af555a2..4279b04 100755
--- a/sources/lib/Minz/Url.php
+++ b/sources/lib/Minz/Url.php
@@ -10,7 +10,6 @@ class Minz_Url {
* $url['c'] = controller
* $url['a'] = action
* $url['params'] = tableau des paramètres supplémentaires
- * $url['protocol'] = protocole à utiliser (http par défaut)
* ou comme une chaîne de caractère
* @param $encodage pour indiquer comment encoder les & (& ou & pour html)
* @return l'url formatée
@@ -19,26 +18,24 @@ class Minz_Url {
$isArray = is_array($url);
if ($isArray) {
- $url = self::checkUrl ($url);
+ $url = self::checkUrl($url);
}
$url_string = '';
if ($absolute) {
- if ($isArray && isset ($url['protocol'])) {
- $protocol = $url['protocol'];
- } elseif (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
- $protocol = 'https:';
- } else {
- $protocol = 'http:';
+ $url_string = Minz_Request::getBaseUrl(PUBLIC_TO_INDEX_PATH);
+ if ($url_string === PUBLIC_TO_INDEX_PATH) {
+ $url_string = Minz_Request::guessBaseUrl();
}
- $url_string = $protocol . '//' . Minz_Request::getDomainName () . Minz_Request::getBaseUrl ();
} else {
$url_string = $isArray ? '.' : PUBLIC_RELATIVE;
}
if ($isArray) {
- $url_string .= self::printUri ($url, $encodage);
+ $url_string .= self::printUri($url, $encodage);
+ } elseif ($encodage === 'html') {
+ $url_string = Minz_Helper::htmlspecialchars_utf8($url_string . $url);
} else {
$url_string .= $url;
}
diff --git a/sources/lib/Minz/View.php b/sources/lib/Minz/View.php
index ff5cce4..8c5230a 100755
--- a/sources/lib/Minz/View.php
+++ b/sources/lib/Minz/View.php
@@ -91,6 +91,7 @@ class Minz_View {
* Construit le layout
*/
public function buildLayout () {
+ header('Content-Type: text/html; charset=UTF-8');
$this->includeFile(self::LAYOUT_PATH_NAME . self::LAYOUT_FILENAME);
}
diff --git a/sources/lib/SimplePie/SimplePie.php b/sources/lib/SimplePie/SimplePie.php
index c4872b5..6c0962a 100755
--- a/sources/lib/SimplePie/SimplePie.php
+++ b/sources/lib/SimplePie/SimplePie.php
@@ -74,6 +74,12 @@ define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed
*/
define('SIMPLEPIE_LINKBACK', '
' . SIMPLEPIE_NAME . ' ');
+/**
+ * Use syslog to report HTTP requests done by SimplePie.
+ * @see SimplePie::set_syslog()
+ */
+define('SIMPLEPIE_SYSLOG', true); //FreshRSS
+
/**
* No Autodiscovery
* @see SimplePie::set_autodiscovery_level()
@@ -450,7 +456,7 @@ class SimplePie
* @see SimplePie::subscribe_url()
* @access private
*/
- public $permanent_url = null; //FreshRSS
+ public $permanent_url = null;
/**
* @var object Instance of SimplePie_File to use as a feed
@@ -473,6 +479,13 @@ class SimplePie
*/
public $timeout = 10;
+ /**
+ * @var array Custom curl options
+ * @see SimplePie::set_curl_options()
+ * @access private
+ */
+ public $curl_options = array();
+
/**
* @var bool Forces fsockopen() to be used for remote files instead
* of cURL, even if a new enough version is installed
@@ -622,6 +635,12 @@ class SimplePie
*/
public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
+ /**
+ * Use syslog to report HTTP requests done by SimplePie.
+ * @see SimplePie::set_syslog()
+ */
+ public $syslog_enabled = SIMPLEPIE_SYSLOG;
+
/**
* The SimplePie class contains feed level data and options
*
@@ -742,7 +761,7 @@ class SimplePie
else
{
$this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1));
- $this->permanent_url = $this->feed_url; //FreshRSS
+ $this->permanent_url = $this->feed_url;
}
}
@@ -757,7 +776,7 @@ class SimplePie
if ($file instanceof SimplePie_File)
{
$this->feed_url = $file->url;
- $this->permanent_url = $this->feed_url; //FreshRSS
+ $this->permanent_url = $this->feed_url;
$this->file =& $file;
return true;
}
@@ -795,6 +814,19 @@ class SimplePie
{
$this->timeout = (int) $timeout;
}
+
+ /**
+ * Set custom curl options
+ *
+ * This allows you to change default curl options
+ *
+ * @since 1.0 Beta 3
+ * @param array $curl_options Curl options to add to default settings
+ */
+ public function set_curl_options(array $curl_options = array())
+ {
+ $this->curl_options = $curl_options;
+ }
/**
* Force SimplePie to use fsockopen() instead of cURL
@@ -1136,7 +1168,7 @@ class SimplePie
$this->sanitize->strip_attributes($attribs);
}
- public function add_attributes($attribs = '')
+ public function add_attributes($attribs = '') //FreshRSS
{
if ($attribs === '')
{
@@ -1145,6 +1177,14 @@ class SimplePie
$this->sanitize->add_attributes($attribs);
}
+ /**
+ * Use syslog to report HTTP requests done by SimplePie.
+ */
+ public function set_syslog($value = SIMPLEPIE_SYSLOG) //FreshRSS
+ {
+ $this->syslog_enabled = $value == true;
+ }
+
/**
* Set the output encoding
*
@@ -1231,7 +1271,8 @@ class SimplePie
$this->enable_exceptions = $enable;
}
- function cleanMd5($rss) { //FreshRSS
+ function cleanMd5($rss)
+ {
return md5(preg_replace(array('#<(lastBuildDate|pubDate|updated|feedDate|dc:date|slash:comments)>[^<]+\\1>#', '##s'), '', $rss));
}
@@ -1276,7 +1317,7 @@ class SimplePie
// Pass whatever was set with config options over to the sanitizer.
// Pass the classes in for legacy support; new classes should use the registry instead
$this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache'));
- $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen);
+ $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options);
if (!empty($this->multifeed_url))
{
@@ -1321,7 +1362,7 @@ class SimplePie
// Fetch the data via SimplePie_File into $this->raw_data
if (($fetched = $this->fetch_data($cache)) === true)
{
- return $this->data['mtime']; //FreshRSS
+ return $this->data['mtime'];
}
elseif ($fetched === false) {
return false;
@@ -1329,7 +1370,8 @@ class SimplePie
list($headers, $sniffed) = $fetched;
- if (isset($this->data['md5'])) { //FreshRSS
+ if (isset($this->data['md5']))
+ {
$md5 = $this->data['md5'];
}
}
@@ -1413,8 +1455,8 @@ class SimplePie
$this->data['headers'] = $headers;
}
$this->data['build'] = SIMPLEPIE_BUILD;
- $this->data['mtime'] = time(); //FreshRSS
- $this->data['md5'] = empty($md5) ? $this->cleanMd5($this->raw_data) : $md5; //FreshRSS
+ $this->data['mtime'] = time();
+ $this->data['md5'] = empty($md5) ? $this->cleanMd5($this->raw_data) : $md5;
// Cache the file if caching is enabled
if ($cache && !$cache->save($this))
@@ -1429,7 +1471,7 @@ class SimplePie
if (isset($parser))
{
// We have an error, just set SimplePie_Misc::error to it and quit
- $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column());
+ $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d, encoding %s, URL: %s', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column(), $encoding, $this->feed_url);
}
else
{
@@ -1455,7 +1497,8 @@ class SimplePie
{
// Load the Cache
$this->data = $cache->load();
- if ($cache->mtime() + $this->cache_duration > time()) { //FreshRSS
+ if ($cache->mtime() + $this->cache_duration > time())
+ {
$this->raw_data = false;
return true; // If the cache is still valid, just return true
}
@@ -1491,65 +1534,58 @@ class SimplePie
}
}
// Check if the cache has been updated
- else //if ($cache->mtime() + $this->cache_duration < time()) //FreshRSS removed
+ else
{
- // If we have last-modified and/or etag set
- //if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) //FreshRSS removed
+ $headers = array(
+ 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
+ );
+ if (isset($this->data['headers']['last-modified']))
{
- $headers = array(
- 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
- );
- if (isset($this->data['headers']['last-modified']))
- {
- $headers['if-modified-since'] = $this->data['headers']['last-modified'];
- }
- if (isset($this->data['headers']['etag']))
- {
- $headers['if-none-match'] = $this->data['headers']['etag'];
- }
+ $headers['if-modified-since'] = $this->data['headers']['last-modified'];
+ }
+ if (isset($this->data['headers']['etag']))
+ {
+ $headers['if-none-match'] = $this->data['headers']['etag'];
+ }
- $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen)); //FreshRSS
+ $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
- if ($file->success)
+ if ($file->success)
+ {
+ if ($file->status_code === 304)
{
- if ($file->status_code === 304)
- {
- $cache->touch();
- return true;
- }
- }
- else
- {
- $cache->touch(); //FreshRSS
- $this->error = $file->error; //FreshRSS
- return !empty($this->data); //FreshRSS
- //unset($file); //FreshRSS removed
+ $cache->touch();
+ return true;
}
}
- { //FreshRSS
- $md5 = $this->cleanMd5($file->body);
- if ($this->data['md5'] === $md5) {
- // syslog(LOG_DEBUG, 'SimplePie MD5 cache match for ' . $this->feed_url);
- $cache->touch();
- return true; //Content unchanged even though server did not send a 304
- } else {
- // syslog(LOG_DEBUG, 'SimplePie MD5 cache no match for ' . $this->feed_url);
- $this->data['md5'] = $md5;
+ else
+ {
+ $cache->touch();
+ $this->error = $file->error;
+ return !empty($this->data);
+ }
+
+ $md5 = $this->cleanMd5($file->body);
+ if ($this->data['md5'] === $md5) {
+ if ($this->syslog_enabled)
+ {
+ syslog(LOG_DEBUG, 'SimplePie MD5 cache match for ' . SimplePie_Misc::url_remove_credentials($this->feed_url));
}
+ $cache->touch();
+ return true; //Content unchanged even though server did not send a 304
+ } else {
+ if ($this->syslog_enabled)
+ {
+ syslog(LOG_DEBUG, 'SimplePie MD5 cache no match for ' . SimplePie_Misc::url_remove_credentials($this->feed_url));
+ }
+ $this->data['md5'] = $md5;
}
}
- //// If the cache is still valid, just return true
- //else //FreshRSS removed
- //{
- // $this->raw_data = false;
- // return true;
- //}
}
- // If the cache is empty, delete it
+ // If the cache is empty
else
{
- //$cache->unlink(); //FreshRSS removed
- $cache->touch(); //FreshRSS
+ $cache->touch(); //To keep the date/time of the last tentative update
$this->data = array();
}
}
@@ -1565,7 +1601,7 @@ class SimplePie
$headers = array(
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
);
- $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen));
+ $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
}
}
// If the file connection has an error, set SimplePie::error to that and quit
@@ -1582,15 +1618,15 @@ class SimplePie
if (!$locate->is_feed($file))
{
- $copyStatusCode = $file->status_code; //FreshRSS
- $copyContentType = $file->headers['content-type']; //FreshRSS
+ $copyStatusCode = $file->status_code;
+ $copyContentType = $file->headers['content-type'];
// We need to unset this so that if SimplePie::set_file() has been called that object is untouched
unset($file);
try
{
if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds)))
{
- $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`"; //FreshRSS
+ $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`";
$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
return false;
}
@@ -1605,8 +1641,8 @@ class SimplePie
if ($cache)
{
$this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
- $this->data['mtime'] = time(); //FreshRSS
- $this->data['md5'] = empty($md5) ? $this->cleanMd5($file->body) : $md5; //FreshRSS
+ $this->data['mtime'] = time();
+ $this->data['md5'] = empty($md5) ? $this->cleanMd5($file->body) : $md5;
if (!$cache->save($this))
{
trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
@@ -1619,7 +1655,7 @@ class SimplePie
}
$this->raw_data = $file->body;
- $this->permanent_url = $file->permanent_url; //FreshRSS
+ $this->permanent_url = $file->permanent_url;
$headers = $file->headers;
$sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file));
$sniffed = $sniffer->get_type();
@@ -1823,7 +1859,7 @@ class SimplePie
*/
public function subscribe_url($permanent = false)
{
- if ($permanent) //FreshRSS
+ if ($permanent)
{
if ($this->permanent_url !== null)
{
diff --git a/sources/lib/SimplePie/SimplePie/Cache/File.php b/sources/lib/SimplePie/SimplePie/Cache/File.php
index cb4b528..72e75a4 100755
--- a/sources/lib/SimplePie/SimplePie/Cache/File.php
+++ b/sources/lib/SimplePie/SimplePie/Cache/File.php
@@ -136,11 +136,7 @@ class SimplePie_Cache_File implements SimplePie_Cache_Base
*/
public function mtime()
{
- //if (file_exists($this->name)) //FreshRSS removed
- {
- return @filemtime($this->name); //FreshRSS
- }
- //return false; //FreshRSS removed
+ return @filemtime($this->name);
}
/**
@@ -150,11 +146,7 @@ class SimplePie_Cache_File implements SimplePie_Cache_Base
*/
public function touch()
{
- //if (file_exists($this->name)) //FreshRSS removed
- {
- return @touch($this->name); //FreshRSS
- }
- //return false; //FreshRSS removed
+ return @touch($this->name);
}
/**
diff --git a/sources/lib/SimplePie/SimplePie/Decode/HTML/Entities.php b/sources/lib/SimplePie/SimplePie/Decode/HTML/Entities.php
index cde06c8..46b3a1d 100755
--- a/sources/lib/SimplePie/SimplePie/Decode/HTML/Entities.php
+++ b/sources/lib/SimplePie/SimplePie/Decode/HTML/Entities.php
@@ -169,7 +169,6 @@ class SimplePie_Decode_HTML_Entities
case "\x09":
case "\x0A":
case "\x0B":
- case "\x0B":
case "\x0C":
case "\x20":
case "\x3C":
diff --git a/sources/lib/SimplePie/SimplePie/File.php b/sources/lib/SimplePie/SimplePie/File.php
index 9625af2..45994d1 100755
--- a/sources/lib/SimplePie/SimplePie/File.php
+++ b/sources/lib/SimplePie/SimplePie/File.php
@@ -66,7 +66,7 @@ class SimplePie_File
var $method = SIMPLEPIE_FILE_SOURCE_NONE;
var $permanent_url; //FreshRSS
- public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false)
+ public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $curl_options = array(), $syslog_enabled = SIMPLEPIE_SYSLOG)
{
if (class_exists('idna_convert'))
{
@@ -75,11 +75,14 @@ class SimplePie_File
$url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
}
$this->url = $url;
- $this->permanent_url = $url; //FreshRSS
+ $this->permanent_url = $url;
$this->useragent = $useragent;
if (preg_match('/^http(s)?:\/\//i', $url))
{
- // syslog(LOG_INFO, 'SimplePie GET ' . $url); //FreshRSS
+ if ($syslog_enabled)
+ {
+ syslog(LOG_INFO, 'SimplePie GET ' . SimplePie_Misc::url_remove_credentials($url)); //FreshRSS
+ }
if ($useragent === null)
{
$useragent = ini_get('user_agent');
@@ -110,12 +113,15 @@ class SimplePie_File
curl_setopt($fp, CURLOPT_REFERER, $url);
curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
- curl_setopt($fp, CURLOPT_SSL_VERIFYPEER, false); //FreshRSS
if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
{
curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
}
+ foreach ($curl_options as $curl_param => $curl_value)
+ {
+ curl_setopt($fp, $curl_param, $curl_value);
+ }
$this->headers = curl_exec($fp);
if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
@@ -146,7 +152,7 @@ class SimplePie_File
$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
$previousStatusCode = $this->status_code;
$this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
- $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; //FreshRSS
+ $this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
return;
}
}
diff --git a/sources/lib/SimplePie/SimplePie/Item.php b/sources/lib/SimplePie/SimplePie/Item.php
index 7bd96c1..27e9345 100755
--- a/sources/lib/SimplePie/SimplePie/Item.php
+++ b/sources/lib/SimplePie/SimplePie/Item.php
@@ -406,6 +406,30 @@ class SimplePie_Item
return null;
}
}
+
+ /**
+ * Get the media:thumbnail of the item
+ *
+ * Uses `
`
+ *
+ *
+ * @return array|null
+ */
+ public function get_thumbnail()
+ {
+ if (!isset($this->data['thumbnail']))
+ {
+ if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
+ {
+ $this->data['thumbnail'] = $return[0]['attribs'][''];
+ }
+ else
+ {
+ $this->data['thumbnail'] = null;
+ }
+ }
+ return $this->data['thumbnail'];
+ }
/**
* Get a category for the item
@@ -738,6 +762,18 @@ class SimplePie_Item
{
$this->data['date']['raw'] = $return[0]['data'];
}
+ elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
+ {
+ $this->data['date']['raw'] = $return[0]['data'];
+ }
+ elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
+ {
+ $this->data['date']['raw'] = $return[0]['data'];
+ }
+ elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
+ {
+ $this->data['date']['raw'] = $return[0]['data'];
+ }
elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
{
$this->data['date']['raw'] = $return[0]['data'];
@@ -754,18 +790,6 @@ class SimplePie_Item
{
$this->data['date']['raw'] = $return[0]['data'];
}
- elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
- {
- $this->data['date']['raw'] = $return[0]['data'];
- }
- elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
- {
- $this->data['date']['raw'] = $return[0]['data'];
- }
- elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
- {
- $this->data['date']['raw'] = $return[0]['data'];
- }
if (!empty($this->data['date']['raw']))
{
@@ -2733,7 +2757,9 @@ class SimplePie_Item
{
foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
{
- $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
+ if (isset($thumbnail['attribs']['']['url'])) {
+ $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
+ }
}
if (is_array($thumbnails))
{
diff --git a/sources/lib/SimplePie/SimplePie/Locator.php b/sources/lib/SimplePie/SimplePie/Locator.php
index 4e5f7c1..ba4a843 100755
--- a/sources/lib/SimplePie/SimplePie/Locator.php
+++ b/sources/lib/SimplePie/SimplePie/Locator.php
@@ -148,7 +148,7 @@ class SimplePie_Locator
{
$sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
$sniffed = $sniffer->get_type();
- if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml', 'application/x-rss+xml'))) //FreshRSS
+ if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml', 'application/x-rss+xml')))
{
return true;
}
diff --git a/sources/lib/SimplePie/SimplePie/Misc.php b/sources/lib/SimplePie/SimplePie/Misc.php
index 5a263a2..9e7ac4f 100755
--- a/sources/lib/SimplePie/SimplePie/Misc.php
+++ b/sources/lib/SimplePie/SimplePie/Misc.php
@@ -79,8 +79,8 @@ class SimplePie_Misc
public static function absolutize_url($relative, $base)
{
- if (substr($relative, 0, 2) === '//') //FreshRSS: disable absolutize_url for "//www.example.net" which will pick HTTP or HTTPS automatically
- {
+ if (substr($relative, 0, 2) === '//')
+ {//Allow protocol-relative URLs "//www.example.net" which will pick HTTP or HTTPS automatically
return $relative;
}
$iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
@@ -128,7 +128,7 @@ class SimplePie_Misc
{
$attribs[$j][2] = $attribs[$j][1];
}
- $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); //FreshRSS
+ $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8');
}
}
}
@@ -142,7 +142,7 @@ class SimplePie_Misc
foreach ($element['attribs'] as $key => $value)
{
$key = strtolower($key);
- $full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"'; //FreshRSS
+ $full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"';
}
if ($element['self_closing'])
{
@@ -2240,5 +2240,15 @@ function embed_wmedia(width, height, link) {
{
// No-op
}
+
+ /**
+ * Sanitize a URL by removing HTTP credentials.
+ * @param $url the URL to sanitize.
+ * @return the same URL without HTTP credentials.
+ */
+ public static function url_remove_credentials($url) //FreshRSS
+ {
+ return preg_replace('#^(https?://)[^/:@]+:[^/:@]+@#i', '$1', $url);
+ }
}
diff --git a/sources/lib/SimplePie/SimplePie/Parse/Date.php b/sources/lib/SimplePie/SimplePie/Parse/Date.php
index ba7c070..50bb5cf 100755
--- a/sources/lib/SimplePie/SimplePie/Parse/Date.php
+++ b/sources/lib/SimplePie/SimplePie/Parse/Date.php
@@ -173,7 +173,7 @@ class SimplePie_Parse_Date
'aug' => 8,
'august' => 8,
'sep' => 9,
- 'september' => 8,
+ 'september' => 9,
'oct' => 10,
'october' => 10,
'nov' => 11,
@@ -331,8 +331,8 @@ class SimplePie_Parse_Date
'CCT' => 23400,
'CDT' => -18000,
'CEDT' => 7200,
- 'CEST' => 7200, //FreshRSS
'CET' => 3600,
+ 'CEST' => 7200,
'CGST' => -7200,
'CGT' => -10800,
'CHADT' => 49500,
@@ -721,7 +721,7 @@ class SimplePie_Parse_Date
{
$output .= substr($string, $position, $pos - $position);
$position = $pos + 1;
- if ($string[$pos - 1] !== '\\')
+ if ($pos === 0 || $string[$pos - 1] !== '\\')
{
$depth++;
while ($depth && $position < $length)
diff --git a/sources/lib/SimplePie/SimplePie/Registry.php b/sources/lib/SimplePie/SimplePie/Registry.php
index bd9c1f5..dac55e3 100755
--- a/sources/lib/SimplePie/SimplePie/Registry.php
+++ b/sources/lib/SimplePie/SimplePie/Registry.php
@@ -113,7 +113,7 @@ class SimplePie_Registry
*/
public function register($type, $class, $legacy = false)
{
- if (!is_subclass_of($class, $this->default[$type]))
+ if (!@is_subclass_of($class, $this->default[$type]))
{
return false;
}
@@ -222,4 +222,4 @@ class SimplePie_Registry
$result = call_user_func_array(array($class, $method), $parameters);
return $result;
}
-}
\ No newline at end of file
+}
diff --git a/sources/lib/SimplePie/SimplePie/Sanitize.php b/sources/lib/SimplePie/SimplePie/Sanitize.php
index 168a5e2..a6863ec 100755
--- a/sources/lib/SimplePie/SimplePie/Sanitize.php
+++ b/sources/lib/SimplePie/SimplePie/Sanitize.php
@@ -249,6 +249,7 @@ class SimplePie_Sanitize
{
if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
{
+ $data = htmlspecialchars_decode($data, ENT_QUOTES);
if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data))
{
$type |= SIMPLEPIE_CONSTRUCT_HTML;
@@ -279,7 +280,7 @@ class SimplePie_Sanitize
$document->loadHTML($data);
restore_error_handler();
- $xpath = new DOMXPath($document); //FreshRSS
+ $xpath = new DOMXPath($document);
// Strip comments
if ($this->strip_comments)
diff --git a/sources/lib/lib_rss.php b/sources/lib/lib_rss.php
index e5fe730..2a23fca 100755
--- a/sources/lib/lib_rss.php
+++ b/sources/lib/lib_rss.php
@@ -38,7 +38,7 @@ function classAutoloader($class) {
include(APP_PATH . '/Models/' . $components[1] . '.php');
return;
case 3: //Controllers, Exceptions
- @include(APP_PATH . '/' . $components[2] . 's/' . $components[1] . $components[2] . '.php');
+ include(APP_PATH . '/' . $components[2] . 's/' . $components[1] . $components[2] . '.php');
return;
}
} elseif (strpos($class, 'Minz') === 0) {
@@ -51,6 +51,21 @@ function classAutoloader($class) {
spl_autoload_register('classAutoloader');
//
+function idn_to_puny($url) {
+ if (function_exists('idn_to_ascii')) {
+ $parts = parse_url($url);
+ if (!empty($parts['host'])) {
+ $idn = $parts['host'];
+ $puny = idn_to_ascii($idn);
+ $pos = strpos($url, $idn);
+ if ($pos !== false) {
+ return substr_replace($url, $puny, $pos, strlen($idn));
+ }
+ }
+ }
+ return $url;
+}
+
function checkUrl($url) {
if (empty ($url)) {
return '';
@@ -58,6 +73,7 @@ function checkUrl($url) {
if (!preg_match ('#^https?://#i', $url)) {
$url = 'http://' . $url;
}
+ $url = idn_to_puny($url); //PHP bug #53474 IDN
if (filter_var($url, FILTER_VALIDATE_URL) ||
(version_compare(PHP_VERSION, '5.3.3', '<') && (strpos($url, '-') > 0) && //PHP bug #51192
($url === filter_var($url, FILTER_SANITIZE_URL)))) {
@@ -67,6 +83,33 @@ function checkUrl($url) {
}
}
+
+/**
+ * Test if a given server address is publicly accessible.
+ *
+ * Note: for the moment it tests only if address is corresponding to a
+ * localhost address.
+ *
+ * @param $address the address to test, can be an IP or a URL.
+ * @return true if server is accessible, false else.
+ * @todo improve test with a more valid technique (e.g. test with an external server?)
+ */
+function server_is_public($address) {
+ $host = parse_url($address, PHP_URL_HOST);
+
+ $is_public = !in_array($host, array(
+ '127.0.0.1',
+ 'localhost',
+ 'localhost.localdomain',
+ '[::1]',
+ 'localhost6',
+ 'localhost6.localdomain6',
+ ));
+
+ return $is_public;
+}
+
+
function format_number($n, $precision = 0) {
// number_format does not seem to be Unicode-compatible
return str_replace(' ', ' ', //Espace fine insécable
@@ -123,9 +166,11 @@ function customSimplePie() {
$limits = $system_conf->limits;
$simplePie = new SimplePie();
$simplePie->set_useragent(_t('gen.freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION);
+ $simplePie->set_syslog($system_conf->simplepie_syslog_enabled);
$simplePie->set_cache_location(CACHE_PATH);
$simplePie->set_cache_duration($limits['cache_duration']);
$simplePie->set_timeout($limits['timeout']);
+ $simplePie->set_curl_options($system_conf->curl_options);
$simplePie->strip_htmltags(array(
'base', 'blink', 'body', 'doctype', 'embed',
'font', 'form', 'frame', 'frameset', 'html',
@@ -178,17 +223,27 @@ function sanitizeHTML($data, $base = '') {
/* permet de récupérer le contenu d'un article pour un flux qui n'est pas complet */
function get_content_by_parsing ($url, $path) {
- require_once (LIB_PATH . '/lib_phpQuery.php');
+ require_once(LIB_PATH . '/lib_phpQuery.php');
- Minz_Log::notice('FreshRSS GET ' . url_remove_credentials($url));
- $html = file_get_contents ($url);
+ Minz_Log::notice('FreshRSS GET ' . SimplePie_Misc::url_remove_credentials($url));
+ $html = file_get_contents($url);
if ($html) {
- $doc = phpQuery::newDocument ($html);
- $content = $doc->find ($path);
+ $doc = phpQuery::newDocument($html);
+ $content = $doc->find($path);
+
+ foreach (pq('img[data-src]') as $img) {
+ $imgP = pq($img);
+ $dataSrc = $imgP->attr('data-src');
+ if (strlen($dataSrc) > 4) {
+ $imgP->attr('src', $dataSrc);
+ $imgP->removeAttr('data-src');
+ }
+ }
+
return sanitizeHTML($content->__toString(), $url);
} else {
- throw new Exception ();
+ throw new Exception();
}
}
@@ -238,6 +293,22 @@ function listUsers() {
}
+/**
+ * Return if the maximum number of registrations has been reached.
+ *
+ * Note a max_regstrations of 0 means there is no limit.
+ *
+ * @return true if number of users >= max registrations, false else.
+ */
+function max_registrations_reached() {
+ $system_conf = Minz_Configuration::get('system');
+ $limit_registrations = $system_conf->limits['max_registrations'];
+ $number_accounts = count(listUsers());
+
+ return $limit_registrations > 0 && $number_accounts >= $limit_registrations;
+}
+
+
/**
* Register and return the configuration for a given user.
*
@@ -430,12 +501,11 @@ function array_remove(&$array, $value) {
$array = array_diff($array, array($value));
}
-
-/**
- * Sanitize a URL by removing HTTP credentials.
- * @param $url the URL to sanitize.
- * @return the same URL without HTTP credentials.
- */
-function url_remove_credentials($url) {
- return preg_replace('/[^\/]*:[^:]*@/', '', $url);
+//RFC 4648
+function base64url_encode($data) {
+ return strtr(rtrim(base64_encode($data), '='), '+/', '-_');
+}
+//RFC 4648
+function base64url_decode($data) {
+ return base64_decode(strtr($data, '-_', '+/'));
}
diff --git a/sources/p/api/greader.php b/sources/p/api/greader.php
index ab1a022..b9942f0 100755
--- a/sources/p/api/greader.php
+++ b/sources/p/api/greader.php
@@ -77,7 +77,7 @@ class MyPDO extends Minz_ModelPdo {
}
function logMe($text) {
- file_put_contents(join_path(USERS_PATH, '_', 'log_api.txt'), $text, FILE_APPEND);
+ file_put_contents(join_path(USERS_PATH, '_', 'log_api.txt'), date('c') . "\t" . $text . "\n", FILE_APPEND);
}
function debugInfo() {
@@ -96,7 +96,7 @@ function debugInfo() {
}
function badRequest() {
- logMe("badRequest()\n");
+ logMe("badRequest()");
logMe(debugInfo());
header('HTTP/1.1 400 Bad Request');
header('Content-Type: text/plain; charset=UTF-8');
@@ -104,7 +104,7 @@ function badRequest() {
}
function unauthorized() {
- logMe("unauthorized()\n");
+ logMe("unauthorized()");
logMe(debugInfo());
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: text/plain; charset=UTF-8');
@@ -113,7 +113,7 @@ function unauthorized() {
}
function notImplemented() {
- logMe("notImplemented()\n");
+ logMe("notImplemented()");
logMe(debugInfo());
header('HTTP/1.1 501 Not Implemented');
header('Content-Type: text/plain; charset=UTF-8');
@@ -121,14 +121,14 @@ function notImplemented() {
}
function serviceUnavailable() {
- logMe("serviceUnavailable()\n");
+ logMe("serviceUnavailable()");
header('HTTP/1.1 503 Service Unavailable');
header('Content-Type: text/plain; charset=UTF-8');
die('Service Unavailable!');
}
function checkCompatibility() {
- logMe("checkCompatibility()\n");
+ logMe("checkCompatibility()");
header('Content-Type: text/plain; charset=UTF-8');
if (PHP_INT_SIZE < 8 && !function_exists('gmp_init')) {
die('FAIL 64-bit or GMP extension!');
@@ -159,7 +159,7 @@ function authorizationToUser() {
if ($headerAuthX[1] === sha1($system_conf->salt . $user . $conf->apiPasswordHash)) {
return $user;
} else {
- logMe('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1] . "\n");
+ logMe('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]);
Minz_Log::warning('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]);
unauthorized();
}
@@ -172,7 +172,7 @@ function authorizationToUser() {
}
function clientLogin($email, $pass) { //http://web.archive.org/web/20130604091042/http://undoc.in/clientLogin.html
- logMe('clientLogin(' . $email . ")\n");
+ //logMe('clientLogin(' . $email . ")");
if (ctype_alnum($email)) {
if (!function_exists('password_verify')) {
include_once(LIB_PATH . '/password_compat.php');
@@ -205,7 +205,7 @@ function token($conf) {
//http://blog.martindoms.com/2009/08/15/using-the-google-reader-api-part-1/
//https://github.com/ericmann/gReader-Library/blob/master/greader.class.php
$user = Minz_Session::param('currentUser', '_');
- logMe('token('. $user . ")\n"); //TODO: Implement real token that expires
+ //logMe('token('. $user . ")"); //TODO: Implement real token that expires
$system_conf = Minz_Configuration::get('system');
$token = str_pad(sha1($system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z'); //Must have 57 characters
echo $token, "\n";
@@ -215,7 +215,7 @@ function token($conf) {
function checkToken($conf, $token) {
//http://code.google.com/p/google-reader-api/wiki/ActionToken
$user = Minz_Session::param('currentUser', '_');
- logMe('checkToken(' . $token . ")\n");
+ //logMe('checkToken(' . $token . ")");
$system_conf = Minz_Configuration::get('system');
if ($token === str_pad(sha1($system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z')) {
return true;
@@ -224,7 +224,7 @@ function checkToken($conf, $token) {
}
function tagList() {
- logMe("tagList()\n");
+ //logMe("tagList()");
header('Content-Type: application/json; charset=UTF-8');
$pdo = new MyPDO();
@@ -249,7 +249,7 @@ function tagList() {
}
function subscriptionList() {
- logMe("subscriptionList()\n");
+ //logMe("subscriptionList()");
header('Content-Type: application/json; charset=UTF-8');
$pdo = new MyPDO();
@@ -283,7 +283,7 @@ function subscriptionList() {
}
function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#unread-count
- logMe("unreadCount()\n");
+ //logMe("unreadCount()");
header('Content-Type: application/json; charset=UTF-8');
$totalUnreads = 0;
@@ -330,7 +330,7 @@ function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-googl
function streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation) {
//http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
- logMe("streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation)\n");
+ //logMe("streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation)");
header('Content-Type: application/json; charset=UTF-8');
$feedDAO = FreshRSS_Factory::createFeedDao();
@@ -371,7 +371,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex
}
$entryDAO = FreshRSS_Factory::createEntryDao();
- $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, '', $start_time);
+ $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, new FreshRSS_Search(''), $start_time);
$items = array();
foreach ($entries as $entry) {
@@ -436,7 +436,7 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
//http://code.google.com/p/google-reader-api/wiki/ApiStreamItemsIds
//http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
- logMe("streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target)\n");
+ //logMe("streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target)");
$type = 'A';
$id = '';
@@ -465,8 +465,11 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
}
$entryDAO = FreshRSS_Factory::createEntryDao();
- $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time);
+ $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', new FreshRSS_Search(''), $start_time);
+ if (empty($ids)) { //For News+ bug https://github.com/noinnion/newsplus/issues/84#issuecomment-57834632
+ $ids[] = 0;
+ }
$itemRefs = array();
foreach ($ids as $id) {
$itemRefs[] = array(
@@ -481,7 +484,7 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
}
function editTag($e_ids, $a, $r) {
- logMe("editTag()\n");
+ //logMe("editTag()");
foreach ($e_ids as $i => $e_id) {
$e_ids[$i] = hex2dec(basename($e_id)); //Strip prefix 'tag:google.com,2005:reader/item/'
@@ -517,7 +520,7 @@ function editTag($e_ids, $a, $r) {
}
function markAllAsRead($streamId, $olderThanId) {
- logMe("markAllAsRead($streamId, $olderThanId)\n");
+ //logMe("markAllAsRead($streamId, $olderThanId)");
$entryDAO = FreshRSS_Factory::createEntryDao();
if (strpos($streamId, 'feed/') === 0) {
$f_id = basename($streamId);
@@ -535,7 +538,7 @@ function markAllAsRead($streamId, $olderThanId) {
exit();
}
-logMe('----------------------------------------------------------------'."\n");
+//logMe('----------------------------------------------------------------');
//logMe(debugInfo());
$pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : urldecode($_SERVER['PATH_INFO']);
@@ -557,7 +560,7 @@ if ($user !== '') {
$conf = get_user_configuration($user);
}
-logMe('User => ' . $user . "\n");
+//logMe('User => ' . $user);
Minz_Session::_param('currentUser', $user);
diff --git a/sources/p/api/pshb.php b/sources/p/api/pshb.php
new file mode 100755
index 0000000..4bb4694
--- /dev/null
+++ b/sources/p/api/pshb.php
@@ -0,0 +1,133 @@
+ $_SERVER, '_GET' => $_GET, '_POST' => $_POST, 'INPUT' => $ORIGINAL_INPUT), true));
+
+$key = isset($_GET['k']) ? substr($_GET['k'], 0, 128) : '';
+if (!ctype_xdigit($key)) {
+ header('HTTP/1.1 422 Unprocessable Entity');
+ die('Invalid feed key format!');
+}
+chdir(PSHB_PATH);
+$canonical64 = @file_get_contents('keys/' . $key . '.txt');
+if ($canonical64 === false) {
+ header('HTTP/1.1 404 Not Found');
+ logMe('Error: Feed key not found!: ' . $key);
+ die('Feed key not found!');
+}
+$canonical64 = trim($canonical64);
+if (!preg_match('/^[A-Za-z0-9_-]+$/D', $canonical64)) {
+ header('HTTP/1.1 500 Internal Server Error');
+ logMe('Error: Invalid key reference!: ' . $canonical64);
+ die('Invalid key reference!');
+}
+$hubFile = @file_get_contents('feeds/' . $canonical64 . '/!hub.json');
+if ($hubFile === false) {
+ header('HTTP/1.1 404 Not Found');
+ //@unlink('keys/' . $key . '.txt');
+ logMe('Error: Feed info not found!: ' . $canonical64);
+ die('Feed info not found!');
+}
+$hubJson = json_decode($hubFile, true);
+if (!$hubJson || empty($hubJson['key']) || $hubJson['key'] !== $key) {
+ header('HTTP/1.1 500 Internal Server Error');
+ logMe('Error: Invalid key cross-check!: ' . $key);
+ die('Invalid key cross-check!');
+}
+chdir('feeds/' . $canonical64);
+$users = glob('*.txt', GLOB_NOSORT);
+if (empty($users)) {
+ header('HTTP/1.1 410 Gone');
+ logMe('Error: Nobody is subscribed to this feed anymore!: ' . $canonical64);
+ die('Nobody is subscribed to this feed anymore!');
+}
+
+if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] === 'subscribe') {
+ $leaseSeconds = empty($_REQUEST['hub_lease_seconds']) ? 0 : intval($_REQUEST['hub_lease_seconds']);
+ if ($leaseSeconds > 60) {
+ $hubJson['lease_end'] = time() + $leaseSeconds;
+ } else {
+ unset($hubJson['lease_end']);
+ }
+ $hubJson['lease_start'] = time();
+ if (!isset($hubJson['error'])) {
+ $hubJson['error'] = true; //Do not assume that PubSubHubbub works until the first successul push
+ }
+ file_put_contents('./!hub.json', json_encode($hubJson));
+ exit(isset($_REQUEST['hub_challenge']) ? $_REQUEST['hub_challenge'] : '');
+}
+
+if ($ORIGINAL_INPUT == '') {
+ header('HTTP/1.1 422 Unprocessable Entity');
+ die('Missing XML payload!');
+}
+
+Minz_Configuration::register('system', DATA_PATH . '/config.php', DATA_PATH . '/config.default.php');
+$system_conf = Minz_Configuration::get('system');
+$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!)
+Minz_Translate::init('en');
+Minz_Request::_param('ajax', true);
+$feedController = new FreshRSS_feed_Controller();
+
+$simplePie = customSimplePie();
+$simplePie->set_raw_data($ORIGINAL_INPUT);
+$simplePie->init();
+unset($ORIGINAL_INPUT);
+
+$links = $simplePie->get_links('self');
+$self = isset($links[0]) ? $links[0] : null;
+
+if ($self !== base64url_decode($canonical64)) {
+ //header('HTTP/1.1 422 Unprocessable Entity');
+ logMe('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . base64url_decode($canonical64));
+ //die('Self URL does not match registered canonical URL!');
+ $self = base64url_decode($canonical64);
+}
+Minz_Request::_param('url', $self);
+
+$nb = 0;
+foreach ($users as $userFilename) {
+ $username = basename($userFilename, '.txt');
+ if (!file_exists(USERS_PATH . '/' . $username . '/config.php')) {
+ break;
+ }
+
+ try {
+ Minz_Session::_param('currentUser', $username);
+ Minz_Configuration::register('user',
+ join_path(USERS_PATH, $username, 'config.php'),
+ join_path(USERS_PATH, '_', 'config.default.php'));
+ FreshRSS_Context::init();
+ if ($feedController->actualizeAction($simplePie) > 0) {
+ $nb++;
+ }
+ } catch (Exception $e) {
+ logMe('Error: ' . $e->getMessage());
+ }
+}
+
+$simplePie->__destruct();
+unset($simplePie);
+
+if ($nb === 0) {
+ header('HTTP/1.1 410 Gone');
+ logMe('Error: Nobody is subscribed to this feed anymore after all!: ' . $self);
+ die('Nobody is subscribed to this feed anymore after all!');
+} elseif (!empty($hubJson['error'])) {
+ $hubJson['error'] = false;
+ file_put_contents('./!hub.json', json_encode($hubJson));
+}
+
+logMe('PubSubHubbub ' . $self . ' done: ' . $nb);
+exit('Done: ' . $nb . "\n");
diff --git a/sources/p/index.html b/sources/p/index.html
index 260f437..ef5cb87 100755
--- a/sources/p/index.html
+++ b/sources/p/index.html
@@ -8,7 +8,7 @@
-
+