From: Brion Vibber Date: Mon, 8 Feb 2010 22:06:36 +0000 (-0800) Subject: OStatus cleanup... X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=384387c9b05aefb438f5dbe7e272b1f234ede172;p=quix0rs-gnu-social.git OStatus cleanup... * Treat linkless feed posts as status updates; drop the "New post:" prefix and quotes on them. * Use stable user IDs for atom/rss2 feed links instead of unstable nicknames * Pull Atom feed preferentially when subscribing -- can now put the remote user's profile page straight into the feed subscription form and get to the right place. * Clean up naming for push endpoints --- diff --git a/actions/showstream.php b/actions/showstream.php index 07cc68b765..f9407e35a1 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -131,14 +131,14 @@ class ShowstreamAction extends ProfileAction new Feed(Feed::RSS2, common_local_url('ApiTimelineUser', array( - 'id' => $this->user->nickname, + 'id' => $this->user->id, 'format' => 'rss')), sprintf(_('Notice feed for %s (RSS 2.0)'), $this->user->nickname)), new Feed(Feed::ATOM, common_local_url('ApiTimelineUser', array( - 'id' => $this->user->nickname, + 'id' => $this->user->id, 'format' => 'atom')), sprintf(_('Notice feed for %s (Atom)'), $this->user->nickname)), diff --git a/classes/Notice.php b/classes/Notice.php index f9f3863579..fca1c599ce 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1176,6 +1176,10 @@ class Notice extends Memcached_DataObject // Figure out who that is. $sender = Profile::staticGet('id', $profile_id); + if (empty($sender)) { + return null; + } + $recipient = common_relative_profile($sender, $nickname, common_sql_now()); if (empty($recipient)) { diff --git a/lib/util.php b/lib/util.php index f0f262dc5e..00c21aeb21 100644 --- a/lib/util.php +++ b/lib/util.php @@ -665,6 +665,9 @@ function common_valid_profile_tag($str) function common_at_link($sender_id, $nickname) { $sender = Profile::staticGet($sender_id); + if (!$sender) { + return $nickname; + } $recipient = common_relative_profile($sender, common_canonical_nickname($nickname)); if ($recipient) { $user = User::staticGet('id', $recipient->id); diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 9419121121..4e8b892c6b 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -53,10 +53,10 @@ class OStatusPlugin extends Plugin */ function onRouterInitialized($m) { - $m->connect('push/hub', array('action' => 'hub')); + $m->connect('main/push/hub', array('action' => 'pushhub')); - $m->connect('feedsub/callback/:feed', - array('action' => 'feedsubcallback'), + $m->connect('main/push/callback/:feed', + array('action' => 'pushcallback'), array('feed' => '[0-9]+')); $m->connect('settings/feedsub', array('action' => 'feedsubsettings')); @@ -97,7 +97,7 @@ class OStatusPlugin extends Plugin // Canonical form of id in URL? // Updates will be handled for our internal PuSH hub. $action->element('link', array('rel' => 'hub', - 'href' => common_local_url('hub'))); + 'href' => common_local_url('pushhub'))); } } return true; diff --git a/plugins/OStatus/actions/feedsubcallback.php b/plugins/OStatus/actions/feedsubcallback.php deleted file mode 100644 index c57ea5b101..0000000000 --- a/plugins/OStatus/actions/feedsubcallback.php +++ /dev/null @@ -1,105 +0,0 @@ -. - */ - -/** - * @package FeedSubPlugin - * @maintainer Brion Vibber - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } - - -class FeedSubCallbackAction 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); - } - - $feedinfo = Feedinfo::staticGet('id', $feedid); - if (!$feedinfo) { - throw new ServerException('Unknown feed id ' . $feedid, 400); - } - - $hmac = ''; - if (isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) { - $hmac = $_SERVER['HTTP_X_HUB_SIGNATURE']; - } - - $post = file_get_contents('php://input'); - $feedinfo->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); - } - - $feedinfo = Feedinfo::staticGet('feeduri', $topic); - if (!$feedinfo) { - common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback for unknown feed $topic"); - throw new ServerException("Bogus hub callback: unknown feed", 404); - } - - # Can't currently set the token in our sub api - #if ($feedinfo->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); - #} - - // OK! - common_log(LOG_INFO, __METHOD__ . ': sub confirmed'); - $feedinfo->sub_start = common_sql_date(time()); - if ($lease_seconds > 0) { - $feedinfo->sub_end = common_sql_date(time() + $lease_seconds); - } else { - $feedinfo->sub_end = null; - } - $feedinfo->update(); - - print $challenge; - } -} diff --git a/plugins/OStatus/actions/hub.php b/plugins/OStatus/actions/hub.php deleted file mode 100644 index 5caf4b48eb..0000000000 --- a/plugins/OStatus/actions/hub.php +++ /dev/null @@ -1,176 +0,0 @@ -. - */ - -/** - * Integrated PuSH hub; lets us only ping them what need it. - * @package Hub - * @maintainer Brion Vibber - */ - -/** - - -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 HubAction 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/pushcallback.php b/plugins/OStatus/actions/pushcallback.php new file mode 100644 index 0000000000..a5e02e08f1 --- /dev/null +++ b/plugins/OStatus/actions/pushcallback.php @@ -0,0 +1,105 @@ +. + */ + +/** + * @package FeedSubPlugin + * @maintainer Brion Vibber + */ + +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); + } + + $feedinfo = Feedinfo::staticGet('id', $feedid); + if (!$feedinfo) { + throw new ServerException('Unknown feed id ' . $feedid, 400); + } + + $hmac = ''; + if (isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) { + $hmac = $_SERVER['HTTP_X_HUB_SIGNATURE']; + } + + $post = file_get_contents('php://input'); + $feedinfo->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); + } + + $feedinfo = Feedinfo::staticGet('feeduri', $topic); + if (!$feedinfo) { + common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback for unknown feed $topic"); + throw new ServerException("Bogus hub callback: unknown feed", 404); + } + + # Can't currently set the token in our sub api + #if ($feedinfo->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); + #} + + // OK! + common_log(LOG_INFO, __METHOD__ . ': sub confirmed'); + $feedinfo->sub_start = common_sql_date(time()); + if ($lease_seconds > 0) { + $feedinfo->sub_end = common_sql_date(time() + $lease_seconds); + } else { + $feedinfo->sub_end = null; + } + $feedinfo->update(); + + print $challenge; + } +} diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php new file mode 100644 index 0000000000..901c18f702 --- /dev/null +++ b/plugins/OStatus/actions/pushhub.php @@ -0,0 +1,176 @@ +. + */ + +/** + * Integrated PuSH hub; lets us only ping them what need it. + * @package Hub + * @maintainer Brion Vibber + */ + +/** + + +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/classes/Feedinfo.php b/plugins/OStatus/classes/Feedinfo.php index f29d08cb03..107faf0125 100644 --- a/plugins/OStatus/classes/Feedinfo.php +++ b/plugins/OStatus/classes/Feedinfo.php @@ -248,7 +248,7 @@ class Feedinfo extends Memcached_DataObject #$this->verify_token = $token; #$this->update(); // @fixme try { - $callback = common_local_url('feedsubcallback', array('feed' => $this->id)); + $callback = common_local_url('pushcallback', array('feed' => $this->id)); $headers = array('Content-Type: application/x-www-form-urlencoded'); $post = array('hub.mode' => 'subscribe', 'hub.callback' => $callback, diff --git a/plugins/OStatus/lib/feeddiscovery.php b/plugins/OStatus/lib/feeddiscovery.php index 9bc7892fb2..39985fc902 100644 --- a/plugins/OStatus/lib/feeddiscovery.php +++ b/plugins/OStatus/lib/feeddiscovery.php @@ -168,7 +168,13 @@ class FeedDiscovery } // Ok... now on to the links! + // Types listed in order of priority -- we'll prefer Atom if available. // @fixme merge with the munger link checks + $feeds = array( + 'application/atom+xml' => false, + 'application/rss+xml' => false, + ); + $nodes = $dom->getElementsByTagName('link'); for ($i = 0; $i < $nodes->length; $i++) { $node = $nodes->item($i); @@ -181,17 +187,21 @@ class FeedDiscovery $type = trim($type->value); $href = trim($href->value); - $feedTypes = array( - 'application/rss+xml', - 'application/atom+xml', - ); - if (trim($rel) == 'alternate' && in_array($type, $feedTypes)) { - return $this->resolveURI($href, $base); + if (trim($rel) == 'alternate' && array_key_exists($type, $feeds) && empty($feeds[$type])) { + // Save the first feed found of each type... + $feeds[$type] = $this->resolveURI($href, $base); } } } } + // Return the highest-priority feed found + foreach ($feeds as $type => $url) { + if ($url) { + return $url; + } + } + return false; } diff --git a/plugins/OStatus/lib/feedmunger.php b/plugins/OStatus/lib/feedmunger.php index eeb8d2df39..9480177025 100644 --- a/plugins/OStatus/lib/feedmunger.php +++ b/plugins/OStatus/lib/feedmunger.php @@ -235,34 +235,45 @@ class FeedMunger */ function noticeFromEntry($entry) { + $max = Notice::maxContent(); + $ellipsis = "\xe2\x80\xa6"; // U+2026 HORIZONTAL ELLIPSIS $title = $entry->title; $link = $entry->link; - + // @todo We can get entries like this: // $cats = $entry->getCategory('category', array(0, true)); // but it feels like an awful hack. If it's accessible cleanly, // try adding #hashtags from the categories/tags on a post. - - // @todo Should we force a language here? - $format = _m('New post: "%1$s" %2$s'); + $title = $entry->title; $link = $this->getAltLink($entry); - $out = sprintf($format, $title, $link); - - // Trim link if needed... - $max = Notice::maxContent(); - if (mb_strlen($out) > $max) { - $link = common_shorten_url($link); + if ($link) { + // Blog post or such... + // @todo Should we force a language here? + $format = _m('New post: "%1$s" %2$s'); $out = sprintf($format, $title, $link); - } - // Trim title if needed... - if (mb_strlen($out) > $max) { - $ellipsis = "\xe2\x80\xa6"; // U+2026 HORIZONTAL ELLIPSIS - $used = mb_strlen($out) - mb_strlen($title); - $available = $max - $used - mb_strlen($ellipsis); - $title = mb_substr($title, 0, $available) . $ellipsis; - $out = sprintf($format, $title, $link); + // Trim link if needed... + if (mb_strlen($out) > $max) { + $link = common_shorten_url($link); + $out = sprintf($format, $title, $link); + } + + // Trim title if needed... + if (mb_strlen($out) > $max) { + $used = mb_strlen($out) - mb_strlen($title); + $available = $max - $used - mb_strlen($ellipsis); + $title = mb_substr($title, 0, $available) . $ellipsis; + $out = sprintf($format, $title, $link); + } + } else { + // No link? Consider a bare status update. + if (mb_strlen($title) > $max) { + $available = $max - mb_strlen($ellipsis); + $out = mb_substr($title, 0, $available) . $ellipsis; + } else { + $out = $title; + } } return $out;