diff options
author | Zach Copley <zach@status.net> | 2010-02-15 21:14:01 +0000 |
---|---|---|
committer | Zach Copley <zach@status.net> | 2010-02-15 21:14:01 +0000 |
commit | 82033b3773ac0fc95236716388a02bb6d2da2cab (patch) | |
tree | f58d7a9a220035e63fa13bc36f0922c5f1c8d729 /plugins/OStatus/actions | |
parent | fe2ebec732ecae97b0616cdf627cbaeaf53dab48 (diff) | |
parent | 14a7353fd5583066b154836cccf035e87310ee97 (diff) |
Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline into 0.9.x
Diffstat (limited to 'plugins/OStatus/actions')
-rw-r--r-- | plugins/OStatus/actions/feedsubsettings.php | 269 | ||||
-rw-r--r-- | plugins/OStatus/actions/hostmeta.php | 42 | ||||
-rw-r--r-- | plugins/OStatus/actions/ostatusinit.php | 128 | ||||
-rw-r--r-- | plugins/OStatus/actions/ostatussub.php | 226 | ||||
-rw-r--r-- | plugins/OStatus/actions/pushcallback.php | 107 | ||||
-rw-r--r-- | plugins/OStatus/actions/pushhub.php | 176 | ||||
-rw-r--r-- | plugins/OStatus/actions/salmon.php | 81 | ||||
-rw-r--r-- | plugins/OStatus/actions/webfinger.php | 77 |
8 files changed, 1106 insertions, 0 deletions
diff --git a/plugins/OStatus/actions/feedsubsettings.php b/plugins/OStatus/actions/feedsubsettings.php new file mode 100644 index 000000000..6933c9bf2 --- /dev/null +++ b/plugins/OStatus/actions/feedsubsettings.php @@ -0,0 +1,269 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * 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 + * 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 FeedSubPlugin + * @maintainer Brion Vibber <brion@status.net> + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +class FeedSubSettingsAction extends ConnectSettingsAction +{ + protected $feedurl; + protected $preview; + protected $munger; + + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _m('Feed subscriptions'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _m('You can subscribe to feeds from other sites; ' . + 'updates will appear in your personal timeline.'); + } + + /** + * Content area of the page + * + * Shows a form for associating a Twitter account with this + * StatusNet account. Also lets the user set preferences. + * + * @return void + */ + + function showContent() + { + $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', + 'class' => 'form_settings', + 'action' => + common_local_url('feedsubsettings'))); + + $this->hidden('token', common_session_token()); + + $this->elementStart('fieldset', array('id' => 'settings_feeds')); + + $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->elementEnd('li'); + $this->elementEnd('ul'); + + if ($this->preview) { + $this->submit('subscribe', _m('Subscribe')); + } else { + $this->submit('validate', _m('Continue')); + } + + $this->elementEnd('fieldset'); + + $this->elementEnd('form'); + + if ($this->preview) { + $this->previewFeed(); + } + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + 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 + * + * @return boolean true if feed successfully read + * Sends you back to input form if not. + */ + function validateFeed() + { + $feedurl = trim($this->arg('feedurl')); + + if ($feedurl == '') { + $this->showForm(_m('Empty feed URL!')); + return; + } + $this->feedurl = $feedurl; + + // Get the canonical feed URI and check it + try { + $discover = new FeedDiscovery(); + $uri = $discover->discoverFromURL($feedurl); + } catch (FeedSubBadURLException $e) { + $this->showForm(_m('Invalid URL or could not reach server.')); + return false; + } catch (FeedSubBadResponseException $e) { + $this->showForm(_m('Cannot read feed; server returned error.')); + return false; + } catch (FeedSubEmptyException $e) { + $this->showForm(_m('Cannot read feed; server returned an empty page.')); + return false; + } catch (FeedSubBadHTMLException $e) { + $this->showForm(_m('Bad HTML, could not find feed link.')); + return false; + } catch (FeedSubNoFeedException $e) { + $this->showForm(_m('Could not find a feed linked from this URL.')); + return false; + } catch (FeedSubUnrecognizedTypeException $e) { + $this->showForm(_m('Not a recognized feed type.')); + return false; + } catch (FeedSubException $e) { + // Any new ones we forgot about + $this->showForm(_m('Bad feed URL.')); + return false; + } + + $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; + } + + 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 ($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 { + $local = $this->profile->localProfile(); + if ($user->isSubscribed($local)) { + $this->showForm(_m('Already subscribed!')); + } elseif ($user->subscribeTo($local)) { + $this->showForm(_m('Feed subscribed!')); + } else { + $this->showForm(_m('Feed subscription failed!')); + } + } + } + } + + function validateAndPreview() + { + if ($this->validateFeed()) { + $this->preview = true; + $this->showForm(_m('Previewing feed:')); + } + } + + 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.'); + } + } + + function showScripts() + { + parent::showScripts(); + $this->autofocus('feedurl'); + } +} diff --git a/plugins/OStatus/actions/hostmeta.php b/plugins/OStatus/actions/hostmeta.php new file mode 100644 index 000000000..850b8a0fe --- /dev/null +++ b/plugins/OStatus/actions/hostmeta.php @@ -0,0 +1,42 @@ +<?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 + * @maintainer James Walker <james@status.net> + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +class HostMetaAction extends Action +{ + + function handle() + { + parent::handle(); + + $w = new Webfinger(); + + + $domain = common_config('site', 'server'); + $url = common_local_url('webfinger'); + $url.= '?uri={uri}'; + print $w->getHostMeta($domain, $url); + } +} diff --git a/plugins/OStatus/actions/ostatusinit.php b/plugins/OStatus/actions/ostatusinit.php new file mode 100644 index 000000000..bac2c4d43 --- /dev/null +++ b/plugins/OStatus/actions/ostatusinit.php @@ -0,0 +1,128 @@ +<?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 + * @maintainer James Walker <james@status.net> + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + + +class OStatusInitAction extends Action +{ + + var $nickname; + var $acct; + var $err; + + function prepare($args) + { + parent::prepare($args); + + if (common_logged_in()) { + $this->clientError(_('You can use the local subscription!')); + return false; + } + + $this->nickname = $this->trimmed('nickname'); + $this->acct = $this->trimmed('acct'); + + return true; + } + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + /* 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. '. + 'Try again, please.')); + return; + } + $this->ostatusConnect(); + } else { + $this->showForm(); + } + } + + function showForm($err = null) + { + $this->err = $err; + $this->showPage(); + + } + + function showContent() + { + $this->elementStart('form', array('id' => 'form_ostatus_connect', + 'method' => 'post', + 'class' => 'form_settings', + 'action' => common_local_url('ostatusinit'))); + $this->elementStart('fieldset'); + $this->element('legend', _('Subscribe to a remote user')); + $this->hidden('token', common_session_token()); + + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + $this->input('nickname', _('User nickname'), $this->nickname, + _('Nickname of the user you want to follow')); + $this->elementEnd('li'); + $this->elementStart('li'); + $this->input('acct', _('Profile Account'), $this->acct, + _('Your account id (i.e. user@identi.ca)')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + $this->submit('submit', _('Subscribe')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } + + function ostatusConnect() + { + $w = new Webfinger; + + $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! + + $user = User::staticGet('nickname', $this->nickname); + + $feed_url = common_local_url('ApiTimelineUser', + array('id' => $user->id, + 'format' => 'atom')); + $url = $w->applyTemplate($link['template'], $feed_url); + + common_redirect($url, 303); + } + + } + + } + + function title() + { + return _('OStatus Connect'); + } + +}
\ No newline at end of file diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php new file mode 100644 index 000000000..9774286fd --- /dev/null +++ b/plugins/OStatus/actions/ostatussub.php @@ -0,0 +1,226 @@ +<?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 + * @maintainer James Walker <james@status.net> + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +class OStatusSubAction extends Action +{ + + protected $feedurl; + + function title() + { + return _m("OStatus Subscribe"); + } + + function handle($args) + { + if ($this->validateFeed()) { + $this->showForm(); + } + + return true; + + } + + function showForm($err = null) + { + $this->err = $err; + $this->showPage(); + } + + + function showContent() + { + $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', + 'class' => 'form_settings', + 'action' => + common_local_url('feedsubsettings'))); + + $this->hidden('token', common_session_token()); + + $this->elementStart('fieldset', array('id' => 'settings_feeds')); + + $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->elementEnd('li'); + $this->elementEnd('ul'); + + $this->submit('subscribe', _m('Subscribe')); + + $this->elementEnd('fieldset'); + + $this->elementEnd('form'); + + $this->previewFeed(); + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + if ($this->arg('subscribe')) { + $this->saveFeed(); + } else { + $this->showForm(_('Unexpected form submission.')); + } + } + + + /** + * Set up and add a feed + * + * @return boolean true if feed successfully read + * Sends you back to input form if not. + */ + function validateFeed() + { + $feedurl = $this->trimmed('feed'); + + if ($feedurl == '') { + $this->showForm(_m('Empty feed URL!')); + return; + } + $this->feedurl = $feedurl; + + // Get the canonical feed URI and check it + try { + $discover = new FeedDiscovery(); + $uri = $discover->discoverFromURL($feedurl); + } catch (FeedSubBadURLException $e) { + $this->showForm(_m('Invalid URL or could not reach server.')); + return false; + } catch (FeedSubBadResponseException $e) { + $this->showForm(_m('Cannot read feed; server returned error.')); + return false; + } catch (FeedSubEmptyException $e) { + $this->showForm(_m('Cannot read feed; server returned an empty page.')); + return false; + } catch (FeedSubBadHTMLException $e) { + $this->showForm(_m('Bad HTML, could not find feed link.')); + return false; + } catch (FeedSubNoFeedException $e) { + $this->showForm(_m('Could not find a feed linked from this URL.')); + return false; + } catch (FeedSubUnrecognizedTypeException $e) { + $this->showForm(_m('Not a recognized feed type.')); + return false; + } catch (FeedSubException $e) { + // Any new ones we forgot about + $this->showForm(_m('Bad feed URL.')); + return false; + } + + $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; + } + + 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!')); + } else { + $this->showForm(_m('Feed subscription failed!')); + } + } + } + + + 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.'); + } + } + + +}
\ No newline at end of file diff --git a/plugins/OStatus/actions/pushcallback.php b/plugins/OStatus/actions/pushcallback.php new file mode 100644 index 000000000..2601a377a --- /dev/null +++ b/plugins/OStatus/actions/pushcallback.php @@ -0,0 +1,107 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * 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 + * 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 FeedSubPlugin + * @maintainer Brion Vibber <brion@status.net> + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + + +class PushCallbackAction extends Action +{ + function handle() + { + parent::handle(); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->handlePost(); + } else { + $this->handleGet(); + } + } + + /** + * Handler for POST content updates from the hub + */ + function handlePost() + { + $feedid = $this->arg('feed'); + common_log(LOG_INFO, "POST for feed id $feedid"); + if (!$feedid) { + 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); + } + + $hmac = ''; + if (isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) { + $hmac = $_SERVER['HTTP_X_HUB_SIGNATURE']; + } + + $post = file_get_contents('php://input'); + $profile->postUpdates($post, $hmac); + } + + /** + * Handler for GET verification requests from the hub + */ + function handleGet() + { + $mode = $this->arg('hub_mode'); + $topic = $this->arg('hub_topic'); + $challenge = $this->arg('hub_challenge'); + $lease_seconds = $this->arg('hub_lease_seconds'); + $verify_token = $this->arg('hub_verify_token'); + + if ($mode != 'subscribe' && $mode != 'unsubscribe') { + common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with mode \"$mode\""); + throw new ServerException("Bogus hub callback: bad mode", 404); + } + + $profile = Ostatus_profile::staticGet('feeduri', $topic); + if (!$profile) { + 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) { + 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); + } + + 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}\""); + 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); + } else { + common_log(LOG_INFO, __METHOD__ . ": unsub confirmed; deleting sub record for $topic"); + $profile->confirmUnsubscribe(); + } + print $challenge; + } +} diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php new file mode 100644 index 000000000..901c18f70 --- /dev/null +++ b/plugins/OStatus/actions/pushhub.php @@ -0,0 +1,176 @@ +<?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/>. + */ + +/** + * Integrated PuSH hub; lets us only ping them what need it. + * @package Hub + * @maintainer Brion Vibber <brion@status.net> + */ + +/** + + +Things to consider... +* should we purge incomplete subscriptions that never get a verification pingback? +* when can we send subscription renewal checks? + - at next send time probably ok +* when can we handle trimming of subscriptions? + - at next send time probably ok +* should we keep a fail count? + +*/ + + +class PushHubAction extends Action +{ + function arg($arg, $def=null) + { + // PHP converts '.'s in incoming var names to '_'s. + // It also merges multiple values, which'll break hub.verify and hub.topic for publishing + // @fixme handle multiple args + $arg = str_replace('.', '_', $arg); + return parent::arg($arg, $def); + } + + function prepare($args) + { + StatusNet::setApi(true); // reduce exception reports to aid in debugging + return parent::prepare($args); + } + + function handle() + { + $mode = $this->trimmed('hub.mode'); + switch ($mode) { + case "subscribe": + $this->subscribe(); + break; + case "unsubscribe": + $this->unsubscribe(); + break; + case "publish": + throw new ServerException("Publishing outside feeds not supported.", 400); + default: + throw new ServerException("Unrecognized mode '$mode'.", 400); + } + } + + /** + * Process a PuSH feed subscription request. + * + * HTTP return codes: + * 202 Accepted - request saved and awaiting verification + * 204 No Content - already subscribed + * 403 Forbidden - rejecting this (not specifically spec'd) + */ + function subscribe() + { + $feed = $this->argUrl('hub.topic'); + $callback = $this->argUrl('hub.callback'); + + common_log(LOG_DEBUG, __METHOD__ . ": checking sub'd to $feed $callback"); + if ($this->getSub($feed, $callback)) { + // Already subscribed; return 204 per spec. + header('HTTP/1.1 204 No Content'); + common_log(LOG_DEBUG, __METHOD__ . ': already subscribed'); + return; + } + + common_log(LOG_DEBUG, __METHOD__ . ': setting up'); + $sub = new HubSub(); + $sub->topic = $feed; + $sub->callback = $callback; + $sub->secret = $this->arg('hub.secret', null); + $sub->setLease(intval($this->arg('hub.lease_seconds'))); + + // @fixme check for feeds we don't manage + // @fixme check the verification mode, might want a return immediately? + + common_log(LOG_DEBUG, __METHOD__ . ': inserting'); + $ok = $sub->insert(); + + if (!$ok) { + throw new ServerException("Failed to save subscription record", 500); + } + + // @fixme check errors ;) + + $data = array('sub' => $sub, 'mode' => 'subscribe'); + $qm = QueueManager::get(); + $qm->enqueue($data, 'hubverify'); + + header('HTTP/1.1 202 Accepted'); + common_log(LOG_DEBUG, __METHOD__ . ': done'); + } + + /** + * Process a PuSH feed unsubscription request. + * + * HTTP return codes: + * 202 Accepted - request saved and awaiting verification + * 204 No Content - already subscribed + * 400 Bad Request - invalid params or rejected feed + */ + function unsubscribe() + { + $feed = $this->argUrl('hub.topic'); + $callback = $this->argUrl('hub.callback'); + $sub = $this->getSub($feed, $callback); + + if ($sub) { + if ($sub->verify('unsubscribe')) { + $sub->delete(); + common_log(LOG_INFO, "PuSH unsubscribed $feed for $callback"); + } else { + throw new ServerException("Failed PuSH unsubscription: verification failed! $feed for $callback"); + } + } else { + throw new ServerException("Failed PuSH unsubscription: not subscribed! $feed for $callback"); + } + } + + /** + * Grab and validate a URL from POST parameters. + * @throws ServerException for malformed or non-http/https URLs + */ + protected function argUrl($arg) + { + $url = $this->arg($arg); + $params = array('domain_check' => false, // otherwise breaks my local tests :P + 'allowed_schemes' => array('http', 'https')); + if (Validate::uri($url, $params)) { + return $url; + } else { + throw new ServerException("Invalid URL passed for $arg: '$url'", 400); + } + } + + /** + * Get HubSub subscription record for a given feed & subscriber. + * + * @param string $feed + * @param string $callback + * @return mixed HubSub or false + */ + protected function getSub($feed, $callback) + { + return HubSub::staticGet($feed, $callback); + } +} + diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php new file mode 100644 index 000000000..b616027a9 --- /dev/null +++ b/plugins/OStatus/actions/salmon.php @@ -0,0 +1,81 @@ +<?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 = Activity::fromAtomEntry($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/webfinger.php b/plugins/OStatus/actions/webfinger.php new file mode 100644 index 000000000..75ba16638 --- /dev/null +++ b/plugins/OStatus/actions/webfinger.php @@ -0,0 +1,77 @@ +<?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 + * @maintainer James Walker <james@status.net> + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +class WebfingerAction extends Action +{ + + public $uri; + + function prepare($args) + { + parent::prepare($args); + + $this->uri = $this->trimmed('uri'); + + return true; + } + + function handle() + { + $acct = Webfinger::normalize($this->uri); + + $xrd = new XRD(); + + list($nick, $domain) = explode('@', urldecode($acct)); + $nick = common_canonical_nickname($nick); + + $this->user = User::staticGet('nickname', $nick); + if (!$this->user) { + $this->clientError(_('No such user.'), 404); + return false; + } + + $xrd->subject = $this->uri; + $xrd->alias[] = common_profile_url($nick); + $xrd->links[] = array('rel' => 'http://webfinger.net/rel/profile-page', + 'type' => 'text/html', + 'href' => common_profile_url($nick)); + + $salmon_url = common_local_url('salmon', + array('id' => $this->user->id)); + + $xrd->links[] = array('rel' => 'salmon', + 'href' => $salmon_url); + + // TODO - finalize where the redirect should go on the publisher + $url = common_local_url('ostatussub') . '?feed={uri}'; + $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/subscribe', + 'template' => $url ); + + header('Content-type: text/xml'); + print $xrd->toXML(); + } + +} |