From 22ff358ba8d1fd0396136e1de570d788dd0727b6 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 18 Feb 2010 18:20:48 +0000 Subject: [PATCH] OStatus sub/unsub updates: - fix for PuSH unsub verification - send Salmon notification on unsub --- lib/atom10entry.php | 5 +- lib/atom10feed.php | 11 +++- lib/atomnoticefeed.php | 8 +-- plugins/OStatus/OStatusPlugin.php | 55 +++++++++++++++++-- plugins/OStatus/actions/pushcallback.php | 2 +- plugins/OStatus/actions/pushhub.php | 9 ++- plugins/OStatus/actions/salmon.php | 2 + plugins/OStatus/classes/HubSub.php | 11 ++-- plugins/OStatus/classes/Ostatus_profile.php | 44 ++++++++------- plugins/OStatus/lib/activity.php | 6 ++ plugins/OStatus/lib/hubverifyqueuehandler.php | 3 +- 11 files changed, 108 insertions(+), 48 deletions(-) diff --git a/lib/atom10entry.php b/lib/atom10entry.php index 5710c80fc5..f8f16d5946 100644 --- a/lib/atom10entry.php +++ b/lib/atom10entry.php @@ -27,8 +27,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') -{ +if (!defined('STATUSNET')) { exit(1); } @@ -87,7 +86,7 @@ class Atom10Entry extends XMLStringer * * @return void */ - function validate + function validate() { } diff --git a/lib/atom10feed.php b/lib/atom10feed.php index 14a3beb83e..5e17b20d3a 100644 --- a/lib/atom10feed.php +++ b/lib/atom10feed.php @@ -78,7 +78,7 @@ class Atom10Feed extends XMLStringer $this->authors = array(); $this->links = array(); $this->entries = array(); - $this->addNamespace('xmlns', 'http://www.w3.org/2005/Atom'); + $this->addNamespace('', 'http://www.w3.org/2005/Atom'); } /** @@ -162,7 +162,14 @@ class Atom10Feed extends XMLStringer { $this->xw->startDocument('1.0', 'UTF-8'); $commonAttrs = array('xml:lang' => 'en-US'); - $commonAttrs = array_merge($commonAttrs, $this->namespaces); + foreach ($this->namespaces as $prefix => $uri) { + if ($prefix == '') { + $attr = 'xmlns'; + } else { + $attr = 'xmlns:' . $prefix; + } + $commonAttrs[$attr] = $uri; + } $this->elementStart('feed', $commonAttrs); $this->element('id', null, $this->id); diff --git a/lib/atomnoticefeed.php b/lib/atomnoticefeed.php index b7a60bde6e..7653f91544 100644 --- a/lib/atomnoticefeed.php +++ b/lib/atomnoticefeed.php @@ -50,23 +50,23 @@ class AtomNoticeFeed extends Atom10Feed // Feeds containing notice info use these namespaces $this->addNamespace( - 'xmlns:thr', + 'thr', 'http://purl.org/syndication/thread/1.0' ); $this->addNamespace( - 'xmlns:georss', + 'georss', 'http://www.georss.org/georss' ); $this->addNamespace( - 'xmlns:activity', + 'activity', 'http://activitystrea.ms/spec/1.0/' ); // XXX: What should the uri be? $this->addNamespace( - 'xmlns:ostatus', + 'ostatus', 'http://ostatus.org/schema/1.0' ); } diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index b6c9fa1d4c..e548a151c7 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -112,7 +112,7 @@ class OStatusPlugin extends Plugin * Set up a PuSH hub link to our internal link for canonical timeline * Atom feeds for users and groups. */ - function onStartApiAtom(AtomNoticeFeed $feed) + function onStartApiAtom($feed) { $id = null; @@ -171,6 +171,12 @@ class OStatusPlugin extends Plugin { $base = dirname(__FILE__); $lower = strtolower($cls); + $map = array('activityverb' => 'activity', + 'activityobject' => 'activity', + 'activityutils' => 'activity'); + if (isset($map[$lower])) { + $lower = $map[$lower]; + } $files = array("$base/classes/$cls.php", "$base/lib/$lower.php"); if (substr($lower, -6) == 'action') { @@ -253,18 +259,45 @@ class OStatusPlugin extends Plugin } /** - * Garbage collect unused feeds on unsubscribe + * Notify remote server when one of our users subscribes. + * @fixme Check and restart the PuSH subscription if needed + * + * @param User $user + * @param Profile $other + * @return hook return value + */ + function onEndSubscribe($user, $other) + { + $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); + if ($oprofile) { + // Notify the remote server of the unsub, if supported. + $oprofile->notify($user->getProfile(), ActivityVerb::FOLLOW, $oprofile); + } + return true; + } + + /** + * Notify remote server and garbage collect unused feeds on unsubscribe. + * @fixme send these operations to background queues + * + * @param User $user + * @param Profile $other + * @return hook return value */ function onEndUnsubscribe($user, $other) { - $profile = Ostatus_profile::staticGet('profile_id', $other->id); - if ($feed) { + $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); + if ($oprofile) { + // Notify the remote server of the unsub, if supported. + $oprofile->notify($user->getProfile(), ActivityVerb::UNFOLLOW, $oprofile); + + // Drop the PuSH subscription if there are no other subscribers. $sub = new Subscription(); $sub->subscribed = $other->id; $sub->limit(1); if (!$sub->find(true)) { - common_log(LOG_INFO, "Unsubscribing from now-unused feed $feed->feeduri on hub $feed->huburi"); - $profile->unsubscribe(); + common_log(LOG_INFO, "Unsubscribing from now-unused feed $oprofile->feeduri on hub $oprofile->huburi"); + $oprofile->unsubscribe(); } } return true; @@ -290,6 +323,16 @@ class OStatusPlugin extends Plugin return true; } + /** + * Override the "from ostatus" bit in notice lists to link to the + * original post and show the domain it came from. + * + * @param Notice in $notice + * @param string out &$name + * @param string out &$url + * @param string out &$title + * @return mixed hook return code + */ function onStartNoticeSourceLink($notice, &$name, &$url, &$title) { if ($notice->source == 'ostatus') { diff --git a/plugins/OStatus/actions/pushcallback.php b/plugins/OStatus/actions/pushcallback.php index 388c8f9c3d..ed859a32f8 100644 --- a/plugins/OStatus/actions/pushcallback.php +++ b/plugins/OStatus/actions/pushcallback.php @@ -89,7 +89,7 @@ class PushCallbackAction extends Action if ($profile->verify_token !== $verify_token) { common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad token \"$verify_token\" for feed $topic"); - throw new ServerError("Bogus hub callback: bad token", 404); + throw new ServerException("Bogus hub callback: bad token", 404); } if ($mode != $profile->sub_state) { diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php index 13ec09d528..19599d815f 100644 --- a/plugins/OStatus/actions/pushhub.php +++ b/plugins/OStatus/actions/pushhub.php @@ -83,6 +83,7 @@ class PushHubAction extends Action { $feed = $this->argUrl('hub.topic'); $callback = $this->argUrl('hub.callback'); + $token = $this->arg('hub.verify_token', null); common_log(LOG_DEBUG, __METHOD__ . ": checking sub'd to $feed $callback"); if ($this->getSub($feed, $callback)) { @@ -96,7 +97,6 @@ class PushHubAction extends Action $sub = new HubSub(); $sub->topic = $feed; $sub->callback = $callback; - $sub->verify_token = $this->arg('hub.verify_token', null); $sub->secret = $this->arg('hub.secret', null); if (strlen($sub->secret) > 200) { throw new ClientException("hub.secret must be no longer than 200 chars", 400); @@ -115,7 +115,7 @@ class PushHubAction extends Action // @fixme check errors ;) - $data = array('sub' => $sub, 'mode' => 'subscribe'); + $data = array('sub' => $sub, 'mode' => 'subscribe', 'token' => $token); $qm = QueueManager::get(); $qm->enqueue($data, 'hubverify'); @@ -130,6 +130,8 @@ class PushHubAction extends Action * 202 Accepted - request saved and awaiting verification * 204 No Content - already subscribed * 400 Bad Request - invalid params or rejected feed + * + * @fixme background this */ function unsubscribe() { @@ -138,7 +140,8 @@ class PushHubAction extends Action $sub = $this->getSub($feed, $callback); if ($sub) { - if ($sub->verify('unsubscribe')) { + $token = $this->arg('hub.verify_token', null); + if ($sub->verify('unsubscribe', $token)) { $sub->delete(); common_log(LOG_INFO, "PuSH unsubscribed $feed for $callback"); } else { diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index 9ca0198266..224134cd7c 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -34,6 +34,8 @@ class SalmonAction extends Action function prepare($args) { + parent::prepare($args); + if ($_SERVER['REQUEST_METHOD'] != 'POST') { $this->clientError(_('This method requires a POST.')); } diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php index 7071ee5b4f..0cd4281f8f 100644 --- a/plugins/OStatus/classes/HubSub.php +++ b/plugins/OStatus/classes/HubSub.php @@ -30,7 +30,6 @@ class HubSub extends Memcached_DataObject public $topic; public $callback; public $secret; - public $verify_token; public $challenge; public $lease; public $sub_start; @@ -62,7 +61,6 @@ class HubSub extends Memcached_DataObject 'topic' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, 'callback' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, 'secret' => DB_DATAOBJECT_STR, - 'verify_token' => DB_DATAOBJECT_STR, 'challenge' => DB_DATAOBJECT_STR, 'lease' => DB_DATAOBJECT_INT, 'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, @@ -84,8 +82,6 @@ class HubSub extends Memcached_DataObject 255, false), new ColumnDef('secret', 'text', null, true), - new ColumnDef('verify_token', 'text', - null, true), new ColumnDef('challenge', 'varchar', 32, true), new ColumnDef('lease', 'int', @@ -154,8 +150,9 @@ class HubSub extends Memcached_DataObject /** * Send a verification ping to subscriber * @param string $mode 'subscribe' or 'unsubscribe' + * @param string $token hub.verify_token value, if provided by client */ - function verify($mode) + function verify($mode, $token=null) { assert($mode == 'subscribe' || $mode == 'unsubscribe'); @@ -172,8 +169,8 @@ class HubSub extends Memcached_DataObject if ($mode == 'subscribe') { $params['hub.lease_seconds'] = $this->lease; } - if ($this->verify_token) { - $params['hub.verify_token'] = $this->verify_token; + if ($token !== null) { + $params['hub.verify_token'] = $token; } $url = $this->callback . '?' . http_build_query($params, '', '&'); // @fixme ugly urls diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index be01cdfe19..486417617c 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -484,7 +484,7 @@ class Ostatus_profile extends Memcached_DataObject } else { $this->sub_end = null; } - $this->lastupdate = common_sql_date(); + $this->lastupdate = common_sql_now(); return $this->update($original); } @@ -497,12 +497,13 @@ class Ostatus_profile extends Memcached_DataObject { $original = clone($this); - $this->verify_token = null; - $this->secret = null; - $this->sub_state = null; - $this->sub_start = null; - $this->sub_end = null; - $this->lastupdate = common_sql_date(); + // @fixme these should all be null, but DB_DataObject doesn't save null values...????? + $this->verify_token = ''; + $this->secret = ''; + $this->sub_state = ''; + $this->sub_start = ''; + $this->sub_end = ''; + $this->lastupdate = common_sql_now(); return $this->update($original); } @@ -527,24 +528,25 @@ class Ostatus_profile extends Memcached_DataObject ':' . $actor->id . ':' . time(); // @fixme - $entry = new Atom10Entry(); + //$entry = new Atom10Entry(); + $entry = new XMLStringer(); $entry->elementStart('entry'); $entry->element('id', null, $id); $entry->element('title', null, $text); $entry->element('summary', null, $text); - $entry->element('published', null, common_date_w3dtf()); + $entry->element('published', null, common_date_w3dtf(time())); $entry->element('activity:verb', null, $verb); - $entry->raw($profile->asAtomAuthor()); - $entry->raw($profile->asActivityActor()); + $entry->raw($actor->asAtomAuthor()); + $entry->raw($actor->asActivityActor()); $entry->raw($object->asActivityNoun('object')); - $entry->elmentEnd('entry'); + $entry->elementEnd('entry'); $feed = $this->atomFeed($actor); - $feed->initFeed(); + #$feed->initFeed(); $feed->addEntry($entry); - $feed->renderEntries(); - $feed->endFeed(); + #$feed->renderEntries(); + #$feed->endFeed(); $xml = $feed->getString(); common_log(LOG_INFO, "Posting to Salmon endpoint $salmon: $xml"); @@ -568,7 +570,7 @@ class Ostatus_profile extends Memcached_DataObject $feed = new Atom10Feed(); // @fixme should these be set up somewhere else? $feed->addNamespace('activity', 'http://activitystrea.ms/spec/1.0/'); - $feed->addNamesapce('thr', 'http://purl.org/syndication/thread/1.0'); + $feed->addNamespace('thr', 'http://purl.org/syndication/thread/1.0'); $feed->addNamespace('georss', 'http://www.georss.org/georss'); $feed->addNamespace('ostatus', 'http://ostatus.org/schema/1.0'); @@ -579,14 +581,14 @@ class Ostatus_profile extends Memcached_DataObject $feed->setUpdated(time()); $feed->setPublished(time()); - $feed->addLink(common_url('ApiTimelineUser', - array('id' => $actor->id, - 'type' => 'atom')), + $feed->addLink(common_local_url('ApiTimelineUser', + array('id' => $actor->id, + 'type' => 'atom')), array('rel' => 'self', 'type' => 'application/atom+xml')); - $feed->addLink(common_url('userbyid', - array('id' => $actor->id)), + $feed->addLink(common_local_url('userbyid', + array('id' => $actor->id)), array('rel' => 'alternate', 'type' => 'text/html')); diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index f137946ab4..3ed613dc7f 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -303,6 +303,12 @@ class ActivityVerb const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend'; const JOIN = 'http://activitystrea.ms/schema/1.0/join'; const TAG = 'http://activitystrea.ms/schema/1.0/tag'; + + // Custom OStatus verbs for the flipside until they're standardized + const DELETE = 'http://ostatus.org/schema/1.0/unfollow'; + const UNFAVORITE = 'http://ostatus.org/schema/1.0/unfavorite'; + const UNFOLLOW = 'http://ostatus.org/schema/1.0/unfollow'; + const LEAVE = 'http://ostatus.org/schema/1.0/leave'; } /** diff --git a/plugins/OStatus/lib/hubverifyqueuehandler.php b/plugins/OStatus/lib/hubverifyqueuehandler.php index 125d13a777..7ce9e14312 100644 --- a/plugins/OStatus/lib/hubverifyqueuehandler.php +++ b/plugins/OStatus/lib/hubverifyqueuehandler.php @@ -33,13 +33,14 @@ class HubVerifyQueueHandler extends QueueHandler { $sub = $data['sub']; $mode = $data['mode']; + $token = $data['token']; assert($sub instanceof HubSub); assert($mode === 'subscribe' || $mode === 'unsubscribe'); common_log(LOG_INFO, __METHOD__ . ": $mode $sub->callback $sub->topic"); try { - $sub->verify($mode); + $sub->verify($mode, $token); } catch (Exception $e) { common_log(LOG_ERR, "Failed PuSH $mode verify to $sub->callback for $sub->topic: " . $e->getMessage());