فهرست منبع

WebSub: only perform a redirect when coming from WebSub (#7738)

And add support for HTTP Link header for "self" URL
Changing URL based on "self" URL will only be done when coming from a WebSub push
fix https://github.com/FreshRSS/FreshRSS/issues/7737
Alexandre Alapetite 8 ماه پیش
والد
کامیت
01eae00ca2
3فایلهای تغییر یافته به همراه55 افزوده شده و 26 حذف شده
  1. 23 18
      app/Controllers/feedController.php
  2. 4 0
      app/Models/Feed.php
  3. 28 8
      p/api/pshb.php

+ 23 - 18
app/Controllers/feedController.php

@@ -393,12 +393,14 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 	}
 	}
 
 
 	/**
 	/**
+	 * @param \SimplePie\SimplePie|null $simplePiePush Used by WebSub (PubSubHubbub) to push updates
+	 * @param string $selfUrl Used by WebSub (PubSubHubbub) to override the feed URL
 	 * @return array{0:int,1:FreshRSS_Feed|null,2:int,3:array<FreshRSS_Feed>} Number of updated feeds, first feed or null, number of new articles,
 	 * @return array{0:int,1:FreshRSS_Feed|null,2:int,3:array<FreshRSS_Feed>} Number of updated feeds, first feed or null, number of new articles,
 	 * 	list of feeds for which a cache refresh is needed
 	 * 	list of feeds for which a cache refresh is needed
 	 * @throws FreshRSS_BadUrl_Exception
 	 * @throws FreshRSS_BadUrl_Exception
 	 */
 	 */
 	public static function actualizeFeeds(?int $feed_id = null, ?string $feed_url = null, ?int $maxFeeds = null,
 	public static function actualizeFeeds(?int $feed_id = null, ?string $feed_url = null, ?int $maxFeeds = null,
-		?\SimplePie\SimplePie $simplePiePush = null): array {
+		?\SimplePie\SimplePie $simplePiePush = null, string $selfUrl = ''): array {
 		if (function_exists('set_time_limit')) {
 		if (function_exists('set_time_limit')) {
 			@set_time_limit(300);
 			@set_time_limit(300);
 		}
 		}
@@ -422,6 +424,9 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 		if ($feed_id !== null || $feed_url !== null) {
 		if ($feed_id !== null || $feed_url !== null) {
 			$feed = $feed_id !== null ? $feedDAO->searchById($feed_id) : $feedDAO->searchByUrl($feed_url);
 			$feed = $feed_id !== null ? $feedDAO->searchById($feed_id) : $feedDAO->searchByUrl($feed_url);
 			if ($feed !== null && $feed->id() > 0) {
 			if ($feed !== null && $feed->id() > 0) {
+				if ($selfUrl !== '') {
+					$feed->_selfUrl($selfUrl);
+				}
 				$feeds[] = $feed;
 				$feeds[] = $feed;
 				$feed_id = $feed->id();
 				$feed_id = $feed->id();
 			}
 			}
@@ -692,22 +697,19 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 				$feedProperties['attributes'] = $feed->attributes();
 				$feedProperties['attributes'] = $feed->attributes();
 			}
 			}
 
 
-			if ($pubsubhubbubEnabledGeneral && $feed->hubUrl() !== '' && $feed->selfUrl() !== '') {	//selfUrl has priority for WebSub
-				if ($feed->selfUrl() !== $url) {	// https://github.com/pubsubhubbub/PubSubHubbub/wiki/Moving-Feeds-or-changing-Hubs
-					$selfUrl = checkUrl($feed->selfUrl());
-					if ($selfUrl != false) {
-						Minz_Log::debug('WebSub unsubscribe ' . $feed->url(false));
-						if (!$feed->pubSubHubbubSubscribe(false)) {	//Unsubscribe
-							Minz_Log::warning('Error while WebSub unsubscribing from ' . $feed->url(false));
-						}
-						$feed->_url($selfUrl, false);
-						Minz_Log::notice('Feed ' . $url . ' canonical address moved to ' . $feed->url(false));
-						$feedDAO->updateFeed($feed->id(), ['url' => $feed->url()]);
-					}
+			if ($feed->url() !== $url) {	// HTTP 301 Moved Permanently
+				Minz_Log::warning('Feed ' . \SimplePie\Misc::url_remove_credentials($url) .
+					' moved permanently to ' . $feed->url(includeCredentials: false));
+				$feedProperties['url'] = $feed->url();
+			} elseif ($simplePiePush !== null && $selfUrl !== '' && $selfUrl !== $feed->url()) {	// selfUrl has priority for WebSub
+				// https://github.com/pubsubhubbub/PubSubHubbub/wiki/Moving-Feeds-or-changing-Hubs
+				Minz_Log::debug('WebSub unsubscribe ' . $feed->url(includeCredentials: false));
+				if (!$feed->pubSubHubbubSubscribe(false)) {	//Unsubscribe
+					Minz_Log::warning('Error while WebSub unsubscribing from ' . $feed->url(includeCredentials: false));
 				}
 				}
-			} elseif ($feed->url() !== $url) {	// HTTP 301 Moved Permanently
-				Minz_Log::notice('Feed ' . \SimplePie\Misc::url_remove_credentials($url) .
-					' moved permanently to ' .  \SimplePie\Misc::url_remove_credentials($feed->url(false)));
+				$feed->_url($selfUrl);
+				Minz_Log::warning('Feed ' . \SimplePie\Misc::url_remove_credentials($url) .
+					' canonical address moved to ' . $feed->url(includeCredentials: false));
 				$feedProperties['url'] = $feed->url();
 				$feedProperties['url'] = $feed->url();
 			}
 			}
 
 
@@ -826,14 +828,17 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 	}
 	}
 
 
 	/**
 	/**
+	 * @param \SimplePie\SimplePie|null $simplePiePush Used by WebSub (PubSubHubbub) to push updates
+	 * @param string $selfUrl Used by WebSub (PubSubHubbub) to override the feed URL
 	 * @return array{0:int,1:FreshRSS_Feed|null,2:int,3:array<FreshRSS_Feed>} Number of updated feeds, first feed or null, number of new articles,
 	 * @return array{0:int,1:FreshRSS_Feed|null,2:int,3:array<FreshRSS_Feed>} Number of updated feeds, first feed or null, number of new articles,
 	 * 	list of feeds for which a cache refresh is needed
 	 * 	list of feeds for which a cache refresh is needed
 	 * @throws FreshRSS_BadUrl_Exception
 	 * @throws FreshRSS_BadUrl_Exception
 	 */
 	 */
 	public static function actualizeFeedsAndCommit(?int $feed_id = null, ?string $feed_url = null, ?int $maxFeeds = null,
 	public static function actualizeFeedsAndCommit(?int $feed_id = null, ?string $feed_url = null, ?int $maxFeeds = null,
-		?SimplePie\SimplePie $simplePiePush = null): array {
+		?SimplePie\SimplePie $simplePiePush = null, string $selfUrl = ''): array {
 		$entryDAO = FreshRSS_Factory::createEntryDao();
 		$entryDAO = FreshRSS_Factory::createEntryDao();
-		[$nbUpdatedFeeds, $feed, $nbNewArticles, $feedsCacheToRefresh] = FreshRSS_feed_Controller::actualizeFeeds($feed_id, $feed_url, $maxFeeds, $simplePiePush);
+		[$nbUpdatedFeeds, $feed, $nbNewArticles, $feedsCacheToRefresh] =
+			FreshRSS_feed_Controller::actualizeFeeds($feed_id, $feed_url, $maxFeeds, $simplePiePush, $selfUrl);
 		if ($nbNewArticles > 0) {
 		if ($nbNewArticles > 0) {
 			$entryDAO->beginTransaction();
 			$entryDAO->beginTransaction();
 			FreshRSS_feed_Controller::commitNewEntries();
 			FreshRSS_feed_Controller::commitNewEntries();

+ 4 - 0
app/Models/Feed.php

@@ -463,6 +463,10 @@ class FreshRSS_Feed extends Minz_Model {
 		$this->url = $url;
 		$this->url = $url;
 	}
 	}
 
 
+	public function _selfUrl(string $value): void {
+		$this->selfUrl = $value;
+	}
+
 	public function _kind(int $value): void {
 	public function _kind(int $value): void {
 		$this->kind = $value;
 		$this->kind = $value;
 	}
 	}

+ 28 - 8
p/api/pshb.php

@@ -104,13 +104,33 @@ $simplePie->init();
 unset($ORIGINAL_INPUT);
 unset($ORIGINAL_INPUT);
 
 
 $links = $simplePie->get_links('self');
 $links = $simplePie->get_links('self');
-$self = $links[0] ?? null;
+$self = $links[0] ?? '';
+
+// Support HTTP header `Link: <http://example.net/hub.php>; rel="hub", <http://example.net/feed.php>; rel="self"`
+$httpLink = is_string($_SERVER['HTTP_LINK'] ?? null) ? $_SERVER['HTTP_LINK'] : '';
+if ($httpLink !== '' && preg_match_all('/<([^>]+)>;\\s*rel="([^"]+)"/', $httpLink, $matches, PREG_SET_ORDER)) {
+	$links = [];
+	foreach ($matches as $match) {
+		if (!empty($match[1]) && !empty($match[2])) {
+			$links[$match[2]] = $match[1];
+		}
+	}
+	// if (!empty($links['hub'])) {
+	// 	// TODO: Support WebSub hub redirection
+	// }
+	if (!empty($links['self'])) {
+		$httpSelf = checkUrl($links['self']) ?: '';
+		if ($self !== '' && $self !== $httpSelf) {
+			Minz_Log::warning('Warning: Self URL mismatch between XML [' . $self . '] and HTTP!: ' . $httpSelf, PSHB_LOG);
+		}
+		$self = $httpSelf;
+	}
+}
 
 
 if ($self !== $canonical) {
 if ($self !== $canonical) {
 	//header('HTTP/1.1 422 Unprocessable Entity');
 	//header('HTTP/1.1 422 Unprocessable Entity');
 	Minz_Log::warning('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . $canonical, PSHB_LOG);
 	Minz_Log::warning('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . $canonical, PSHB_LOG);
 	//die('Self URL does not match registered canonical URL!');
 	//die('Self URL does not match registered canonical URL!');
-	$self = $canonical;
 }
 }
 
 
 Minz_ExtensionManager::init();
 Minz_ExtensionManager::init();
@@ -120,7 +140,7 @@ $nb = 0;
 foreach ($users as $userFilename) {
 foreach ($users as $userFilename) {
 	$username = basename($userFilename, '.txt');
 	$username = basename($userFilename, '.txt');
 	if (!file_exists(USERS_PATH . '/' . $username . '/config.php')) {
 	if (!file_exists(USERS_PATH . '/' . $username . '/config.php')) {
-		Minz_Log::warning('Warning: Removing broken user link: ' . $username . ' for ' . $self, PSHB_LOG);
+		Minz_Log::warning('Warning: Removing broken user link: ' . $username . ' for ' . $canonical, PSHB_LOG);
 		unlink($userFilename);
 		unlink($userFilename);
 		continue;
 		continue;
 	}
 	}
@@ -134,15 +154,15 @@ foreach ($users as $userFilename) {
 		Minz_ExtensionManager::enableByList(FreshRSS_Context::userConf()->extensions_enabled, 'user');
 		Minz_ExtensionManager::enableByList(FreshRSS_Context::userConf()->extensions_enabled, 'user');
 		Minz_Translate::reset(FreshRSS_Context::userConf()->language);
 		Minz_Translate::reset(FreshRSS_Context::userConf()->language);
 
 
-		[$nbUpdatedFeeds, ] = FreshRSS_feed_Controller::actualizeFeedsAndCommit(null, $self, null, $simplePie);
+		[$nbUpdatedFeeds, ] = FreshRSS_feed_Controller::actualizeFeedsAndCommit(feed_url: $canonical, simplePiePush: $simplePie, selfUrl: $self);
 		if ($nbUpdatedFeeds > 0) {
 		if ($nbUpdatedFeeds > 0) {
 			$nb++;
 			$nb++;
 		} else {
 		} else {
-			Minz_Log::warning('Warning: User ' . $username . ' does not subscribe anymore to ' . $self, PSHB_LOG);
+			Minz_Log::warning('Warning: User ' . $username . ' does not subscribe anymore to ' . $canonical, PSHB_LOG);
 			unlink($userFilename);
 			unlink($userFilename);
 		}
 		}
 	} catch (Exception $e) {
 	} catch (Exception $e) {
-		Minz_Log::error('Error: ' . $e->getMessage() . ' for user ' . $username . ' and feed ' . $self, PSHB_LOG);
+		Minz_Log::error('Error: ' . $e->getMessage() . ' for user ' . $username . ' and feed ' . $canonical, PSHB_LOG);
 	}
 	}
 }
 }
 
 
@@ -151,12 +171,12 @@ unset($simplePie);
 
 
 if ($nb === 0) {
 if ($nb === 0) {
 	header('HTTP/1.1 410 Gone');
 	header('HTTP/1.1 410 Gone');
-	Minz_Log::warning('Warning: Nobody subscribes to this feed anymore after all!: ' . $self, PSHB_LOG);
+	Minz_Log::warning('Warning: Nobody subscribes to this feed anymore after all!: ' . $canonical, PSHB_LOG);
 	die('Nobody subscribes to this feed anymore after all!');
 	die('Nobody subscribes to this feed anymore after all!');
 } elseif (!empty($hubJson['error'])) {
 } elseif (!empty($hubJson['error'])) {
 	$hubJson['error'] = false;
 	$hubJson['error'] = false;
 	file_put_contents('./!hub.json', json_encode($hubJson));
 	file_put_contents('./!hub.json', json_encode($hubJson));
 }
 }
 
 
-Minz_Log::notice('WebSub ' . $self . ' done: ' . $nb, PSHB_LOG);
+Minz_Log::notice('WebSub ' . $canonical . ' done: ' . $nb, PSHB_LOG);
 exit('Done: ' . $nb . "\n");
 exit('Done: ' . $nb . "\n");