Skip to content

Commit

Permalink
Merge branch 'freshrss' into sync-upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
Alkarex authored Sep 27, 2024
2 parents 6b97dec + 5e92ce7 commit 449cd65
Show file tree
Hide file tree
Showing 15 changed files with 468 additions and 79 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ indent_style = space
[*.{yaml,yml}]
indent_size = 2
indent_style = space

[*.xml]
indent_style = tab
21 changes: 21 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# SimplePie

> SimplePie is a very fast and easy-to-use class, written in PHP, that puts the
> *simple* back into *Really Simple Syndication* (RSS). Flexible enough to suit
> beginners and veterans alike, SimplePie is focused on [speed, ease of use,
> compatibility and standards compliance](http://simplepie.org/wiki/faq/what_is_simplepie).
[Read the rest of the original readme](../README.markdown).

## FreshRSS fork

[This repository `FreshRSS/simplepie`](https://github.com/FreshRSS/simplepie) is a fork of the [main repository `simplepie/simplepie`](https://github.com/simplepie/simplepie)
intended to be used for [FreshRSS](https://github.com/FreshRSS/FreshRSS), which is a free, self-hostable news aggregator.

The default branch [`freshrss`](https://github.com/FreshRSS/simplepie/tree/freshrss) is the one used in [the FreshRSS codebase](https://github.com/FreshRSS/FreshRSS/tree/edge/lib/simplepie).

This allows in particular to use fixes and patches, which have not (yet) been submitted upstream or not (yet) merged upstream.

### Differences

[See the differences](https://github.com/simplepie/simplepie/compare/master...FreshRSS:simplepie:freshrss).
29 changes: 29 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="SimplePie">
<arg name="extensions" value="php"/>
<exclude-pattern>./.git/</exclude-pattern>
<exclude-pattern>./compatibility_test/</exclude-pattern>
<exclude-pattern>./vendor/</exclude-pattern>
<rule ref="PSR12">
<exclude name="PSR1.Files.SideEffects.FoundWithSymbols"/>
<exclude name="PSR1.Methods.CamelCapsMethodName.NotCamelCaps"/>
<exclude name="PSR12.ControlStructures.ControlStructureSpacing.CloseParenthesisLine"/>
<exclude name="PSR12.ControlStructures.ControlStructureSpacing.FirstExpressionLine"/>
<exclude name="Squiz.ControlStructures.ControlSignature.SpaceAfterCloseBrace"/>
</rule>
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="165"/>
<property name="absoluteLineLimit" value="400"/>
</properties>
</rule>
<rule ref="Generic.WhiteSpace.DisallowTabIndent.TabsUsed">
<exclude-pattern>./demo/</exclude-pattern>
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
<exclude-pattern>./library/</exclude-pattern>
</rule>
<rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
<exclude-pattern>./library/</exclude-pattern>
</rule>
</ruleset>
9 changes: 5 additions & 4 deletions src/Cache/BaseDataCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ public function get_data(string $key, $default = null)
return $default;
}

// ignore data if internal cache expiration time is expired
if ($data['__cache_expiration_time'] < time()) {
return $default;
}
// FreshRSS commented out, to allow HTTP 304
// // ignore data if internal cache expiration time is expired
// if ($data['__cache_expiration_time'] < time()) {
// return $default;
// }

// remove internal cache expiration time
unset($data['__cache_expiration_time']);
Expand Down
9 changes: 5 additions & 4 deletions src/Content/Type/Sniffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,18 @@ public function get_type()
if ($official === 'unknown/unknown'
|| $official === 'application/unknown') {
return $this->unknown();
} elseif (substr($official, -4) === '+xml'
|| $official === 'text/xml'
|| $official === 'application/xml') {
} elseif (substr($official, -4) === '+xml') { // FreshRSS
return $official;
} elseif (substr($official, 0, 6) === 'image/') {
if ($return = $this->image()) {
return $return;
}

return $official;
} elseif ($official === 'text/html') {
} elseif ($official === 'text/html'
|| $official === 'text/xml' // FreshRSS
|| $official === 'application/xml' // FreshRSS
) {
return $this->feed_or_html();
}

Expand Down
25 changes: 24 additions & 1 deletion src/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public function __construct(string $url, int $timeout = 10, int $redirects = 5,
curl_setopt($fp, CURLOPT_FAILONERROR, 1);
curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($fp, CURLOPT_REFERER, \SimplePie\Misc::url_remove_credentials($url));
// curl_setopt($fp, CURLOPT_REFERER, \SimplePie\Misc::url_remove_credentials($url)); // FreshRSS removed
curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
foreach ($curl_options as $curl_param => $curl_value) {
Expand All @@ -133,14 +133,20 @@ public function __construct(string $url, int $timeout = 10, int $redirects = 5,

$responseHeaders = curl_exec($fp);
if (curl_errno($fp) === 23 || curl_errno($fp) === 61) {
$this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); // FreshRSS
$this->status_code = curl_getinfo($fp, CURLINFO_HTTP_CODE); // FreshRSS
$this->on_http_response();
$this->error = null; // FreshRSS
curl_setopt($fp, CURLOPT_ENCODING, 'none');
$responseHeaders = curl_exec($fp);
}
$this->status_code = curl_getinfo($fp, CURLINFO_HTTP_CODE);
if (curl_errno($fp)) {
$this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
$this->success = false;
$this->on_http_response();
} else {
$this->on_http_response();
// Use the updated url provided by curl_getinfo after any redirects.
if ($info = curl_getinfo($fp)) {
$this->url = $info['url'];
Expand Down Expand Up @@ -176,6 +182,7 @@ public function __construct(string $url, int $timeout = 10, int $redirects = 5,
if (!$fp) {
$this->error = 'fsockopen error: ' . $errstr;
$this->success = false;
$this->on_http_response();
} else {
stream_set_timeout($fp, $timeout);
if (isset($url_parts['path'])) {
Expand Down Expand Up @@ -216,6 +223,7 @@ public function __construct(string $url, int $timeout = 10, int $redirects = 5,
$this->set_headers($parser->headers);
$this->body = $parser->body;
$this->status_code = $parser->status_code;
$this->on_http_response();
if ((in_array($this->status_code, [300, 301, 302, 303, 307]) || $this->status_code > 307 && $this->status_code < 400) && ($locationHeader = $this->get_header_line('location')) !== '' && $this->redirects < $redirects) {
$this->redirects++;
$location = \SimplePie\Misc::absolutize_url($locationHeader, $url);
Expand Down Expand Up @@ -255,10 +263,15 @@ public function __construct(string $url, int $timeout = 10, int $redirects = 5,
$this->success = false;
}
}
} else {
$this->error = 'Could not parse'; // FreshRSS
$this->success = false; // FreshRSS
$this->on_http_response();
}
} else {
$this->error = 'fsocket timed out';
$this->success = false;
$this->on_http_response();
}
fclose($fp);
}
Expand All @@ -273,9 +286,19 @@ public function __construct(string $url, int $timeout = 10, int $redirects = 5,
$this->body = trim($filebody);
$this->status_code = 200;
}
$this->on_http_response();
}
}

/**
* Event to allow inheriting classes to e.g. log the HTTP responses.
* Triggered just after an HTTP response is received.
* FreshRSS.
*/
protected function on_http_response(): void
{
}

public function get_permanent_uri(): string
{
return (string) $this->permanent_url;
Expand Down
79 changes: 79 additions & 0 deletions src/HTTP/Utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace SimplePie\HTTP;

/**
* HTTP util functions
* FreshRSS
* @internal
*/
final class Utils
{
/**
* Extracts `max-age` from the `Cache-Control` HTTP headers
*
* @param array<string,mixed> $http_headers HTTP headers of the response
* @return int|null The `max-age` value or `null` if not found
*
* FreshRSS
*/
public static function get_http_max_age(array $http_headers): ?int
{
$cache_control = $http_headers['cache-control'] ?? null;
if (is_string($cache_control) && preg_match('/\bmax-age=(\d+)\b/', $cache_control, $matches)) {
return (int) $matches[1];
}
return null;
}

/**
* Negotiate the cache expiration time based on the HTTP response headers.
* Return the cache duration time in number of seconds since the Unix Epoch, accounting for:
* - `Cache-Control: max-age` minus `Age`, bounded by `$cache_duration_min` and `$cache_duration_max`
* - `Cache-Control: must-revalidate` will set `$cache_duration` to `$cache_duration_min`
* - `Cache-Control: no-cache` will return `time() + $cache_duration_min`
* - `Cache-Control: no-store` will return `time() + $cache_duration_min - 3`
* - `Expires` like `Cache-Control: max-age` but only if it is absent
*
* @param array<string,mixed> $http_headers HTTP headers of the response
* @param int $cache_duration Desired cache duration in seconds, potentially overridden by HTTP response headers
* @param int $cache_duration_min Minimal cache duration (in seconds), overriding HTTP response headers `Cache-Control` and `Expires`,
* @param int $cache_duration_max Maximal cache duration (in seconds), overriding HTTP response headers `Cache-Control: max-age` and `Expires`,
* @return int The negotiated cache expiration time in seconds since the Unix Epoch
*
* FreshRSS
*/
public static function negociate_cache_expiration_time(array $http_headers, int $cache_duration, int $cache_duration_min, int $cache_duration_max): int
{
$cache_control = $http_headers['cache-control'] ?? '';
if ($cache_control !== '') {
if (preg_match('/\bno-store\b/', $cache_control)) {
return time() + $cache_duration_min - 3; // -3 to distinguish from no-cache if needed
}
if (preg_match('/\bno-cache\b/', $cache_control)) {
return time() + $cache_duration_min;
}
if (preg_match('/\bmust-revalidate\b/', $cache_control)) {
$cache_duration = $cache_duration_min;
}
if (preg_match('/\bmax-age=(\d+)\b/', $cache_control, $matches)) {
$max_age = (int) $matches[1];
$age = $http_headers['age'] ?? '';
if (is_numeric($age)) {
$max_age -= (int) $age;
}
return time() + min(max($max_age, $cache_duration), $cache_duration_max);
}
}
$expires = $http_headers['expires'] ?? '';
if ($expires !== '') {
$expire_date = \SimplePie\Misc::parse_date($expires);
if ($expire_date !== false) {
return min(max($expire_date, time() + $cache_duration), time() + $cache_duration_max);
}
}
return time() + $cache_duration;
}
}
Loading

0 comments on commit 449cd65

Please sign in to comment.