diff options
author | Brion Vibber <brion@pobox.com> | 2010-02-21 11:12:56 -0800 |
---|---|---|
committer | Brion Vibber <brion@pobox.com> | 2010-02-21 11:12:56 -0800 |
commit | 85528ccb1f5840a8c73f6c939f12d98bb3680cde (patch) | |
tree | be0f09c02a2bf003c23d6be98919783347b9786d /plugins/OStatus/actions | |
parent | 86f2f530ef60fdb601720885d493cf5b2a446e6b (diff) | |
parent | 3e7a2a4014dd93637f5a666e238dde13e397523c (diff) |
Merge branch 'testing' of gitorious.org:statusnet/mainline into 0.9.x
Diffstat (limited to 'plugins/OStatus/actions')
-rw-r--r-- | plugins/OStatus/actions/feedsubsettings.php | 107 | ||||
-rw-r--r-- | plugins/OStatus/actions/groupsalmon.php | 108 | ||||
-rw-r--r-- | plugins/OStatus/actions/ostatusinit.php | 78 | ||||
-rw-r--r-- | plugins/OStatus/actions/ostatussub.php | 232 | ||||
-rw-r--r-- | plugins/OStatus/actions/pushcallback.php | 24 | ||||
-rw-r--r-- | plugins/OStatus/actions/pushhub.php | 9 | ||||
-rw-r--r-- | plugins/OStatus/actions/salmon.php | 81 | ||||
-rw-r--r-- | plugins/OStatus/actions/usersalmon.php | 202 | ||||
-rw-r--r-- | plugins/OStatus/actions/webfinger.php | 2 |
9 files changed, 555 insertions, 288 deletions
diff --git a/plugins/OStatus/actions/feedsubsettings.php b/plugins/OStatus/actions/feedsubsettings.php index 6933c9bf2..aee4cee9a 100644 --- a/plugins/OStatus/actions/feedsubsettings.php +++ b/plugins/OStatus/actions/feedsubsettings.php @@ -26,7 +26,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } class FeedSubSettingsAction extends ConnectSettingsAction { - protected $feedurl; + protected $profile_uri; protected $preview; protected $munger; @@ -68,14 +68,6 @@ class FeedSubSettingsAction extends ConnectSettingsAction $profile = $user->getProfile(); - $fuser = null; - - $flink = Foreign_link::getByUserID($user->id, FEEDSUB_SERVICE); - - if (!empty($flink)) { - $fuser = $flink->getForeignUser(); - } - $this->elementStart('form', array('method' => 'post', 'id' => 'form_settings_feedsub', 'class' => 'form_settings', @@ -88,7 +80,10 @@ class FeedSubSettingsAction extends ConnectSettingsAction $this->elementStart('ul', 'form_data'); $this->elementStart('li', array('id' => 'settings_twitter_login_button')); - $this->input('feedurl', _('Feed URL'), $this->feedurl, _('Enter the URL of a PubSubHubbub-enabled feed')); + $this->input('profile_uri', + _m('Feed URL'), + $this->profile_uri, + _m('Enter the profile URL of a PubSubHubbub-enabled feed')); $this->elementEnd('li'); $this->elementEnd('ul'); @@ -145,79 +140,55 @@ class FeedSubSettingsAction extends ConnectSettingsAction */ function validateFeed() { - $feedurl = trim($this->arg('feedurl')); + $profile_uri = trim($this->arg('profile_uri')); - if ($feedurl == '') { - $this->showForm(_m('Empty feed URL!')); + if ($profile_uri == '') { + $this->showForm(_m('Empty remote profile URL!')); return; } - $this->feedurl = $feedurl; + $this->profile_uri = $profile_uri; - // Get the canonical feed URI and check it + // @fixme validate, normalize bla bla try { - $discover = new FeedDiscovery(); - $uri = $discover->discoverFromURL($feedurl); + $oprofile = Ostatus_profile::ensureProfile($this->profile_uri); + $this->oprofile = $oprofile; + return true; } catch (FeedSubBadURLException $e) { - $this->showForm(_m('Invalid URL or could not reach server.')); - return false; + $err = _m('Invalid URL or could not reach server.'); } catch (FeedSubBadResponseException $e) { - $this->showForm(_m('Cannot read feed; server returned error.')); - return false; + $err = _m('Cannot read feed; server returned error.'); } catch (FeedSubEmptyException $e) { - $this->showForm(_m('Cannot read feed; server returned an empty page.')); - return false; + $err = _m('Cannot read feed; server returned an empty page.'); } catch (FeedSubBadHTMLException $e) { - $this->showForm(_m('Bad HTML, could not find feed link.')); - return false; + $err = _m('Bad HTML, could not find feed link.'); } catch (FeedSubNoFeedException $e) { - $this->showForm(_m('Could not find a feed linked from this URL.')); - return false; + $err = _m('Could not find a feed linked from this URL.'); } catch (FeedSubUnrecognizedTypeException $e) { - $this->showForm(_m('Not a recognized feed type.')); - return false; + $err = _m('Not a recognized feed type.'); } catch (FeedSubException $e) { // Any new ones we forgot about - $this->showForm(_m('Bad feed URL.')); - return false; + $err = sprintf(_m('Bad feed URL: %s %s'), get_class($e), $e->getMessage()); } - - $this->munger = $discover->feedMunger(); - $this->profile = $this->munger->ostatusProfile(); - if ($this->profile->huburi == '' && !common_config('feedsub', 'nohub')) { - $this->showForm(_m('Feed is not PuSH-enabled; cannot subscribe.')); - return false; - } - - return true; + $this->showForm($err); + return false; } function saveFeed() { if ($this->validateFeed()) { $this->preview = true; - $this->profile = Ostatus_profile::ensureProfile($this->munger); - if (!$this->profile) { - throw new ServerException("Feed profile was not saved properly."); - } - - // If not already in use, subscribe to updates via the hub - if ($this->profile->sub_start) { - common_log(LOG_INFO, __METHOD__ . ": double the fun! new sub for {$this->profile->feeduri} last subbed {$this->profile->sub_start}"); - } else { - $ok = $this->profile->subscribe(); - common_log(LOG_INFO, __METHOD__ . ": sub was $ok"); - if (!$ok) { - $this->showForm(_m('Feed subscription failed! Bad response from hub.')); - return; - } - } // And subscribe the current user to the local profile $user = common_current_user(); - if ($this->profile->isGroup()) { - $group = $this->profile->localGroup(); + if (!$this->oprofile->subscribe()) { + $this->showForm(_m("Failed to set up server-to-server subscription.")); + return; + } + + if ($this->oprofile->isGroup()) { + $group = $this->oprofile->localGroup(); if ($user->isMember($group)) { $this->showForm(_m('Already a member!')); } elseif (Group_member::join($this->profile->group_id, $user->id)) { @@ -226,13 +197,13 @@ class FeedSubSettingsAction extends ConnectSettingsAction $this->showForm(_m('Remote group join failed!')); } } else { - $local = $this->profile->localProfile(); + $local = $this->oprofile->localProfile(); if ($user->isSubscribed($local)) { $this->showForm(_m('Already subscribed!')); - } elseif ($user->subscribeTo($local)) { - $this->showForm(_m('Feed subscribed!')); + } elseif ($this->oprofile->subscribeLocalToRemote($user)) { + $this->showForm(_m('Remote user subscribed!')); } else { - $this->showForm(_m('Feed subscription failed!')); + $this->showForm(_m('Remote subscription failed!')); } } } @@ -248,17 +219,7 @@ class FeedSubSettingsAction extends ConnectSettingsAction function previewFeed() { - $profile = $this->munger->ostatusProfile(); - $notice = $this->munger->notice(0, true); // preview - - if ($notice) { - $this->element('b', null, 'Preview of latest post from this feed:'); - - $item = new NoticeList($notice, $this); - $item->show(); - } else { - $this->element('b', null, 'No posts in this feed yet.'); - } + $this->text('Profile preview should go here'); } function showScripts() diff --git a/plugins/OStatus/actions/groupsalmon.php b/plugins/OStatus/actions/groupsalmon.php new file mode 100644 index 000000000..64ae9f3cc --- /dev/null +++ b/plugins/OStatus/actions/groupsalmon.php @@ -0,0 +1,108 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @package OStatusPlugin + * @author James Walker <james@status.net> + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class GroupsalmonAction extends SalmonAction +{ + var $group = null; + + function prepare($args) + { + parent::prepare($args); + + $id = $this->trimmed('id'); + + if (!$id) { + $this->clientError(_('No ID.')); + } + + $this->group = User_group::staticGet('id', $id); + + if (empty($this->group)) { + $this->clientError(_('No such group.')); + } + + return true; + } + + /** + * We've gotten a post event on the Salmon backchannel, probably a reply. + */ + + function handlePost() + { + switch ($this->act->object->type) { + case ActivityObject::ARTICLE: + case ActivityObject::BLOGENTRY: + case ActivityObject::NOTE: + case ActivityObject::STATUS: + case ActivityObject::COMMENT: + break; + default: + throw new ClientException("Can't handle that kind of post."); + } + + // Notice must be to the attention of this group + + $context = $this->act->context; + + if (empty($context->attention)) { + throw new ClientException("Not to the attention of anyone."); + } else { + $uri = common_local_url('groupbyid', array('id' => $this->group->id)); + if (!in_array($context->attention, $uri)) { + throw new ClientException("Not to the attention of this group."); + } + } + + $profile = $this->ensureProfile(); + // @fixme save the post + } + + /** + * We've gotten a follow/subscribe notification from a remote user. + * Save a subscription relationship for them. + */ + + function handleFollow() + { + $this->handleJoin(); // ??? + } + + function handleUnfollow() + { + } + + /** + * A remote user joined our group. + */ + + function handleJoin() + { + } + +} diff --git a/plugins/OStatus/actions/ostatusinit.php b/plugins/OStatus/actions/ostatusinit.php index d21774420..4afde2c36 100644 --- a/plugins/OStatus/actions/ostatusinit.php +++ b/plugins/OStatus/actions/ostatusinit.php @@ -37,7 +37,7 @@ class OStatusInitAction extends Action parent::prepare($args); if (common_logged_in()) { - $this->clientError(_('You can use the local subscription!')); + $this->clientError(_m('You can use the local subscription!')); return false; } @@ -55,7 +55,7 @@ class OStatusInitAction extends Action /* Use a session token for CSRF protection. */ $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { - $this->showForm(_('There was a problem with your session token. '. + $this->showForm(_m('There was a problem with your session token. '. 'Try again, please.')); return; } @@ -73,7 +73,7 @@ class OStatusInitAction extends Action $this->xw->startDocument('1.0', 'UTF-8'); $this->elementStart('html'); $this->elementStart('head'); - $this->element('title', null, _('Subscribe to user')); + $this->element('title', null, _m('Subscribe to user')); $this->elementEnd('head'); $this->elementStart('body'); $this->showContent(); @@ -91,50 +91,78 @@ class OStatusInitAction extends Action 'class' => 'form_settings', 'action' => common_local_url('ostatusinit'))); $this->elementStart('fieldset'); - $this->element('legend', null, sprintf(_('Subscribe to %s'), $this->nickname)); + $this->element('legend', null, sprintf(_m('Subscribe to %s'), $this->nickname)); $this->hidden('token', common_session_token()); $this->elementStart('ul', 'form_data'); $this->elementStart('li', array('id' => 'ostatus_nickname')); - $this->input('nickname', _('User nickname'), $this->nickname, - _('Nickname of the user you want to follow')); + $this->input('nickname', _m('User nickname'), $this->nickname, + _m('Nickname of the user you want to follow')); $this->elementEnd('li'); $this->elementStart('li', array('id' => 'ostatus_profile')); - $this->input('acct', _('Profile Account'), $this->acct, - _('Your account id (i.e. user@identi.ca)')); + $this->input('acct', _m('Profile Account'), $this->acct, + _m('Your account id (i.e. user@identi.ca)')); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('submit', _('Subscribe')); + $this->submit('submit', _m('Subscribe')); $this->elementEnd('fieldset'); $this->elementEnd('form'); } function ostatusConnect() { - $w = new Webfinger; + $opts = array('allowed_schemes' => array('http', 'https', 'acct')); + if (Validate::uri($this->acct, $opts)) { + $bits = parse_url($this->acct); + if ($bits['scheme'] == 'acct') { + $this->connectWebfinger($bits['path']); + } else { + $this->connectProfile($this->acct); + } + } elseif (strpos('@', $this->acct) !== false) { + $this->connectWebfinger($this->acct); + } + } + + function connectWebfinger($acct) + { + $w = new Webfinger; + + $result = $w->lookup($acct); + if (!$result) { + $this->clientError(_m("Couldn't look up OStatus account profile.")); + } + foreach ($result->links as $link) { + if ($link['rel'] == 'http://ostatus.org/schema/1.0/subscribe') { + // We found a URL - let's redirect! + + $user = User::staticGet('nickname', $this->nickname); + $target_profile = common_local_url('userbyid', array('id' => $user->id)); - $result = $w->lookup($this->acct); - foreach ($result->links as $link) { - if ($link['rel'] == 'http://ostatus.org/schema/1.0/subscribe') { - // We found a URL - let's redirect! + $url = $w->applyTemplate($link['template'], $feed_url); - $user = User::staticGet('nickname', $this->nickname); + common_redirect($url, 303); + } + + } + + } - $feed_url = common_local_url('ApiTimelineUser', - array('id' => $user->id, - 'format' => 'atom')); - $url = $w->applyTemplate($link['template'], $feed_url); + function connectProfile($subscriber_profile) + { + $user = User::staticGet('nickname', $this->nickname); + $target_profile = common_local_url('userbyid', array('id' => $user->id)); - common_redirect($url, 303); - } + // @fixme hack hack! We should look up the remote sub URL from XRDS + $suburl = preg_replace('!^(.*)/(.*?)$!', '$1/main/ostatussub', $subscriber_profile); + $suburl .= '?profile=' . urlencode($target_profile); - } - + common_redirect($suburl, 303); } - + function title() { - return _('OStatus Connect'); + return _m('OStatus Connect'); } } diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index 239122501..bbbd1b7e6 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -1,7 +1,7 @@ <?php /* * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2010, StatusNet, Inc. + * Copyright (C) 2009, StatusNet, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -19,57 +19,70 @@ /** * @package OStatusPlugin - * @maintainer James Walker <james@status.net> + * @maintainer Brion Vibber <brion@status.net> */ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } class OStatusSubAction extends Action { + protected $profile_uri; + protected $preview; + protected $munger; + + /** + * Title of the page + * + * @return string Title of the page + */ - protected $feedurl; - function title() { - return _m("OStatus Subscribe"); + return _m('Authorize subscription'); } - function handle($args) - { - if ($this->validateFeed()) { - $this->showForm(); - } - - return true; + /** + * Instructions for use + * + * @return instructions for use + */ + function getInstructions() + { + return _m('You can subscribe to users from other supported sites. Paste their address or profile URI below:'); } - function showForm($err = null) + function showForm($error=null) { - $this->err = $err; + $this->error = $error; $this->showPage(); } + /** + * Content area of the page + * + * Shows a form for associating a remote OStatus account with this + * StatusNet account. + * + * @return void + */ function showContent() { + // @fixme is this right place? + if ($this->error) { + $this->text($this->error); + } + $user = common_current_user(); $profile = $user->getProfile(); - $fuser = null; - - $flink = Foreign_link::getByUserID($user->id, FEEDSUB_SERVICE); - - if (!empty($flink)) { - $fuser = $flink->getForeignUser(); - } - $this->elementStart('form', array('method' => 'post', - 'id' => 'form_settings_feedsub', + 'id' => 'ostatus_sub', 'class' => 'form_settings', 'action' => - common_local_url('feedsubsettings'))); + common_local_url('ostatussub'))); $this->hidden('token', common_session_token()); @@ -77,17 +90,58 @@ class OStatusSubAction extends Action $this->elementStart('ul', 'form_data'); $this->elementStart('li'); - $this->input('feedurl', _('Feed URL'), $this->feedurl, _('Enter the URL of a PubSubHubbub-enabled feed')); + $this->input('profile', + _m('Address or profile URL'), + $this->profile_uri, + _m('Enter the profile URL of a PubSubHubbub-enabled feed')); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('subscribe', _m('Subscribe')); + if ($this->preview) { + $this->submit('subscribe', _m('Subscribe')); + } else { + $this->submit('validate', _m('Continue')); + } $this->elementEnd('fieldset'); $this->elementEnd('form'); - $this->previewFeed(); + if ($this->preview) { + $this->previewFeed(); + } + } + + function prepare($args) + { + parent::prepare($args); + + if (!common_logged_in()) { + // XXX: selfURL() didn't work. :< + common_set_returnto($_SERVER['REQUEST_URI']); + if (Event::handle('RedirectToLogin', array($this, null))) { + common_redirect(common_local_url('login'), 303); + } + return false; + } + + $this->profile_uri = $this->arg('profile'); + + return true; + } + + function handle($args) + { + parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->handlePost(); + } else { + if ($this->profile_uri) { + $this->validateAndPreview(); + } else { + $this->showPage(); + } + } } /** @@ -111,14 +165,15 @@ class OStatusSubAction extends Action return; } - if ($this->arg('subscribe')) { + if ($this->arg('validate')) { + $this->validateAndPreview(); + } else if ($this->arg('subscribe')) { $this->saveFeed(); } else { $this->showForm(_('Unexpected form submission.')); } } - /** * Set up and add a feed * @@ -127,100 +182,91 @@ class OStatusSubAction extends Action */ function validateFeed() { - $feedurl = $this->trimmed('feed'); - - if ($feedurl == '') { - $this->showForm(_m('Empty feed URL!')); + $profile_uri = trim($this->arg('profile')); + + if ($profile_uri == '') { + $this->showForm(_m('Empty remote profile URL!')); return; } - $this->feedurl = $feedurl; - - // Get the canonical feed URI and check it + $this->profile_uri = $profile_uri; + + // @fixme validate, normalize bla bla try { - $discover = new FeedDiscovery(); - $uri = $discover->discoverFromURL($feedurl); + $oprofile = Ostatus_profile::ensureProfile($this->profile_uri); + $this->oprofile = $oprofile; + return true; } catch (FeedSubBadURLException $e) { - $this->showForm(_m('Invalid URL or could not reach server.')); - return false; + $err = _m('Invalid URL or could not reach server.'); } catch (FeedSubBadResponseException $e) { - $this->showForm(_m('Cannot read feed; server returned error.')); - return false; + $err = _m('Cannot read feed; server returned error.'); } catch (FeedSubEmptyException $e) { - $this->showForm(_m('Cannot read feed; server returned an empty page.')); - return false; + $err = _m('Cannot read feed; server returned an empty page.'); } catch (FeedSubBadHTMLException $e) { - $this->showForm(_m('Bad HTML, could not find feed link.')); - return false; + $err = _m('Bad HTML, could not find feed link.'); } catch (FeedSubNoFeedException $e) { - $this->showForm(_m('Could not find a feed linked from this URL.')); - return false; + $err = _m('Could not find a feed linked from this URL.'); } catch (FeedSubUnrecognizedTypeException $e) { - $this->showForm(_m('Not a recognized feed type.')); - return false; + $err = _m('Not a recognized feed type.'); } catch (FeedSubException $e) { // Any new ones we forgot about - $this->showForm(_m('Bad feed URL.')); - return false; + $err = sprintf(_m('Bad feed URL: %s %s'), get_class($e), $e->getMessage()); } - - $this->munger = $discover->feedMunger(); - $this->profile = $this->munger->ostatusProfile(); - if ($this->profile->huburi == '') { - $this->showForm(_m('Feed is not PuSH-enabled; cannot subscribe.')); - return false; - } - - return true; + $this->showForm($err); + return false; } function saveFeed() { if ($this->validateFeed()) { $this->preview = true; - $this->profile = Ostatus_profile::ensureProfile($this->munger); - // If not already in use, subscribe to updates via the hub - if ($this->profile->sub_start) { - common_log(LOG_INFO, __METHOD__ . ": double the fun! new sub for {$this->profile->feeduri} last subbed {$this->profile->sub_start}"); - } else { - $ok = $this->profile->subscribe(); - common_log(LOG_INFO, __METHOD__ . ": sub was $ok"); - if (!$ok) { - $this->showForm(_m('Feed subscription failed! Bad response from hub.')); - return; - } - } - // And subscribe the current user to the local profile $user = common_current_user(); - $profile = $this->profile->getProfile(); - - if ($user->isSubscribed($profile)) { - $this->showForm(_m('Already subscribed!')); - } elseif ($user->subscribeTo($profile)) { - $this->showForm(_m('Feed subscribed!')); + + if (!$this->oprofile->subscribe()) { + $this->showForm(_m("Failed to set up server-to-server subscription.")); + return; + } + + if ($this->oprofile->isGroup()) { + $group = $this->oprofile->localGroup(); + if ($user->isMember($group)) { + $this->showForm(_m('Already a member!')); + } elseif (Group_member::join($this->profile->group_id, $user->id)) { + $this->showForm(_m('Joined remote group!')); + } else { + $this->showForm(_m('Remote group join failed!')); + } } else { - $this->showForm(_m('Feed subscription failed!')); + $local = $this->oprofile->localProfile(); + if ($user->isSubscribed($local)) { + $this->showForm(_m('Already subscribed!')); + } elseif ($this->oprofile->subscribeLocalToRemote($user)) { + $this->showForm(_m('Remote user subscribed!')); + } else { + $this->showForm(_m('Remote subscription failed!')); + } } } } - - function previewFeed() + function validateAndPreview() { - $profile = $this->munger->ostatusProfile(); - $notice = $this->munger->notice(0, true); // preview - - if ($notice) { - $this->element('b', null, 'Preview of latest post from this feed:'); - - $item = new NoticeList($notice, $this); - $item->show(); - } else { - $this->element('b', null, 'No posts in this feed yet.'); + if ($this->validateFeed()) { + $this->preview = true; + $this->showForm(_m('Previewing feed:')); } } + function previewFeed() + { + $this->text('Profile preview should go here'); + } + function showScripts() + { + parent::showScripts(); + $this->autofocus('feedurl'); + } } diff --git a/plugins/OStatus/actions/pushcallback.php b/plugins/OStatus/actions/pushcallback.php index 388c8f9c3..7e1227a66 100644 --- a/plugins/OStatus/actions/pushcallback.php +++ b/plugins/OStatus/actions/pushcallback.php @@ -48,9 +48,9 @@ class PushCallbackAction extends Action throw new ServerException('Empty or invalid feed id', 400); } - $profile = Ostatus_profile::staticGet('id', $feedid); - if (!$profile) { - throw new ServerException('Unknown OStatus/PuSH feed id ' . $feedid, 400); + $feedsub = FeedSub::staticGet('id', $feedid); + if (!$feedsub) { + throw new ServerException('Unknown PuSH feed id ' . $feedid, 400); } $hmac = ''; @@ -62,7 +62,7 @@ class PushCallbackAction extends Action // @fixme Queue this to a background process; we should return // as quickly as possible from a distribution POST. - $profile->postUpdates($post, $hmac); + $feedsub->receive($post, $hmac); } /** @@ -81,29 +81,29 @@ class PushCallbackAction extends Action throw new ServerException("Bogus hub callback: bad mode", 404); } - $profile = Ostatus_profile::staticGet('feeduri', $topic); - if (!$profile) { + $feedsub = FeedSub::staticGet('uri', $topic); + if (!$feedsub) { common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback for unknown feed $topic"); throw new ServerException("Bogus hub callback: unknown feed", 404); } - if ($profile->verify_token !== $verify_token) { + if ($feedsub->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) { - common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad mode \"$mode\" for feed $topic in state \"{$profile->sub_state}\""); + if ($mode != $feedsub->sub_state) { + common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad mode \"$mode\" for feed $topic in state \"{$feedsub->sub_state}\""); throw new ServerException("Bogus hub callback: mode doesn't match subscription state.", 404); } // OK! if ($mode == 'subscribe') { common_log(LOG_INFO, __METHOD__ . ': sub confirmed'); - $profile->confirmSubscribe($lease_seconds); + $feedsub->confirmSubscribe($lease_seconds); } else { common_log(LOG_INFO, __METHOD__ . ": unsub confirmed; deleting sub record for $topic"); - $profile->confirmUnsubscribe(); + $feedsub->confirmUnsubscribe(); } print $challenge; } diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php index 13ec09d52..19599d815 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 deleted file mode 100644 index c79d09c95..000000000 --- a/plugins/OStatus/actions/salmon.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -/* - * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2010, StatusNet, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** - * @package OStatusPlugin - * @author James Walker <james@status.net> - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -class SalmonAction extends Action -{ - var $user = null; - var $xml = null; - var $activity = null; - - function prepare($args) - { - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - $this->clientError(_('This method requires a POST.')); - } - - if ($_SERVER['CONTENT_TYPE'] != 'application/atom+xml') { - $this->clientError(_('Salmon requires application/atom+xml')); - } - - $id = $this->trimmed('id'); - - if (!$id) { - $this->clientError(_('No ID.')); - } - - $this->user = User::staticGet($id); - - if (empty($this->user)) { - $this->clientError(_('No such user.')); - } - - $xml = file_get_contents('php://input'); - - $dom = DOMDocument::loadXML($xml); - - // XXX: check that document element is Atom entry - // XXX: check the signature - - $this->act = new Activity($dom->documentElement); - } - - function handle($args) - { - common_log(LOG_DEBUG, 'Salmon: incoming post for user: '. $user_id); - - // TODO : Insert new $xml -> notice code - - switch ($this->act->verb) - { - case Activity::POST: - case Activity::SHARE: - case Activity::FAVORITE: - case Activity::FOLLOW: - } - } -} diff --git a/plugins/OStatus/actions/usersalmon.php b/plugins/OStatus/actions/usersalmon.php new file mode 100644 index 000000000..12c74798f --- /dev/null +++ b/plugins/OStatus/actions/usersalmon.php @@ -0,0 +1,202 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @package OStatusPlugin + * @author James Walker <james@status.net> + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class UsersalmonAction extends SalmonAction +{ + function prepare($args) + { + parent::prepare($args); + + $id = $this->trimmed('id'); + + if (!$id) { + $this->clientError(_('No ID.')); + } + + $this->user = User::staticGet('id', $id); + + if (empty($this->user)) { + $this->clientError(_('No such user.')); + } + + return true; + } + + /** + * We've gotten a post event on the Salmon backchannel, probably a reply. + * + * @todo validate if we need to handle this post, then call into + * ostatus_profile's general incoming-post handling. + */ + function handlePost() + { + switch ($this->act->object->type) { + case ActivityObject::ARTICLE: + case ActivityObject::BLOGENTRY: + case ActivityObject::NOTE: + case ActivityObject::STATUS: + case ActivityObject::COMMENT: + break; + default: + throw new ClientException("Can't handle that kind of post."); + } + + // Notice must either be a) in reply to a notice by this user + // or b) to the attention of this user + + $context = $this->act->context; + + if (!empty($context->replyToID)) { + $notice = Notice::staticGet('uri', $context->replyToID); + if (empty($notice)) { + throw new ClientException("In reply to unknown notice"); + } + if ($notice->profile_id != $this->user->id) { + throw new ClientException("In reply to a notice not by this user"); + } + } else if (!empty($context->attention)) { + if (!in_array($context->attention, $this->user->uri)) { + throw new ClientException("To the attention of user(s) not including this one!"); + } + } else { + throw new ClientException("Not to anyone in reply to anything!"); + } + + $this->saveNotice(); + } + + /** + * We've gotten a follow/subscribe notification from a remote user. + * Save a subscription relationship for them. + */ + + function handleFollow() + { + $oprofile = $this->ensureProfile(); + if ($oprofile) { + common_log(LOG_INFO, "Setting up subscription from remote {$oprofile->uri} to local {$this->user->nickname}"); + Subscription::start($oprofile->localProfile(), + $this->user->getProfile()); + } else { + common_log(LOG_INFO, "Can't set up subscription from remote; missing profile."); + } + } + + /** + * We've gotten an unfollow/unsubscribe notification from a remote user. + * Check if we have a subscription relationship for them and kill it. + * + * @fixme probably catch exceptions on fail? + */ + function handleUnfollow() + { + $oprofile = $this->ensureProfile(); + if ($oprofile) { + common_log(LOG_INFO, "Canceling subscription from remote {$oprofile->uri} to local {$this->user->nickname}"); + Subscription::cancel($oprofile->localProfile(), $this->user->getProfile()); + } else { + common_log(LOG_ERR, "Can't cancel subscription from remote, didn't find the profile"); + } + } + + /** + * Remote user likes one of our posts. + * Confirm the post is ours, and save a local favorite event. + */ + + function handleFavorite() + { + $notice = $this->getNotice($this->act->object); + $profile = $this->ensureProfile()->localProfile(); + + $old = Fave::pkeyGet(array('user_id' => $profile->id, + 'notice_id' => $notice->id)); + + if (!empty($old)) { + throw new ClientException("We already know that's a fave!"); + } + + if (!Fave::addNew($profile, $notice)) { + throw new ClientException("Could not save new favorite."); + } + } + + /** + * Remote user doesn't like one of our posts after all! + * Confirm the post is ours, and save a local favorite event. + */ + function handleUnfavorite() + { + $notice = $this->getNotice($this->act->object); + $profile = $this->ensureProfile()->localProfile(); + + $fave = Fave::pkeyGet(array('user_id' => $profile->id, + 'notice_id' => $notice->id)); + if (empty($fave)) { + throw new ClientException("Notice wasn't favorited!"); + } + + $fave->delete(); + } + + /** + * @param ActivityObject $object + * @return Notice + * @throws ClientException on invalid input + */ + function getNotice($object) + { + if (!$object) { + throw new ClientException("Can't favorite/unfavorite without an object."); + } + + switch ($object->type) { + case ActivityObject::ARTICLE: + case ActivityObject::BLOGENTRY: + case ActivityObject::NOTE: + case ActivityObject::STATUS: + case ActivityObject::COMMENT: + break; + default: + throw new ClientException("Can't handle that kind of object for liking/faving."); + } + + $notice = Notice::staticGet('uri', $object->id); + + if (empty($notice)) { + throw new ClientException("Notice with ID $object->id unknown."); + } + + if ($notice->profile_id != $this->user->id) { + throw new ClientException("Notice with ID $object->id not posted by $this->user->id."); + } + + return $notice; + } + +} diff --git a/plugins/OStatus/actions/webfinger.php b/plugins/OStatus/actions/webfinger.php index 75ba16638..f4dc61b7d 100644 --- a/plugins/OStatus/actions/webfinger.php +++ b/plugins/OStatus/actions/webfinger.php @@ -66,7 +66,7 @@ class WebfingerAction extends Action 'href' => $salmon_url); // TODO - finalize where the redirect should go on the publisher - $url = common_local_url('ostatussub') . '?feed={uri}'; + $url = common_local_url('ostatussub') . '?profile={uri}'; $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/subscribe', 'template' => $url ); |