From b03c7a383f00a97b08a270c6c472b46e11314b84 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 19 Feb 2010 13:03:26 +0100 Subject: [PATCH 1/4] Only load json2.js if native JSON is not supported --- lib/action.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/action.php b/lib/action.php index b85f353a3d..cd84662c96 100644 --- a/lib/action.php +++ b/lib/action.php @@ -249,7 +249,7 @@ class Action extends HTMLOutputter // lawsuit $this->script('jquery.min.js'); $this->script('jquery.form.js'); $this->script('jquery.cookie.js'); - $this->script('json2.js'); + $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/json2.js').'"); }'); $this->script('jquery.joverlay.min.js'); Event::handle('EndShowJQueryScripts', array($this)); } From 8d59f7cc9d7f9f12d46530db2d9efe0b7aaac1a5 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 19 Feb 2010 13:13:05 +0100 Subject: [PATCH 2/4] Using inlineScript instead for clickjacking check --- lib/action.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/action.php b/lib/action.php index cd84662c96..fa9ddb9110 100644 --- a/lib/action.php +++ b/lib/action.php @@ -259,8 +259,7 @@ class Action extends HTMLOutputter // lawsuit $this->script('util.js'); $this->script('geometa.js'); // Frame-busting code to avoid clickjacking attacks. - $this->element('script', array('type' => 'text/javascript'), - 'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }'); + $this->inlineScript('if (window.top !== window.self) { window.top.location.href = window.self.location.href; }'); Event::handle('EndShowStatusNetScripts', array($this)); Event::handle('EndShowLaconicaScripts', array($this)); } From 114eb310ca4f53e09c98f2337850829a9036c133 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 19 Feb 2010 10:29:06 -0800 Subject: [PATCH 3/4] OStatus: fix up Salmon endpoint detection/saving, timestamp fixes. --- plugins/OStatus/actions/salmon.php | 2 +- plugins/OStatus/classes/FeedSub.php | 2 +- plugins/OStatus/classes/Ostatus_profile.php | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index e9d6015f43..2da9db9eb7 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -142,7 +142,7 @@ class SalmonAction extends Action function ensureProfile() { $actor = $this->act->actor; - + common_log(LOG_DEBUG, "Received salmon bit: " . var_export($this->act, true)); if (empty($actor->id)) { throw new Exception("Received a salmon slap from unidentified actor."); } diff --git a/plugins/OStatus/classes/FeedSub.php b/plugins/OStatus/classes/FeedSub.php index dc2c0b710b..bf9d063fa1 100644 --- a/plugins/OStatus/classes/FeedSub.php +++ b/plugins/OStatus/classes/FeedSub.php @@ -99,7 +99,7 @@ class FeedSub extends Memcached_DataObject 'sub_state' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, 'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, 'sub_end' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, - 'last_update' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'last_update' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index e0cb467e5e..319b76a661 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -328,7 +328,7 @@ class Ostatus_profile extends Memcached_DataObject $entry->element('id', null, $id); $entry->element('title', null, $text); $entry->element('summary', null, $text); - $entry->element('published', null, common_date_w3dtf(time())); + $entry->element('published', null, common_date_w3dtf(common_sql_now())); $entry->element('activity:verb', null, $verb); $entry->raw($actor->asAtomAuthor()); @@ -516,7 +516,7 @@ class Ostatus_profile extends Memcached_DataObject throw new FeedSubException('empty feed'); } $first = new Activity($entries->item(0), $discover->feed); - return self::ensureActorProfile($first, $feeduri); + return self::ensureActorProfile($first, $feeduri, $salmonuri); } /** @@ -598,13 +598,14 @@ class Ostatus_profile extends Memcached_DataObject * * @param Activity $activity * @param string $feeduri if we already know the canonical feed URI! + * @param string $salmonuri if we already know the salmon return channel URI * @return Ostatus_profile */ - public static function ensureActorProfile($activity, $feeduri=null) + public static function ensureActorProfile($activity, $feeduri=null, $salmonuri=null) { $profile = self::getActorProfile($activity); if (!$profile) { - $profile = self::createActorProfile($activity, $feeduri); + $profile = self::createActorProfile($activity, $feeduri, $salmonuri); } return $profile; } @@ -640,7 +641,7 @@ class Ostatus_profile extends Memcached_DataObject /** * @fixme validate stuff somewhere */ - protected static function createActorProfile($activity, $feeduri=null) + protected static function createActorProfile($activity, $feeduri=null, $salmonuri=null) { $actor = $activity->actor; $homeuri = self::getActorProfileURI($activity); @@ -674,10 +675,17 @@ class Ostatus_profile extends Memcached_DataObject $oprofile = new Ostatus_profile(); $oprofile->uri = $homeuri; if ($feeduri) { + // If we don't have these, we can look them up later. $oprofile->feeduri = $feeduri; + if ($salmonuri) { + $oprofile->salmonuri = $salmonuri; + } } $oprofile->profile_id = $profile->id; + $oprofile->created = common_sql_now(); + $oprofile->modified = common_sql_now(); + $ok = $oprofile->insert(); if ($ok) { $oprofile->updateAvatar($avatar); From a1a3ab1c58cf8636c4e9ac6b9d44bdc18946a547 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 19 Feb 2010 12:08:07 -0800 Subject: [PATCH 4/4] OStatus: hooked up follow/unfollow events on Salmon endpoint to create/destroy remote subscriber relationships --- plugins/OStatus/OStatusPlugin.php | 9 ++- plugins/OStatus/actions/feedsubsettings.php | 8 --- plugins/OStatus/actions/salmon.php | 66 ++++++++++++++++++--- plugins/OStatus/classes/Ostatus_profile.php | 22 +++++-- 4 files changed, 81 insertions(+), 24 deletions(-) diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 4ebe4551ec..4c43d8a303 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -253,17 +253,22 @@ class OStatusPlugin extends Plugin */ function onEndUnsubscribe($user, $other) { + if ($user instanceof Profile) { + $profile = $user; + } else if ($user instanceof Profile) { + $profile = $user->getProfile(); + } $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); if ($oprofile) { // Notify the remote server of the unsub, if supported. - $oprofile->notify($user->getProfile(), ActivityVerb::UNFOLLOW, $oprofile); + $oprofile->notify($profile, ActivityVerb::UNFOLLOW, $oprofile); // Drop the PuSH subscription if there are no other subscribers. $sub = new Subscription(); $sub->subscribed = $other->id; $sub->limit(1); if (!$sub->find(true)) { - common_log(LOG_INFO, "Unsubscribing from now-unused feed $oprofile->feeduri on hub $oprofile->huburi"); + common_log(LOG_INFO, "Unsubscribing from now-unused feed $oprofile->feeduri"); $oprofile->unsubscribe(); } } diff --git a/plugins/OStatus/actions/feedsubsettings.php b/plugins/OStatus/actions/feedsubsettings.php index 3e1d0aa823..aee4cee9af 100644 --- a/plugins/OStatus/actions/feedsubsettings.php +++ b/plugins/OStatus/actions/feedsubsettings.php @@ -68,14 +68,6 @@ class FeedSubSettingsAction extends ConnectSettingsAction $profile = $user->getProfile(); - $fuser = null; - - $flink = Foreign_link::getByUserID($user->id, FEEDSUB_SERVICE); - - if (!empty($flink)) { - $fuser = $flink->getForeignUser(); - } - $this->elementStart('form', array('method' => 'post', 'id' => 'form_settings_feedsub', 'class' => 'form_settings', diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index 2da9db9eb7..1ed322ed28 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -63,13 +63,17 @@ class SalmonAction extends Action // XXX: check that document element is Atom entry // XXX: check the signature - $this->act = new Activity($dom->documentElement); + $entries = $dom->getElementsByTagNameNS(Activity::ATOM, 'entry'); + if ($entries && $entries->length) { + // @fixme is it legit to have multiple entries? + $this->act = new Activity($entries->item(0), $dom->documentElement); + } return true; } /** - * @fixme probably call Ostatus_profile::processFeed + * Check the posted activity type and break out to appropriate processing. */ function handle($args) @@ -94,13 +98,19 @@ class SalmonAction extends Action case ActivityVerb::FRIEND: $this->handleFollow(); break; + case ActivityVerb::UNFOLLOW: + $this->handleUnfollow(); + break; } Event::handle('EndHandleSalmon', array($this->user, $this->activity)); } } /** - * @fixme probably call Ostatus_profile::processFeed + * We've gotten a post event on the Salmon backchannel, probably a reply. + * + * @todo validate if we need to handle this post, then call into + * ostatus_profile's general incoming-post handling. */ function handlePost() { @@ -116,43 +126,81 @@ class SalmonAction extends Action } $profile = $this->ensureProfile(); + // @fixme do something with the post } /** - * @fixme probably call Ostatus_profile::processFeed + * We've gotten a follow/subscribe notification from a remote user. + * Save a subscription relationship for them. */ function handleFollow() { + $oprofile = $this->ensureProfile(); + if ($oprofile) { + common_log(LOG_INFO, "Setting up subscription from remote {$oprofile->uri} to local {$this->user->nickname}"); + $oprofile->subscribeRemoteToLocal($this->user); + } else { + common_log(LOG_INFO, "Can't set up subscription from remote; missing profile."); + } } /** - * @fixme probably call Ostatus_profile::processFeed + * We've gotten an unfollow/unsubscribe notification from a remote user. + * Check if we have a subscription relationship for them and kill it. + * + * @fixme probably catch exceptions on fail? + */ + function handleUnfollow() + { + $oprofile = $this->ensureProfile(); + if ($oprofile) { + common_log(LOG_INFO, "Canceling subscription from remote {$oprofile->uri} to local {$this->user->nickname}"); + Subscription::cancel($oprofile->localProfile(), $this->user->getProfile()); + } else { + common_log(LOG_ERR, "Can't cancel subscription from remote, didn't find the profile"); + } + } + + /** + * Remote user likes one of our posts. + * Confirm the post is ours, and save a local favorite event. */ function handleFavorite() { } /** - * @fixme probably call Ostatus_profile::processFeed + * Remote user doesn't like one of our posts after all! + * Confirm the post is ours, and save a local favorite event. + */ + function handleUnfavorite() + { + } + + /** + * Hmmmm */ function handleShare() { } + /** + * @return Ostatus_profile + */ function ensureProfile() { $actor = $this->act->actor; common_log(LOG_DEBUG, "Received salmon bit: " . var_export($this->act, true)); if (empty($actor->id)) { + common_log(LOG_ERR, "broken actor: " . var_export($actor, true)); throw new Exception("Received a salmon slap from unidentified actor."); } - $ostatusProfile = Ostatus_profile::ensureActorProfile($this->act); - return $oprofile->localProfile(); + return Ostatus_profile::ensureActorProfile($this->act); } /** - * @fixme anything new in here probably should be merged into Ostatus_profile::ensureActorProfile and friends + * @fixme merge into Ostatus_profile::ensureActorProfile and friends */ function createProfile() { diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 319b76a661..97d8eec101 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -310,8 +310,15 @@ class Ostatus_profile extends Memcached_DataObject * @param $verb eg Activity::SUBSCRIBE or Activity::JOIN * @param $object object of the action; if null, the remote entity itself is assumed */ - public function notify(Profile $actor, $verb, $object=null) + public function notify($actor, $verb, $object=null) { + if (!($actor instanceof Profile)) { + $type = gettype($actor); + if ($type == 'object') { + $type = get_class($actor); + } + throw new ServerException("Invalid actor passed to " . __METHOD__ . ": " . $type); + } if ($object == null) { $object = $this; } @@ -340,7 +347,7 @@ class Ostatus_profile extends Memcached_DataObject $feed->addEntry($entry); $xml = $feed->getString(); - common_log(LOG_INFO, "Posting to Salmon endpoint $salmon: $xml"); + common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml"); $salmon = new Salmon(); // ? $salmon->post($this->salmonuri, $xml); @@ -531,9 +538,14 @@ class Ostatus_profile extends Memcached_DataObject $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); copy($url, $temp_filename); + if ($this->isGroup()) { + $id = $this->group_id; + } else { + $id = $this->profile_id; + } // @fixme should we be using different ids? - $imagefile = new ImageFile($this->id, $temp_filename); - $filename = Avatar::filename($this->id, + $imagefile = new ImageFile($id, $temp_filename); + $filename = Avatar::filename($id, image_type_to_extension($imagefile->type), null, common_timestamp()); @@ -646,7 +658,7 @@ class Ostatus_profile extends Memcached_DataObject $actor = $activity->actor; $homeuri = self::getActorProfileURI($activity); $nickname = self::getAuthorNick($activity); - $avatar = self::getAvatar($actor, $feed); + $avatar = self::getAvatar($actor, $activity->feed); if (!$homeuri) { common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true));