Initial support for third-party fallback hub such as Superfeedr for feed subscriptions.

If set up, this hub will be used to subscribe to feeds that don't specify a hub of their own.
Assumes that the fallback hub will, in fact, handle polling and updates for any feed we throw at it!
Authentication may be specified for the fallback hub.

Example:

  $config['feedsub']['fallback_hub'] = 'https://superfeedr.com/hubbub';
  $config['feedsub']['hub_user'] = 'abcd';
  $config['feedsub']['hub_pass'] = 'ckcmdkmckdmkcdk';

Also:
* Fix for WordPress-RSS-via-Superfeedr-Atom; if we have <author> info but no ID from a native ActivityStreams actor, don't freak out in the low-level processing code that checks for identity matches.
* enhanced messages for low-level FeedSub exceptions if they make it to outside display
This commit is contained in:
Brion Vibber 2010-08-02 16:08:54 -07:00 committed by Brion Vibber
parent 6a2659ed67
commit 9a53be4669
6 changed files with 89 additions and 19 deletions

View File

@ -28,6 +28,15 @@ set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/ext
class FeedSubException extends Exception class FeedSubException extends Exception
{ {
function __construct($msg=null)
{
$type = get_class($this);
if ($msg) {
parent::__construct("$type: $msg");
} else {
parent::__construct($type);
}
}
} }
class OStatusPlugin extends Plugin class OStatusPlugin extends Plugin

View File

@ -1,18 +1,42 @@
Plugin to support importing updates from external RSS and Atom feeds into your timeline. Plugin to support importing and exporting notices through Atom and RSS feeds.
The OStatus plugin concentrates on user-to-user cases for federating StatusNet
and similar social networking / microblogging / blogging sites, but includes
low-level feed subscription systems which are used by some other plugins.
Uses PubSubHubbub for push feed updates; currently non-PuSH feeds cannot be
subscribed unless an external PuSH hub proxy is used.
Uses PubSubHubbub for push feed updates; currently non-PuSH feeds cannot be subscribed.
Configuration options available: Configuration options available:
$config['ostatus']['hub'] $config['ostatus']['hub']
(default internal hub) (default internal hub)
Set to URL of an external PuSH hub to use it instead of our internal hub. Set to URL of an external PuSH hub to use it instead of our internal hub
for sending outgoing updates in user and group feeds.
$config['ostatus']['hub_retries'] $config['ostatus']['hub_retries']
(default 0) (default 0)
Number of times to retry a PuSH send to consumers if using internal hub Number of times to retry a PuSH send to consumers if using internal hub
Settings controlling incoming feed subscription:
$config['feedsub']['fallback_hub']
To subscribe to feeds that don't have a hub, an external PuSH proxy hub
such as Superfeedr may be used. Any feed without a hub of its own will
be subscribed through the specified hub URL instead. If the external hub
has usage charges, be aware that there is no restriction placed to how
many feeds may be subscribed!
$config['feedsub']['fallback_hub'] = 'https://superfeedr.com/hubbub';
$config['feedsub']['hub_user']
$config['feedsub']['hub_password']
If using the fallback hub mode, these settings may be used to provide
HTTP authentication credentials for contacting the hub. Default hubs
specified from feeds are assumed to not require
For testing, shouldn't be used in production: For testing, shouldn't be used in production:
$config['ostatus']['skip_signatures'] $config['ostatus']['skip_signatures']
@ -23,12 +47,11 @@ $config['feedsub']['nohub']
(default require hub) (default require hub)
Allow low-level feed subscription setup for feeds without hubs. Allow low-level feed subscription setup for feeds without hubs.
Not actually usable at this stage, OStatus will check for hubs too Not actually usable at this stage, OStatus will check for hubs too
and we have no polling backend. and we have no polling backend. (The fallback hub option can be used
with a 3rd-party service to provide such polling.)
Todo: Todo:
* fully functional l10n * better support for feeds that aren't natively oriented at social networking
* redo non-OStatus feed support
** rssCloud support?
** possibly a polling daemon to support non-PuSH feeds?
* make use of tags/categories from feeds * make use of tags/categories from feeds
* better repeat handling

View File

@ -207,8 +207,8 @@ class FeedSub extends Memcached_DataObject
$discover = new FeedDiscovery(); $discover = new FeedDiscovery();
$discover->discoverFromFeedURL($feeduri); $discover->discoverFromFeedURL($feeduri);
$huburi = $discover->getAtomLink('hub'); $huburi = $discover->getHubLink();
if (!$huburi) { if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
throw new FeedSubNoHubException(); throw new FeedSubNoHubException();
} }
@ -241,8 +241,12 @@ class FeedSub extends Memcached_DataObject
common_log(LOG_WARNING, "Attempting to (re)start PuSH subscription to $this->uri in unexpected state $this->sub_state"); common_log(LOG_WARNING, "Attempting to (re)start PuSH subscription to $this->uri in unexpected state $this->sub_state");
} }
if (empty($this->huburi)) { if (empty($this->huburi)) {
if (common_config('feedsub', 'nohub')) { if (common_config('feedsub', 'fallback_hub')) {
// No native hub on this feed?
// Use our fallback hub, which handles polling on our behalf.
} else if (common_config('feedsub', 'nohub')) {
// Fake it! We're just testing remote feeds w/o hubs. // Fake it! We're just testing remote feeds w/o hubs.
// We'll never actually get updates in this mode.
return true; return true;
} else { } else {
throw new ServerException("Attempting to start PuSH subscription for feed with no hub"); throw new ServerException("Attempting to start PuSH subscription for feed with no hub");
@ -267,8 +271,12 @@ class FeedSub extends Memcached_DataObject
common_log(LOG_WARNING, "Attempting to (re)end PuSH subscription to $this->uri in unexpected state $this->sub_state"); common_log(LOG_WARNING, "Attempting to (re)end PuSH subscription to $this->uri in unexpected state $this->sub_state");
} }
if (empty($this->huburi)) { if (empty($this->huburi)) {
if (common_config('feedsub', 'nohub')) { if (common_config('feedsub', 'fallback_hub')) {
// No native hub on this feed?
// Use our fallback hub, which handles polling on our behalf.
} else if (common_config('feedsub', 'nohub')) {
// Fake it! We're just testing remote feeds w/o hubs. // Fake it! We're just testing remote feeds w/o hubs.
// We'll never actually get updates in this mode.
return true; return true;
} else { } else {
throw new ServerException("Attempting to end PuSH subscription for feed with no hub"); throw new ServerException("Attempting to end PuSH subscription for feed with no hub");
@ -326,7 +334,21 @@ class FeedSub extends Memcached_DataObject
'hub.secret' => $this->secret, 'hub.secret' => $this->secret,
'hub.topic' => $this->uri); 'hub.topic' => $this->uri);
$client = new HTTPClient(); $client = new HTTPClient();
$response = $client->post($this->huburi, $headers, $post); if ($this->huburi) {
$hub = $this->huburi;
} else {
if (common_config('feedsub', 'fallback_hub')) {
$hub = common_config('feedsub', 'fallback_hub');
if (common_config('feedsub', 'hub_user')) {
$u = common_config('feedsub', 'hub_user');
$p = common_config('feedsub', 'hub_pass');
$client->setAuth($u, $p);
}
} else {
throw new FeedSubException('WTF?');
}
}
$response = $client->post($hub, $headers, $post);
$status = $response->getStatus(); $status = $response->getStatus();
if ($status == 202) { if ($status == 202) {
common_log(LOG_INFO, __METHOD__ . ': sub req ok, awaiting verification callback'); common_log(LOG_INFO, __METHOD__ . ': sub req ok, awaiting verification callback');

View File

@ -493,8 +493,14 @@ class Ostatus_profile extends Memcached_DataObject
// OK here! assume the default // OK here! assume the default
} else if ($actor->id == $this->uri || $actor->link == $this->uri) { } else if ($actor->id == $this->uri || $actor->link == $this->uri) {
$this->updateFromActivityObject($actor); $this->updateFromActivityObject($actor);
} else { } else if ($actor->id) {
// We have an ActivityStreams actor with an explicit ID that doesn't match the feed owner.
// This isn't what we expect from mainline OStatus person feeds!
// Group feeds go down another path, with different validation.
throw new Exception("Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}"); throw new Exception("Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}");
} else {
// Plain <author> without ActivityStreams actor info.
// We'll just ignore this info for now and save the update under the feed's identity.
} }
$oprofile = $this; $oprofile = $this;
@ -869,12 +875,12 @@ class Ostatus_profile extends Memcached_DataObject
$feeduri = $discover->discoverFromFeedURL($feed_url); $feeduri = $discover->discoverFromFeedURL($feed_url);
$hints['feedurl'] = $feeduri; $hints['feedurl'] = $feeduri;
$huburi = $discover->getAtomLink('hub'); $huburi = $discover->getHubLink();
$hints['hub'] = $huburi; $hints['hub'] = $huburi;
$salmonuri = $discover->getAtomLink(Salmon::NS_REPLIES); $salmonuri = $discover->getAtomLink(Salmon::NS_REPLIES);
$hints['salmon'] = $salmonuri; $hints['salmon'] = $salmonuri;
if (!$huburi) { if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
// We can only deal with folks with a PuSH hub // We can only deal with folks with a PuSH hub
throw new FeedSubNoHubException(); throw new FeedSubNoHubException();
} }
@ -1270,10 +1276,10 @@ class Ostatus_profile extends Memcached_DataObject
$discover = new FeedDiscovery(); $discover = new FeedDiscovery();
$discover->discoverFromFeedURL($hints['feedurl']); $discover->discoverFromFeedURL($hints['feedurl']);
} }
$huburi = $discover->getAtomLink('hub'); $huburi = $discover->getHubLink();
} }
if (!$huburi) { if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
// We can only deal with folks with a PuSH hub // We can only deal with folks with a PuSH hub
throw new FeedSubNoHubException(); throw new FeedSubNoHubException();
} }

View File

@ -87,6 +87,16 @@ class FeedDiscovery
return ActivityUtils::getLink($this->root, $rel, $type); return ActivityUtils::getLink($this->root, $rel, $type);
} }
/**
* Get the referenced PuSH hub link from an Atom feed.
*
* @return mixed string or false
*/
public function getHubLink()
{
return $this->getAtomLink('hub');
}
/** /**
* @param string $url * @param string $url
* @param bool $htmlOk pass false here if you don't want to follow web pages. * @param bool $htmlOk pass false here if you don't want to follow web pages.

View File

@ -55,7 +55,7 @@ print "Re-running feed discovery for profile URL $oprofile->uri\n";
// @fixme will bork where the URI isn't the profile URL for now // @fixme will bork where the URI isn't the profile URL for now
$discover = new FeedDiscovery(); $discover = new FeedDiscovery();
$feedurl = $discover->discoverFromURL($oprofile->uri); $feedurl = $discover->discoverFromURL($oprofile->uri);
$huburi = $discover->getAtomLink('hub'); $huburi = $discover->getHubLink();
$salmonuri = $discover->getAtomLink(Salmon::NS_REPLIES); $salmonuri = $discover->getAtomLink(Salmon::NS_REPLIES);
print " Feed URL: $feedurl\n"; print " Feed URL: $feedurl\n";