From 2e58802cc9959763f28e2f43c8e0cd0dbe7bcd8e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 02:19:13 +0000 Subject: OStatus: fix group delivery, send reply/group Salmon pings from background. --- plugins/OStatus/lib/ostatusqueuehandler.php | 223 ++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 plugins/OStatus/lib/ostatusqueuehandler.php (limited to 'plugins/OStatus/lib/ostatusqueuehandler.php') diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php new file mode 100644 index 000000000..c1e50bffa --- /dev/null +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -0,0 +1,223 @@ +. + */ + +/** + * Prepare PuSH and Salmon distributions for an outgoing message. + * + * @package OStatusPlugin + * @author Brion Vibber + */ +class OStatusQueueHandler extends QueueHandler +{ + function transport() + { + return 'ostatus'; + } + + function handle($notice) + { + assert($notice instanceof Notice); + + $this->notice = $notice; + $this->user = User::staticGet($notice->profile_id); + + $this->pushUser(); + + foreach ($notice->getGroups() as $group) { + $oprofile = Ostatus_profile::staticGet('group_id', $group->id); + if ($oprofile) { + $this->pingReply($oprofile); + } else { + $this->pushGroup($group->id); + } + } + + foreach ($notice->getReplies() as $profile_id) { + $oprofile = Ostatus_profile::staticGet('profile_id', $profile_id); + if ($oprofile) { + $this->pingReply($oprofile); + } + } + + return true; + } + + function pushUser() + { + if ($this->user) { + // For local posts, ping the PuSH hub to update their feed. + // http://identi.ca/api/statuses/user_timeline/1.atom + $feed = common_local_url('ApiTimelineUser', + array('id' => $this->user->id, + 'format' => 'atom')); + $this->pushFeed($feed, array($this, 'userFeedForNotice')); + } + } + + function pushGroup($group_id) + { + // For a local group, ping the PuSH hub to update its feed. + // Updates may come from either a local or a remote user. + $feed = common_local_url('ApiTimelineGroup', + array('id' => $group_id, + 'format' => 'atom')); + $this->pushFeed($feed, array($this, 'groupFeedForNotice'), $group_id); + } + + function pingReply($oprofile) + { + if ($this->user) { + if (!empty($oprofile->salmonuri)) { + // For local posts, send a Salmon ping to the mentioned + // remote user or group. + // @fixme as an optimization we can skip this if the + // remote profile is subscribed to the author. + + common_log(LOG_INFO, "Prepping to send notice '{$this->notice->uri}' to remote profile '{$oprofile->uri}'."); + + $xml = ''; + $xml .= $this->notice->asAtomEntry(true, true); + + $data = array('salmonuri' => $oprofile->salmonuri, + 'entry' => $xml); + + $qm = QueueManager::get(); + $qm->enqueue($data, 'salmonout'); + } + } + } + + /** + * @param string $feed URI to the feed + * @param callable $callback function to generate Atom feed update if needed + * any additional params are passed to the callback. + */ + function pushFeed($feed, $callback) + { + $hub = common_config('ostatus', 'hub'); + if ($hub) { + $this->pushFeedExternal($feed, $hub); + } + + $sub = new HubSub(); + $sub->topic = $feed; + if ($sub->find()) { + $args = array_slice(func_get_args(), 2); + $atom = call_user_func_array($callback, $args); + $this->pushFeedInternal($atom, $sub); + } else { + common_log(LOG_INFO, "No PuSH subscribers for $feed"); + } + return true; + } + + /** + * Ping external hub about this update. + * The hub will pull the feed and check for new items later. + * Not guaranteed safe in an environment with database replication. + * + * @param string $feed feed topic URI + * @param string $hub PuSH hub URI + * @fixme can consolidate pings for user & group posts + */ + function pushFeedExternal($feed, $hub) + { + $client = new HTTPClient(); + try { + $data = array('hub.mode' => 'publish', + 'hub.url' => $feed); + $response = $client->post($hub, array(), $data); + if ($response->getStatus() == 204) { + common_log(LOG_INFO, "PuSH ping to hub $hub for $feed ok"); + return true; + } else { + common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed with HTTP " . + $response->getStatus() . ': ' . + $response->getBody()); + } + } catch (Exception $e) { + common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed: " . $e->getMessage()); + return false; + } + } + + /** + * Queue up direct feed update pushes to subscribers on our internal hub. + * @param string $atom update feed, containing only new/changed items + * @param HubSub $sub open query of subscribers + */ + function pushFeedInternal($atom, $sub) + { + common_log(LOG_INFO, "Preparing $sub->N PuSH distribution(s) for $sub->topic"); + while ($sub->fetch()) { + $sub->distribute($atom); + } + } + + /** + * Build a single-item version of the sending user's Atom feed. + * @return string + */ + function userFeedForNotice() + { + // @fixme this feels VERY hacky... + // should probably be a cleaner way to do it + + ob_start(); + $api = new ApiTimelineUserAction(); + $api->prepare(array('id' => $this->notice->profile_id, + 'format' => 'atom', + 'max_id' => $this->notice->id, + 'since_id' => $this->notice->id - 1)); + $api->showTimeline(); + $feed = ob_get_clean(); + + // ...and override the content-type back to something normal... eww! + // hope there's no other headers that got set while we weren't looking. + header('Content-Type: text/html; charset=utf-8'); + + common_log(LOG_DEBUG, $feed); + return $feed; + } + + function groupFeedForNotice($group_id) + { + // @fixme this feels VERY hacky... + // should probably be a cleaner way to do it + + ob_start(); + $api = new ApiTimelineGroupAction(); + $args = array('id' => $group_id, + 'format' => 'atom', + 'max_id' => $this->notice->id, + 'since_id' => $this->notice->id - 1); + $api->prepare($args); + $api->handle($args); + $feed = ob_get_clean(); + + // ...and override the content-type back to something normal... eww! + // hope there's no other headers that got set while we weren't looking. + header('Content-Type: text/html; charset=utf-8'); + + common_log(LOG_DEBUG, $feed); + return $feed; + } + +} + -- cgit v1.2.3-54-g00ecf From c36bdc1ba535dc3e2dc9098dbe40735b1955d96d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 20:36:36 +0000 Subject: - break OMB profile update pings to a background queue - add event hooks to profile update pings - send Salmon pings with custom update-profile event to OStatus subscribees and groups (subscribers will see it on your next post) - fix OStatus queues with overlong transport names, should work on DB queues now - Ostatus_profile::notifyActivity() and ::notifyDeferred() now can take XML, Notice, or Activity for convenience --- lib/activity.php | 3 ++ lib/profilequeuehandler.php | 48 ++++++++++++++++++++++++ lib/queuemanager.php | 3 ++ lib/util.php | 14 ++++--- plugins/OStatus/OStatusPlugin.php | 53 ++++++++++++++++++++++++-- plugins/OStatus/actions/pushcallback.php | 2 +- plugins/OStatus/classes/HubSub.php | 2 +- plugins/OStatus/classes/Ostatus_profile.php | 46 ++++++++++++++++++++--- plugins/OStatus/lib/hubconfqueuehandler.php | 54 +++++++++++++++++++++++++++ plugins/OStatus/lib/hubverifyqueuehandler.php | 54 --------------------------- plugins/OStatus/lib/ostatusqueuehandler.php | 22 +++-------- plugins/OStatus/lib/pushinputqueuehandler.php | 49 ------------------------ plugins/OStatus/lib/pushinqueuehandler.php | 49 ++++++++++++++++++++++++ plugins/OStatus/lib/salmonoutqueuehandler.php | 44 ---------------------- plugins/OStatus/lib/salmonqueuehandler.php | 44 ++++++++++++++++++++++ 15 files changed, 308 insertions(+), 179 deletions(-) create mode 100644 lib/profilequeuehandler.php create mode 100644 plugins/OStatus/lib/hubconfqueuehandler.php delete mode 100644 plugins/OStatus/lib/hubverifyqueuehandler.php delete mode 100644 plugins/OStatus/lib/pushinputqueuehandler.php create mode 100644 plugins/OStatus/lib/pushinqueuehandler.php delete mode 100644 plugins/OStatus/lib/salmonoutqueuehandler.php create mode 100644 plugins/OStatus/lib/salmonqueuehandler.php (limited to 'plugins/OStatus/lib/ostatusqueuehandler.php') diff --git a/lib/activity.php b/lib/activity.php index fa4ae0274..33932ad0e 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -691,6 +691,9 @@ class ActivityVerb 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'; + + // For simple profile-update pings; no content to share. + const UPDATE_PROFILE = 'http://ostatus.org/schema/1.0/update-profile'; } class ActivityContext diff --git a/lib/profilequeuehandler.php b/lib/profilequeuehandler.php new file mode 100644 index 000000000..e8a00aef3 --- /dev/null +++ b/lib/profilequeuehandler.php @@ -0,0 +1,48 @@ +. + */ + +/** + * @package QueueHandler + * @maintainer Brion Vibber + */ + +class ProfileQueueHandler extends QueueHandler +{ + + function transport() + { + return 'profile'; + } + + function handle($profile) + { + if (!($profile instanceof Profile)) { + common_log(LOG_ERR, "Got a bogus profile, not broadcasting"); + return true; + } + + if (Event::handle('StartBroadcastProfile', array($profile))) { + require_once(INSTALLDIR.'/lib/omb.php'); + omb_broadcast_profile($profile); + } + Event::handle('EndBroadcastProfile', array($profile)); + return true; + } + +} diff --git a/lib/queuemanager.php b/lib/queuemanager.php index 8f8c8f133..9fdc80110 100644 --- a/lib/queuemanager.php +++ b/lib/queuemanager.php @@ -262,6 +262,9 @@ abstract class QueueManager extends IoManager $this->connect('sms', 'SmsQueueHandler'); } + // Broadcasting profile updates to OMB remote subscribers + $this->connect('profile', 'ProfileQueueHandler'); + // XMPP output handlers... if (common_config('xmpp', 'enabled')) { // Delivery prep, read by queuedaemon.php: diff --git a/lib/util.php b/lib/util.php index 7fb2c6c4b..9354431f2 100644 --- a/lib/util.php +++ b/lib/util.php @@ -1119,12 +1119,16 @@ function common_enqueue_notice($notice) return true; } -function common_broadcast_profile($profile) +/** + * Broadcast profile updates to OMB and other remote subscribers. + * + * Since this may be slow with a lot of subscribers or bad remote sites, + * this is run through the background queues if possible. + */ +function common_broadcast_profile(Profile $profile) { - // XXX: optionally use a queue system like http://code.google.com/p/microapps/wiki/NQDQ - require_once(INSTALLDIR.'/lib/omb.php'); - omb_broadcast_profile($profile); - // XXX: Other broadcasts...? + $qm = QueueManager::get(); + $qm->enqueue($profile, "profile"); return true; } diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 9376c048d..90abe034d 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -82,14 +82,14 @@ class OStatusPlugin extends Plugin $qm->connect('ostatus', 'OStatusQueueHandler'); // Outgoing from our internal PuSH hub - $qm->connect('hubverify', 'HubVerifyQueueHandler'); + $qm->connect('hubconf', 'HubConfQueueHandler'); $qm->connect('hubout', 'HubOutQueueHandler'); // Outgoing Salmon replies (when we don't need a return value) - $qm->connect('salmonout', 'SalmonOutQueueHandler'); + $qm->connect('salmon', 'SalmonQueueHandler'); // Incoming from a foreign PuSH hub - $qm->connect('pushinput', 'PushInputQueueHandler'); + $qm->connect('pushin', 'PushInQueueHandler'); return true; } @@ -656,4 +656,51 @@ class OStatusPlugin extends Plugin return true; } + + /** + * Ping remote profiles with updates to this profile. + * Salmon pings are queued for background processing. + */ + function onEndBroadcastProfile(Profile $profile) + { + $user = User::staticGet('id', $profile->id); + + // Find foreign accounts I'm subscribed to that support Salmon pings. + // + // @fixme we could run updates through the PuSH feed too, + // in which case we can skip Salmon pings to folks who + // are also subscribed to me. + $sql = "SELECT * FROM ostatus_profile " . + "WHERE profile_id IN " . + "(SELECT subscribed FROM subscription WHERE subscriber=%d) " . + "OR group_id IN " . + "(SELECT group_id FROM group_member WHERE profile_id=%d)"; + $oprofile = new Ostatus_profile(); + $oprofile->query(sprintf($sql, $profile->id, $profile->id)); + + if ($oprofile->N == 0) { + common_log(LOG_DEBUG, "No OStatus remote subscribees for $profile->nickname"); + return true; + } + + $act = new Activity(); + + $act->verb = ActivityVerb::UPDATE_PROFILE; + $act->id = TagURI::mint('update-profile:%d:%s', + $profile->id, + common_date_iso8601(time())); + $act->time = time(); + $act->title = _m("Profile update"); + $act->content = sprintf(_m("%s has updated their profile page."), + $profile->getBestName()); + + $act->actor = ActivityObject::fromProfile($profile); + $act->object = $act->actor; + + while ($oprofile->fetch()) { + $oprofile->notifyDeferred($act); + } + + return true; + } } diff --git a/plugins/OStatus/actions/pushcallback.php b/plugins/OStatus/actions/pushcallback.php index 4184f0e0c..9a2067b8c 100644 --- a/plugins/OStatus/actions/pushcallback.php +++ b/plugins/OStatus/actions/pushcallback.php @@ -68,7 +68,7 @@ class PushCallbackAction extends Action 'post' => $post, 'hmac' => $hmac); $qm = QueueManager::get(); - $qm->enqueue($data, 'pushinput'); + $qm->enqueue($data, 'pushin'); } /** diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php index eae2928c3..1ac181fee 100644 --- a/plugins/OStatus/classes/HubSub.php +++ b/plugins/OStatus/classes/HubSub.php @@ -164,7 +164,7 @@ class HubSub extends Memcached_DataObject 'token' => $token, 'retries' => $retries); $qm = QueueManager::get(); - $qm->enqueue($data, 'hubverify'); + $qm->enqueue($data, 'hubconf'); } /** diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 9f9efb96e..61505206e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -431,21 +431,57 @@ class Ostatus_profile extends Memcached_DataObject return false; } - public function notifyActivity($activity) + /** + * Send a Salmon notification ping immediately, and confirm that we got + * an acceptable response from the remote site. + * + * @param mixed $entry XML string, Notice, or Activity + * @return boolean success + */ + public function notifyActivity($entry) { if ($this->salmonuri) { + $salmon = new Salmon(); + return $salmon->post($this->salmonuri, $this->notifyPrepXml($entry)); + } - $xml = '' . - $activity->asString(true); + return false; + } - $salmon = new Salmon(); // ? + /** + * Queue a Salmon notification for later. If queues are disabled we'll + * send immediately but won't get the return value. + * + * @param mixed $entry XML string, Notice, or Activity + * @return boolean success + */ + public function notifyDeferred($entry) + { + if ($this->salmonuri) { + $data = array('salmonuri' => $this->salmonuri, + 'entry' => $this->notifyPrepXml($entry)); - return $salmon->post($this->salmonuri, $xml); + $qm = QueueManager::get(); + return $qm->enqueue($data, 'salmon'); } return false; } + protected function notifyPrepXml($entry) + { + $preamble = ''; + if (is_string($entry)) { + return $entry; + } else if ($entry instanceof Activity) { + return $preamble . $entry->asString(true); + } else if ($entry instanceof Notice) { + return $preamble . $entry->asAtomEntry(true, true); + } else { + throw new ServerException("Invalid type passed to Ostatus_profile::notify; must be XML string or Activity entry"); + } + } + function getBestName() { if ($this->isGroup()) { diff --git a/plugins/OStatus/lib/hubconfqueuehandler.php b/plugins/OStatus/lib/hubconfqueuehandler.php new file mode 100644 index 000000000..c8e0b72fe --- /dev/null +++ b/plugins/OStatus/lib/hubconfqueuehandler.php @@ -0,0 +1,54 @@ +. + */ + +/** + * Send a PuSH subscription verification from our internal hub. + * @package Hub + * @author Brion Vibber + */ +class HubConfQueueHandler extends QueueHandler +{ + function transport() + { + return 'hubconf'; + } + + function handle($data) + { + $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, $token); + } catch (Exception $e) { + common_log(LOG_ERR, "Failed PuSH $mode verify to $sub->callback for $sub->topic: " . + $e->getMessage()); + // @fixme schedule retry? + // @fixme just kill it? + } + + return true; + } +} + diff --git a/plugins/OStatus/lib/hubverifyqueuehandler.php b/plugins/OStatus/lib/hubverifyqueuehandler.php deleted file mode 100644 index 7ce9e1431..000000000 --- a/plugins/OStatus/lib/hubverifyqueuehandler.php +++ /dev/null @@ -1,54 +0,0 @@ -. - */ - -/** - * Send a PuSH subscription verification from our internal hub. - * @package Hub - * @author Brion Vibber - */ -class HubVerifyQueueHandler extends QueueHandler -{ - function transport() - { - return 'hubverify'; - } - - function handle($data) - { - $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, $token); - } catch (Exception $e) { - common_log(LOG_ERR, "Failed PuSH $mode verify to $sub->callback for $sub->topic: " . - $e->getMessage()); - // @fixme schedule retry? - // @fixme just kill it? - } - - return true; - } -} - diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php index c1e50bffa..0da85600f 100644 --- a/plugins/OStatus/lib/ostatusqueuehandler.php +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -83,23 +83,11 @@ class OStatusQueueHandler extends QueueHandler function pingReply($oprofile) { if ($this->user) { - if (!empty($oprofile->salmonuri)) { - // For local posts, send a Salmon ping to the mentioned - // remote user or group. - // @fixme as an optimization we can skip this if the - // remote profile is subscribed to the author. - - common_log(LOG_INFO, "Prepping to send notice '{$this->notice->uri}' to remote profile '{$oprofile->uri}'."); - - $xml = ''; - $xml .= $this->notice->asAtomEntry(true, true); - - $data = array('salmonuri' => $oprofile->salmonuri, - 'entry' => $xml); - - $qm = QueueManager::get(); - $qm->enqueue($data, 'salmonout'); - } + // For local posts, send a Salmon ping to the mentioned + // remote user or group. + // @fixme as an optimization we can skip this if the + // remote profile is subscribed to the author. + $oprofile->notifyDeferred($this->notice); } } diff --git a/plugins/OStatus/lib/pushinputqueuehandler.php b/plugins/OStatus/lib/pushinputqueuehandler.php deleted file mode 100644 index cbd9139b5..000000000 --- a/plugins/OStatus/lib/pushinputqueuehandler.php +++ /dev/null @@ -1,49 +0,0 @@ -. - */ - -/** - * Process a feed distribution POST from a PuSH hub. - * @package FeedSub - * @author Brion Vibber - */ - -class PushInputQueueHandler extends QueueHandler -{ - function transport() - { - return 'pushinput'; - } - - function handle($data) - { - assert(is_array($data)); - - $feedsub_id = $data['feedsub_id']; - $post = $data['post']; - $hmac = $data['hmac']; - - $feedsub = FeedSub::staticGet('id', $feedsub_id); - if ($feedsub) { - $feedsub->receive($post, $hmac); - } else { - common_log(LOG_ERR, "Discarding POST to unknown feed subscription id $feedsub_id"); - } - return true; - } -} diff --git a/plugins/OStatus/lib/pushinqueuehandler.php b/plugins/OStatus/lib/pushinqueuehandler.php new file mode 100644 index 000000000..a90f52df2 --- /dev/null +++ b/plugins/OStatus/lib/pushinqueuehandler.php @@ -0,0 +1,49 @@ +. + */ + +/** + * Process a feed distribution POST from a PuSH hub. + * @package FeedSub + * @author Brion Vibber + */ + +class PushInQueueHandler extends QueueHandler +{ + function transport() + { + return 'pushin'; + } + + function handle($data) + { + assert(is_array($data)); + + $feedsub_id = $data['feedsub_id']; + $post = $data['post']; + $hmac = $data['hmac']; + + $feedsub = FeedSub::staticGet('id', $feedsub_id); + if ($feedsub) { + $feedsub->receive($post, $hmac); + } else { + common_log(LOG_ERR, "Discarding POST to unknown feed subscription id $feedsub_id"); + } + return true; + } +} diff --git a/plugins/OStatus/lib/salmonoutqueuehandler.php b/plugins/OStatus/lib/salmonoutqueuehandler.php deleted file mode 100644 index 536ff94af..000000000 --- a/plugins/OStatus/lib/salmonoutqueuehandler.php +++ /dev/null @@ -1,44 +0,0 @@ -. - */ - -/** - * Send a Salmon notification in the background. - * @package OStatusPlugin - * @author Brion Vibber - */ -class SalmonOutQueueHandler extends QueueHandler -{ - function transport() - { - return 'salmonout'; - } - - function handle($data) - { - assert(is_array($data)); - assert(is_string($data['salmonuri'])); - assert(is_string($data['entry'])); - - $salmon = new Salmon(); - $salmon->post($data['salmonuri'], $data['entry']); - - // @fixme detect failure and attempt to resend - return true; - } -} diff --git a/plugins/OStatus/lib/salmonqueuehandler.php b/plugins/OStatus/lib/salmonqueuehandler.php new file mode 100644 index 000000000..aa97018dc --- /dev/null +++ b/plugins/OStatus/lib/salmonqueuehandler.php @@ -0,0 +1,44 @@ +. + */ + +/** + * Send a Salmon notification in the background. + * @package OStatusPlugin + * @author Brion Vibber + */ +class SalmonQueueHandler extends QueueHandler +{ + function transport() + { + return 'salmon'; + } + + function handle($data) + { + assert(is_array($data)); + assert(is_string($data['salmonuri'])); + assert(is_string($data['entry'])); + + $salmon = new Salmon(); + $salmon->post($data['salmonuri'], $data['entry']); + + // @fixme detect failure and attempt to resend + return true; + } +} -- cgit v1.2.3-54-g00ecf From 223ebc765c454e030a49df7e2e1b9cdc2b005fe6 Mon Sep 17 00:00:00 2001 From: James Walker Date: Fri, 26 Feb 2010 14:21:21 -0500 Subject: move signing to take a local actor profile and use local keys --- plugins/OStatus/OStatusPlugin.php | 14 +++++++------- plugins/OStatus/classes/Magicsig.php | 3 ++- plugins/OStatus/classes/Ostatus_profile.php | 11 ++++++----- plugins/OStatus/lib/magicenvelope.php | 12 +----------- plugins/OStatus/lib/ostatusqueuehandler.php | 2 +- plugins/OStatus/lib/salmon.php | 24 ++++++++++++++++++------ plugins/OStatus/lib/salmonqueuehandler.php | 4 +++- 7 files changed, 38 insertions(+), 32 deletions(-) (limited to 'plugins/OStatus/lib/ostatusqueuehandler.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 89fa45f91..720dedd0a 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -415,7 +415,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($subscriber); $act->object = ActivityObject::fromProfile($other); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $subscriber); return true; } @@ -463,7 +463,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($profile); $act->object = ActivityObject::fromProfile($other); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $profile); return true; } @@ -505,7 +505,7 @@ class OStatusPlugin extends Plugin $member->getBestName(), $oprofile->getBestName()); - if ($oprofile->notifyActivity($act)) { + if ($oprofile->notifyActivity($act, $member)) { return true; } else { $oprofile->garbageCollect(); @@ -555,7 +555,7 @@ class OStatusPlugin extends Plugin $member->getBestName(), $oprofile->getBestName()); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $member); } } @@ -598,7 +598,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($profile); $act->object = ActivityObject::fromNotice($notice); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $profile); return true; } @@ -642,7 +642,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($profile); $act->object = ActivityObject::fromNotice($notice); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $profile); return true; } @@ -731,7 +731,7 @@ class OStatusPlugin extends Plugin $act->object = $act->actor; while ($oprofile->fetch()) { - $oprofile->notifyDeferred($act); + $oprofile->notifyDeferred($act, $profile); } return true; diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index 02882d19b..751527c81 100644 --- a/plugins/OStatus/classes/Magicsig.php +++ b/plugins/OStatus/classes/Magicsig.php @@ -49,7 +49,8 @@ class Magicsig extends Memcached_DataObject public /*static*/ function staticGet($k, $v=null) { - return parent::staticGet(__CLASS__, $k, $v); + $obj = parent::staticGet(__CLASS__, $k, $v); + return Magicsig::fromString($obj->keypair); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index c6f7378c4..35539bff7 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -357,7 +357,7 @@ class Ostatus_profile extends Memcached_DataObject common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml"); $salmon = new Salmon(); // ? - return $salmon->post($this->salmonuri, $xml); + return $salmon->post($this->salmonuri, $xml, $actor); } return false; } @@ -369,11 +369,11 @@ class Ostatus_profile extends Memcached_DataObject * @param mixed $entry XML string, Notice, or Activity * @return boolean success */ - public function notifyActivity($entry) + public function notifyActivity($entry, $actor) { if ($this->salmonuri) { $salmon = new Salmon(); - return $salmon->post($this->salmonuri, $this->notifyPrepXml($entry)); + return $salmon->post($this->salmonuri, $this->notifyPrepXml($entry), $actor); } return false; @@ -386,11 +386,12 @@ class Ostatus_profile extends Memcached_DataObject * @param mixed $entry XML string, Notice, or Activity * @return boolean success */ - public function notifyDeferred($entry) + public function notifyDeferred($entry, $actor) { if ($this->salmonuri) { $data = array('salmonuri' => $this->salmonuri, - 'entry' => $this->notifyPrepXml($entry)); + 'entry' => $this->notifyPrepXml($entry), + 'actor' => $actor->id); $qm = QueueManager::get(); return $qm->enqueue($data, 'salmon'); diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php index 457c0fba2..f33119b8f 100644 --- a/plugins/OStatus/lib/magicenvelope.php +++ b/plugins/OStatus/lib/magicenvelope.php @@ -67,18 +67,8 @@ class MagicEnvelope } - public function signMessage($text, $mimetype, $signer_uri) + public function signMessage($text, $mimetype, $keypair) { - $signer_uri = $this->normalizeUser($signer_uri); - - if (!$this->checkAuthor($text, $signer_uri)) { - throw new Exception("Unable to determine entry author."); - } - - $keypair = $this->getKeyPair($signer_uri); - if (!$keypair) { - throw new Exception("Unable to retrive keypair for ". $signer_uri); - } $signature_alg = Magicsig::fromString($keypair); $armored_text = base64_encode($text); diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php index 0da85600f..6ca31c485 100644 --- a/plugins/OStatus/lib/ostatusqueuehandler.php +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -87,7 +87,7 @@ class OStatusQueueHandler extends QueueHandler // remote user or group. // @fixme as an optimization we can skip this if the // remote profile is subscribed to the author. - $oprofile->notifyDeferred($this->notice); + $oprofile->notifyDeferred($this->notice, $this->user); } } diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index c59de2a14..6e2459544 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -42,14 +42,14 @@ class Salmon * @param string $xml * @return boolean success */ - public function post($endpoint_uri, $xml) + public function post($endpoint_uri, $xml, $actor) { if (empty($endpoint_uri)) { return false; } if (!common_config('ostatus', 'skip_signatures')) { - $xml = $this->createMagicEnv($xml); + $xml = $this->createMagicEnv($xml, $actor); } $headers = array('Content-Type: application/atom+xml'); @@ -70,15 +70,27 @@ class Salmon return true; } - public function createMagicEnv($text) + public function createMagicEnv($text, $actor) { + common_log(LOG_DEBUG, "Got actor as : ". print_r($actor, true)); $magic_env = new MagicEnvelope(); - // TODO: Should probably be getting the signer uri as an argument? - $signer_uri = $magic_env->getAuthor($text); + $user = User::staticGet('id', $actor->id); + if ($user->id) { + // Use local key + $magickey = Magicsig::staticGet('user_id', $user->id); + if (!$magickey) { + // No keypair yet, let's generate one. + $magickey = new Magicsig(); + $magickey->generate($user->id); + } + common_log(LOG_DEBUG, "Salmon: Loaded key for ". $user->id); + } else { + throw new Exception("Salmon invalid actor for signing"); + } try { - $env = $magic_env->signMessage($text, 'application/atom+xml', $signer_uri); + $env = $magic_env->signMessage($text, 'application/atom+xml', $magickey->toString()); } catch (Exception $e) { common_log(LOG_ERR, "Salmon signing failed: ". $e->getMessage()); return $text; diff --git a/plugins/OStatus/lib/salmonqueuehandler.php b/plugins/OStatus/lib/salmonqueuehandler.php index aa97018dc..7eeb5f8e9 100644 --- a/plugins/OStatus/lib/salmonqueuehandler.php +++ b/plugins/OStatus/lib/salmonqueuehandler.php @@ -35,8 +35,10 @@ class SalmonQueueHandler extends QueueHandler assert(is_string($data['salmonuri'])); assert(is_string($data['entry'])); + $actor = Profile::staticGet($data['actor']); + $salmon = new Salmon(); - $salmon->post($data['salmonuri'], $data['entry']); + $salmon->post($data['salmonuri'], $data['entry'], $actor); // @fixme detect failure and attempt to resend return true; -- cgit v1.2.3-54-g00ecf From 9fadf8da1164d620284917b829329e195aa2a226 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 3 Mar 2010 12:51:23 -0800 Subject: Put all required field setup into AtomUserNoticeFeed and AtomGroupNoticeFeed, consolidating some code. (RSS feeds pulling title, logo etc from the Atom data structure so we don't dupe it.) OStatus now calling the feed classes directly instead of faking a call into the API, should be less flakey. --- actions/apitimelinegroup.php | 45 ++++++------------------- actions/apitimelineuser.php | 51 ++++++----------------------- lib/atom10feed.php | 20 +++++++++-- lib/atomgroupnoticefeed.php | 32 ++++++++++++++++-- lib/atomusernoticefeed.php | 41 +++++++++++++++++++++-- plugins/OStatus/lib/ostatusqueuehandler.php | 45 ++++++------------------- 6 files changed, 116 insertions(+), 118 deletions(-) (limited to 'plugins/OStatus/lib/ostatusqueuehandler.php') diff --git a/actions/apitimelinegroup.php b/actions/apitimelinegroup.php index e30a08fb5..8f971392b 100644 --- a/actions/apitimelinegroup.php +++ b/actions/apitimelinegroup.php @@ -104,30 +104,21 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction function showTimeline() { - $sitename = common_config('site', 'name'); - $avatar = $this->group->homepage_logo; - $title = sprintf(_("%s timeline"), $this->group->nickname); - - $subtitle = sprintf( - _('Updates from %1$s on %2$s!'), - $this->group->nickname, - $sitename - ); - - $logo = ($avatar) ? $avatar : User_group::defaultLogo(AVATAR_PROFILE_SIZE); + // We'll pull common formatting out of this for other formats + $atom = new AtomGroupNoticeFeed($this->group); switch($this->format) { case 'xml': $this->showXmlTimeline($this->notices); break; case 'rss': - $this->showRssTimeline( + $this->showRssTimeline( $this->notices, - $title, + $atom->title, $this->group->homeUrl(), - $subtitle, + $atom->subtitle, null, - $logo + $atom->logo ); break; case 'atom': @@ -136,38 +127,22 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction try { - $atom = new AtomGroupNoticeFeed($this->group); - - // @todo set all this Atom junk up inside the feed class - - #$atom->setId($id); - $atom->setTitle($title); - $atom->setSubtitle($subtitle); - $atom->setLogo($logo); - $atom->setUpdated('now'); - $atom->addAuthorRaw($this->group->asAtomAuthor()); $atom->setActivitySubject($this->group->asActivitySubject()); - $atom->addLink($this->group->homeUrl()); - $id = $this->arg('id'); $aargs = array('format' => 'atom'); if (!empty($id)) { $aargs['id'] = $id; } + $self = $this->getSelfUri('ApiTimelineGroup', $aargs); - $atom->setId($this->getSelfUri('ApiTimelineGroup', $aargs)); - - $atom->addLink( - $this->getSelfUri('ApiTimelineGroup', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); + $atom->setId($self); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); - //$this->raw($atom->getString()); - print $atom->getString(); // temp hack until PuSH feeds are redone cleanly + $this->raw($atom->getString()); } catch (Atom10FeedException $e) { $this->serverError( diff --git a/actions/apitimelineuser.php b/actions/apitimelineuser.php index 94491946c..2d0047c04 100644 --- a/actions/apitimelineuser.php +++ b/actions/apitimelineuser.php @@ -112,19 +112,17 @@ class ApiTimelineUserAction extends ApiBareAuthAction function showTimeline() { $profile = $this->user->getProfile(); - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - $sitename = common_config('site', 'name'); - $title = sprintf(_("%s timeline"), $this->user->nickname); + // We'll use the shared params from the Atom stub + // for other feed types. + $atom = new AtomUserNoticeFeed($this->user); + $title = $atom->title; $link = common_local_url( 'showstream', array('nickname' => $this->user->nickname) ); - $subtitle = sprintf( - _('Updates from %1$s on %2$s!'), - $this->user->nickname, $sitename - ); - $logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); + $subtitle = $atom->subtitle; + $logo = $atom->logo; // FriendFeed's SUP protocol // Also added RSS and Atom feeds @@ -146,47 +144,18 @@ class ApiTimelineUserAction extends ApiBareAuthAction header('Content-Type: application/atom+xml; charset=utf-8'); - // @todo set all this Atom junk up inside the feed class - - $atom = new AtomUserNoticeFeed($this->user); - - $atom->setTitle($title); - $atom->setSubtitle($subtitle); - $atom->setLogo($logo); - $atom->setUpdated('now'); - - $atom->addLink( - common_local_url( - 'showstream', - array('nickname' => $this->user->nickname) - ) - ); - $id = $this->arg('id'); $aargs = array('format' => 'atom'); if (!empty($id)) { $aargs['id'] = $id; } - - $atom->setId($this->getSelfUri('ApiTimelineUser', $aargs)); - - $atom->addLink( - $this->getSelfUri('ApiTimelineUser', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); - - $atom->addLink( - $suplink, - array( - 'rel' => 'http://api.friendfeed.com/2008/03#sup', - 'type' => 'application/json' - ) - ); + $self = $this->getSelfUri('ApiTimelineUser', $aargs); + $atom->setId($self); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); - #$this->raw($atom->getString()); - print $atom->getString(); // temporary for output buffering + $this->raw($atom->getString()); break; case 'json': diff --git a/lib/atom10feed.php b/lib/atom10feed.php index 8842840d5..c1fdeaae9 100644 --- a/lib/atom10feed.php +++ b/lib/atom10feed.php @@ -49,6 +49,8 @@ class Atom10FeedException extends Exception class Atom10Feed extends XMLStringer { public $xw; + + // @fixme most of these should probably be read-only properties private $namespaces; private $authors; private $subject; @@ -57,10 +59,12 @@ class Atom10Feed extends XMLStringer private $generator; private $icon; private $links; - private $logo; + private $selfLink; + private $selfLinkType; + public $logo; private $rights; - private $subtitle; - private $title; + public $subtitle; + public $title; private $published; private $updated; private $entries; @@ -184,6 +188,10 @@ class Atom10Feed extends XMLStringer $this->renderAuthors(); + if ($this->selfLink) { + $this->addLink($this->selfLink, array('rel' => 'self', + 'type' => $this->selfLinkType)); + } $this->renderLinks(); } @@ -253,6 +261,12 @@ class Atom10Feed extends XMLStringer $this->id = $id; } + function setSelfLink($url, $type='application/atom+xml') + { + $this->selfLink = $url; + $this->selfLinkType = $type; + } + function setTitle($title) { $this->title = $title; diff --git a/lib/atomgroupnoticefeed.php b/lib/atomgroupnoticefeed.php index 52ee4c7d6..08c1c707c 100644 --- a/lib/atomgroupnoticefeed.php +++ b/lib/atomgroupnoticefeed.php @@ -49,14 +49,42 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed /** * Constructor * - * @param Group $group the group for the feed (optional) + * @param Group $group the group for the feed * @param boolean $indent flag to turn indenting on or off * * @return void */ - function __construct($group = null, $indent = true) { + function __construct($group, $indent = true) { parent::__construct($indent); $this->group = $group; + + $title = sprintf(_("%s timeline"), $group->nickname); + $this->setTitle($title); + + $sitename = common_config('site', 'name'); + $subtitle = sprintf( + _('Updates from %1$s on %2$s!'), + $group->nickname, + $sitename + ); + $this->setSubtitle($subtitle); + + $avatar = $group->homepage_logo; + $logo = ($avatar) ? $avatar : User_group::defaultLogo(AVATAR_PROFILE_SIZE); + $this->setLogo($logo); + + $this->setUpdated('now'); + + $self = common_local_url('ApiTimelineGroup', + array('id' => $group->id, + 'format' => 'atom')); + $this->setId($self); + $this->setSelfLink($self); + + $this->addAuthorRaw($group->asAtomAuthor()); + $this->setActivitySubject($group->asActivitySubject()); + + $this->addLink($group->homeUrl()); } function getGroup() diff --git a/lib/atomusernoticefeed.php b/lib/atomusernoticefeed.php index 2ad8de455..55cebef6d 100644 --- a/lib/atomusernoticefeed.php +++ b/lib/atomusernoticefeed.php @@ -49,19 +49,56 @@ class AtomUserNoticeFeed extends AtomNoticeFeed /** * Constructor * - * @param User $user the user for the feed (optional) + * @param User $user the user for the feed * @param boolean $indent flag to turn indenting on or off * * @return void */ - function __construct($user = null, $indent = true) { + function __construct($user, $indent = true) { parent::__construct($indent); $this->user = $user; if (!empty($user)) { $profile = $user->getProfile(); $this->addAuthor($profile->nickname, $user->uri); } + + $title = sprintf(_("%s timeline"), $user->nickname); + $this->setTitle($title); + + $sitename = common_config('site', 'name'); + $subtitle = sprintf( + _('Updates from %1$s on %2$s!'), + $user->nickname, $sitename + ); + $this->setSubtitle($subtitle); + + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + $logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); + $this->setLogo($logo); + + $this->setUpdated('now'); + + $this->addLink( + common_local_url( + 'showstream', + array('nickname' => $user->nickname) + ) + ); + + $self = common_local_url('ApiTimelineUser', + array('id' => $user->id, + 'format' => 'atom')); + $this->setId($self); + $this->setSelfLink($self); + + $this->addLink( + common_local_url('sup', null, null, $user->id), + array( + 'rel' => 'http://api.friendfeed.com/2008/03#sup', + 'type' => 'application/json' + ) + ); } function getUser() diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php index 6ca31c485..d1e58f1d6 100644 --- a/plugins/OStatus/lib/ostatusqueuehandler.php +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -164,46 +164,21 @@ class OStatusQueueHandler extends QueueHandler */ function userFeedForNotice() { - // @fixme this feels VERY hacky... - // should probably be a cleaner way to do it - - ob_start(); - $api = new ApiTimelineUserAction(); - $api->prepare(array('id' => $this->notice->profile_id, - 'format' => 'atom', - 'max_id' => $this->notice->id, - 'since_id' => $this->notice->id - 1)); - $api->showTimeline(); - $feed = ob_get_clean(); - - // ...and override the content-type back to something normal... eww! - // hope there's no other headers that got set while we weren't looking. - header('Content-Type: text/html; charset=utf-8'); - - common_log(LOG_DEBUG, $feed); + $atom = new AtomUserNoticeFeed($this->user); + $atom->addEntryFromNotice($this->notice); + $feed = $atom->getString(); + return $feed; } function groupFeedForNotice($group_id) { - // @fixme this feels VERY hacky... - // should probably be a cleaner way to do it - - ob_start(); - $api = new ApiTimelineGroupAction(); - $args = array('id' => $group_id, - 'format' => 'atom', - 'max_id' => $this->notice->id, - 'since_id' => $this->notice->id - 1); - $api->prepare($args); - $api->handle($args); - $feed = ob_get_clean(); - - // ...and override the content-type back to something normal... eww! - // hope there's no other headers that got set while we weren't looking. - header('Content-Type: text/html; charset=utf-8'); - - common_log(LOG_DEBUG, $feed); + $group = User_group::staticGet('id', $group_id); + + $atom = new AtomGroupNoticeFeed($group); + $atom->addEntryFromNotice($this->notice); + $feed = $atom->getString(); + return $feed; } -- cgit v1.2.3-54-g00ecf