summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/apitimelinegroup.php3
-rw-r--r--classes/Notice.php111
-rw-r--r--classes/User_group.php18
-rw-r--r--lib/activity.php6
-rw-r--r--lib/distribqueuehandler.php14
-rw-r--r--plugins/OStatus/OStatusPlugin.php109
-rw-r--r--plugins/OStatus/actions/groupsalmon.php77
-rw-r--r--plugins/OStatus/actions/ostatussub.php2
-rw-r--r--plugins/OStatus/classes/Ostatus_profile.php244
-rw-r--r--plugins/OStatus/js/ostatus.js6
-rw-r--r--plugins/OStatus/lib/hubdistribqueuehandler.php2
-rw-r--r--plugins/OStatus/lib/magicsig.php8
-rw-r--r--plugins/OStatus/lib/salmon.php21
-rw-r--r--plugins/OStatus/lib/salmonaction.php23
14 files changed, 551 insertions, 93 deletions
diff --git a/actions/apitimelinegroup.php b/actions/apitimelinegroup.php
index 1d0c4afdd..0bb4860ea 100644
--- a/actions/apitimelinegroup.php
+++ b/actions/apitimelinegroup.php
@@ -176,7 +176,8 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
$atom->addEntryFromNotices($this->notices);
- $this->raw($atom->getString());
+ //$this->raw($atom->getString());
+ print $atom->getString(); // temp hack until PuSH feeds are redone cleanly
} catch (Atom10FeedException $e) {
$this->serverError(
diff --git a/classes/Notice.php b/classes/Notice.php
index ba8646f68..92d959dc5 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -187,7 +187,14 @@ class Notice extends Memcached_DataObject
* int 'location_ns' geoname namespace to interpret location_id
* int 'reply_to'; notice ID this is a reply to
* int 'repeat_of'; notice ID this is a repeat of
- * string 'uri' permalink to notice; defaults to local notice URL
+ * string 'uri' unique ID for notice; defaults to local notice URL
+ * string 'url' permalink to notice; defaults to local notice URL
+ * string 'rendered' rendered HTML version of content
+ * array 'replies' list of profile URIs for reply delivery in
+ * place of extracting @-replies from content.
+ * array 'groups' list of group IDs to deliver to, in place of
+ * extracting ! tags from content
+ * @fixme tag override
*
* @return Notice
* @throws ClientException
@@ -342,6 +349,12 @@ class Notice extends Memcached_DataObject
$notice->saveReplies();
}
+ if (isset($groups)) {
+ $notice->saveKnownGroups($groups);
+ } else {
+ $notice->saveGroups();
+ }
+
$notice->distribute();
return $notice;
@@ -692,7 +705,22 @@ class Notice extends Memcached_DataObject
return $ni;
}
- function addToInboxes($groups, $recipients)
+ /**
+ * Adds this notice to the inboxes of each local user who should receive
+ * it, based on author subscriptions, group memberships, and @-replies.
+ *
+ * Warning: running a second time currently will make items appear
+ * multiple times in users' inboxes.
+ *
+ * @fixme make more robust against errors
+ * @fixme break up massive deliveries to smaller background tasks
+ *
+ * @param array $groups optional list of Group objects;
+ * if left empty, will be loaded from group_inbox records
+ * @param array $recipient optional list of reply profile ids
+ * if left empty, will be loaded from reply records
+ */
+ function addToInboxes($groups=null, $recipients=null)
{
$ni = $this->whoGets($groups, $recipients);
@@ -742,6 +770,42 @@ class Notice extends Memcached_DataObject
}
/**
+ * Record this notice to the given group inboxes for delivery.
+ * Overrides the regular parsing of !group markup.
+ *
+ * @param string $group_ids
+ * @fixme might prefer URIs as identifiers, as for replies?
+ * best with generalizations on user_group to support
+ * remote groups better.
+ */
+ function saveKnownGroups($group_ids)
+ {
+ if (!is_array($group_ids)) {
+ throw new ServerException("Bad type provided to saveKnownGroups");
+ }
+
+ $groups = array();
+ foreach ($group_ids as $id) {
+ $group = User_group::staticGet('id', $id);
+ if ($group) {
+ common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname");
+ $result = $this->addToGroupInbox($group);
+ if (!$result) {
+ common_log_db_error($gi, 'INSERT', __FILE__);
+ }
+
+ // @fixme should we save the tags here or not?
+ $groups[] = clone($group);
+ } else {
+ common_log(LOG_ERR, "Local delivery to group id $id skipped, doesn't exist");
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Parse !group delivery and record targets into group_inbox.
* @return array of Group objects
*/
function saveGroups()
@@ -824,6 +888,19 @@ class Notice extends Memcached_DataObject
return true;
}
+ /**
+ * Save reply records indicating that this notice needs to be
+ * delivered to the local users with the given URIs.
+ *
+ * Since this is expected to be used when saving foreign-sourced
+ * messages, we won't deliver to any remote targets as that's the
+ * source service's responsibility.
+ *
+ * @fixme Unlike saveReplies() there's no mail notification here.
+ * Move that to distrib queue handler?
+ *
+ * @param array of unique identifier URIs for recipients
+ */
function saveKnownReplies($uris)
{
foreach ($uris as $uri) {
@@ -845,6 +922,13 @@ class Notice extends Memcached_DataObject
}
/**
+ * Pull @-replies from this message's content in StatusNet markup format
+ * and save reply records indicating that this message needs to be
+ * delivered to those users.
+ *
+ * Side effect: local recipients get e-mail notifications here.
+ * @fixme move mail notifications to distrib?
+ *
* @return array of integer profile IDs
*/
@@ -934,9 +1018,10 @@ class Notice extends Memcached_DataObject
}
/**
- * Same calculation as saveGroups but without the saving
- * @fixme merge the functions
- * @return array of Group_inbox objects
+ * Pull list of groups this notice needs to be delivered to,
+ * as previously recorded by saveGroups() or saveKnownGroups().
+ *
+ * @return array of Group objects
*/
function getGroups()
{
@@ -959,7 +1044,10 @@ class Notice extends Memcached_DataObject
if ($gi->find()) {
while ($gi->fetch()) {
- $groups[] = clone($gi);
+ $group = User_group::staticGet('id', $gi->group_id);
+ if ($group) {
+ $groups[] = $group;
+ }
}
}
@@ -1063,6 +1151,17 @@ class Notice extends Memcached_DataObject
}
}
+ $groups = $this->getGroups();
+
+ foreach ($groups as $group) {
+ $xs->element(
+ 'link', array(
+ 'rel' => 'ostatus:attention',
+ 'href' => $group->permalink()
+ )
+ );
+ }
+
if (!empty($this->repeat_of)) {
$repeat = Notice::staticGet('id', $this->repeat_of);
if (!empty($repeat)) {
diff --git a/classes/User_group.php b/classes/User_group.php
index 379e6b721..1382aa407 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -39,14 +39,24 @@ class User_group extends Memcached_DataObject
function homeUrl()
{
- return common_local_url('showgroup',
- array('nickname' => $this->nickname));
+ $url = null;
+ if (Event::handle('StartUserGroupHomeUrl', array($this, &$url))) {
+ $url = common_local_url('showgroup',
+ array('nickname' => $this->nickname));
+ }
+ Event::handle('EndUserGroupHomeUrl', array($this, &$url));
+ return $url;
}
function permalink()
{
- return common_local_url('groupbyid',
- array('id' => $this->id));
+ $url = null;
+ if (Event::handle('StartUserGroupPermalink', array($this, &$url))) {
+ $url = common_local_url('groupbyid',
+ array('id' => $this->id));
+ }
+ Event::handle('EndUserGroupPermalink', array($this, &$url));
+ return $url;
}
function getNotices($offset, $limit, $since_id=null, $max_id=null)
diff --git a/lib/activity.php b/lib/activity.php
index 3689dac38..d91e04260 100644
--- a/lib/activity.php
+++ b/lib/activity.php
@@ -476,6 +476,9 @@ class ActivityObject
return $object;
}
+ /**
+ * @fixme missing avatar, bio info, etc
+ */
static function fromProfile($profile)
{
$object = new ActivityObject();
@@ -495,6 +498,9 @@ class ActivityObject
return $object;
}
+ /**
+ * @fixme missing avatar, bio info, etc
+ */
function asString($tag='activity:object')
{
$xs = new XMLStringer(true);
diff --git a/lib/distribqueuehandler.php b/lib/distribqueuehandler.php
index c31b675c1..dc183fb36 100644
--- a/lib/distribqueuehandler.php
+++ b/lib/distribqueuehandler.php
@@ -69,19 +69,7 @@ class DistribQueueHandler
}
try {
- $groups = $notice->saveGroups();
- } catch (Exception $e) {
- $this->logit($notice, $e);
- }
-
- try {
- $recipients = $notice->getReplies();
- } catch (Exception $e) {
- $this->logit($notice, $e);
- }
-
- try {
- $notice->addToInboxes($groups, $recipients);
+ $notice->addToInboxes();
} catch (Exception $e) {
$this->logit($notice, $e);
}
diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php
index 7c6c0c69f..472008419 100644
--- a/plugins/OStatus/OStatusPlugin.php
+++ b/plugins/OStatus/OStatusPlugin.php
@@ -211,7 +211,7 @@ class OStatusPlugin extends Plugin
// FIXME: this needs to go out in a queue handler
- $xml = '<?xml version="1.0" encoding="UTF-8" ?>';
+ $xml = '<?xml version="1.0" encoding="UTF-8" ?' . '>';
$xml .= $notice->asAtomEntry(true, true);
$salmon = new Salmon();
@@ -403,6 +403,97 @@ class OStatusPlugin extends Plugin
}
/**
+ * When one of our local users tries to join a remote group,
+ * notify the remote server. If the notification is rejected,
+ * deny the join.
+ *
+ * @param User_group $group
+ * @param User $user
+ *
+ * @return mixed hook return value
+ */
+
+ function onStartJoinGroup($group, $user)
+ {
+ $oprofile = Ostatus_profile::staticGet('group_id', $group->id);
+ if ($oprofile) {
+ $member = Profile::staticGet($user->id);
+
+ $act = new Activity();
+ $act->id = TagURI::mint('join:%d:%d:%s',
+ $member->id,
+ $group->id,
+ common_date_iso8601(time()));
+
+ $act->actor = ActivityObject::fromProfile($member);
+ $act->verb = ActivityVerb::JOIN;
+ $act->object = $oprofile->asActivityObject();
+
+ $act->time = time();
+ $act->title = _m("Join");
+ $act->content = sprintf(_m("%s has joined group %s."),
+ $member->getBestName(),
+ $oprofile->getBestName());
+
+ if ($oprofile->notifyActivity($act)) {
+ return true;
+ } else {
+ throw new ServerException(_m("Failed joining remote group."));
+ }
+ }
+ }
+
+ /**
+ * When one of our local users leaves a remote group, notify the remote
+ * server.
+ *
+ * @fixme Might be good to schedule a resend of the leave notification
+ * if it failed due to a transitory error. We've canceled the local
+ * membership already anyway, but if the remote server comes back up
+ * it'll be left with a stray membership record.
+ *
+ * @param User_group $group
+ * @param User $user
+ *
+ * @return mixed hook return value
+ */
+
+ function onEndLeaveGroup($group, $user)
+ {
+ $oprofile = Ostatus_profile::staticGet('group_id', $group->id);
+ if ($oprofile) {
+ // Drop the PuSH subscription if there are no other subscribers.
+
+ $members = $group->getMembers(0, 1);
+ if ($members->N == 0) {
+ common_log(LOG_INFO, "Unsubscribing from now-unused group feed $oprofile->feeduri");
+ $oprofile->unsubscribe();
+ }
+
+
+ $member = Profile::staticGet($user->id);
+
+ $act = new Activity();
+ $act->id = TagURI::mint('leave:%d:%d:%s',
+ $member->id,
+ $group->id,
+ common_date_iso8601(time()));
+
+ $act->actor = ActivityObject::fromProfile($member);
+ $act->verb = ActivityVerb::LEAVE;
+ $act->object = $oprofile->asActivityObject();
+
+ $act->time = time();
+ $act->title = _m("Leave");
+ $act->content = sprintf(_m("%s has left group %s."),
+ $member->getBestName(),
+ $oprofile->getBestName());
+
+ $oprofile->notifyActivity($act);
+ }
+ }
+
+ /**
* Notify remote users when their notices get favorited.
*
* @param Profile or User $profile of local user doing the faving
@@ -500,6 +591,22 @@ class OStatusPlugin extends Plugin
return true;
}
+ function onStartUserGroupHomeUrl($group, &$url)
+ {
+ return $this->onStartUserGroupPermalink($group, &$url);
+ }
+
+ function onStartUserGroupPermalink($group, &$url)
+ {
+ $oprofile = Ostatus_profile::staticGet('group_id', $group->id);
+ if ($oprofile) {
+ // @fixme this should probably be in the user_group table
+ // @fixme this uri not guaranteed to be a profile page
+ $url = $oprofile->uri;
+ return false;
+ }
+ }
+
function onStartShowSubscriptionsContent($action)
{
$user = common_current_user();
diff --git a/plugins/OStatus/actions/groupsalmon.php b/plugins/OStatus/actions/groupsalmon.php
index 64ae9f3cc..2e4fe9443 100644
--- a/plugins/OStatus/actions/groupsalmon.php
+++ b/plugins/OStatus/actions/groupsalmon.php
@@ -88,21 +88,96 @@ class GroupsalmonAction extends SalmonAction
* Save a subscription relationship for them.
*/
+ /**
+ * Postel's law: consider a "follow" notification as a "join".
+ */
function handleFollow()
{
- $this->handleJoin(); // ???
+ $this->handleJoin();
}
+ /**
+ * Postel's law: consider an "unfollow" notification as a "leave".
+ */
function handleUnfollow()
{
+ $this->handleLeave();
}
/**
* A remote user joined our group.
+ * @fixme move permission checks and event call into common code,
+ * currently we're doing the main logic in joingroup action
+ * and so have to repeat it here.
*/
function handleJoin()
{
+ $oprofile = $this->ensureProfile();
+ if (!$oprofile) {
+ $this->clientError(_m("Can't read profile to set up group membership."));
+ }
+ if ($oprofile->isGroup()) {
+ $this->clientError(_m("Groups can't join groups."));
+ }
+
+ common_log(LOG_INFO, "Remote profile {$oprofile->uri} joining local group {$this->group->nickname}");
+ $profile = $oprofile->localProfile();
+
+ if ($profile->isMember($this->group)) {
+ // Already a member; we'll take it silently to aid in resolving
+ // inconsistencies on the other side.
+ return true;
+ }
+
+ if (Group_block::isBlocked($this->group, $profile)) {
+ $this->clientError(_('You have been blocked from that group by the admin.'), 403);
+ return false;
+ }
+
+ try {
+ // @fixme that event currently passes a user from main UI
+ // Event should probably move into Group_member::join
+ // and take a Profile object.
+ //
+ //if (Event::handle('StartJoinGroup', array($this->group, $profile))) {
+ Group_member::join($this->group->id, $profile->id);
+ //Event::handle('EndJoinGroup', array($this->group, $profile));
+ //}
+ } catch (Exception $e) {
+ $this->serverError(sprintf(_m('Could not join remote user %1$s to group %2$s.'),
+ $oprofile->uri, $this->group->nickname));
+ }
+ }
+
+ /**
+ * A remote user left our group.
+ */
+
+ function handleLeave()
+ {
+ $oprofile = $this->ensureProfile();
+ if (!$oprofile) {
+ $this->clientError(_m("Can't read profile to cancel group membership."));
+ }
+ if ($oprofile->isGroup()) {
+ $this->clientError(_m("Groups can't join groups."));
+ }
+
+ common_log(LOG_INFO, "Remote profile {$oprofile->uri} leaving local group {$this->group->nickname}");
+ $profile = $oprofile->localProfile();
+
+ try {
+ // @fixme event needs to be refactored as above
+ //if (Event::handle('StartLeaveGroup', array($this->group, $profile))) {
+ Group_member::leave($this->group->id, $profile->id);
+ //Event::handle('EndLeaveGroup', array($this->group, $profile));
+ //}
+ } catch (Exception $e) {
+ $this->serverError(sprintf(_m('Could not remove remote user %1$s from group %2$s.'),
+ $oprofile->uri, $this->group->nickname));
+ return;
+ }
}
}
diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php
index 95dec19af..592ae387e 100644
--- a/plugins/OStatus/actions/ostatussub.php
+++ b/plugins/OStatus/actions/ostatussub.php
@@ -248,7 +248,7 @@ class OStatusSubAction extends Action
$group = $this->oprofile->localGroup();
if ($user->isMember($group)) {
$this->showForm(_m('Already a member!'));
- } elseif (Group_member::join($this->profile->group_id, $user->id)) {
+ } elseif (Group_member::join($this->oprofile->group_id, $user->id)) {
$this->showForm(_m('Joined remote group!'));
} else {
$this->showForm(_m('Remote group join failed!'));
diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php
index 0e12f8fc6..e8cc13c6c 100644
--- a/plugins/OStatus/classes/Ostatus_profile.php
+++ b/plugins/OStatus/classes/Ostatus_profile.php
@@ -138,11 +138,48 @@ class Ostatus_profile extends Memcached_DataObject
}
/**
+ * Returns an ActivityObject describing this remote user or group profile.
+ * Can then be used to generate Atom chunks.
+ *
+ * @return ActivityObject
+ */
+ function asActivityObject()
+ {
+ if ($this->isGroup()) {
+ $object = new ActivityObject();
+ $object->type = 'http://activitystrea.ms/schema/1.0/group';
+ $object->id = $this->uri;
+ $self = $this->localGroup();
+
+ // @fixme put a standard getAvatar() interface on groups too
+ if ($self->homepage_logo) {
+ $object->avatar = $self->homepage_logo;
+ $map = array('png' => 'image/png',
+ 'jpg' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'gif' => 'image/gif');
+ $extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION);
+ if (isset($map[$extension])) {
+ // @fixme this ain't used/saved yet
+ $object->avatarType = $map[$extension];
+ }
+ }
+
+ $object->link = $this->uri; // @fixme accurate?
+ return $object;
+ } else {
+ return ActivityObject::fromProfile($this->localProfile());
+ }
+ }
+
+ /**
* Returns an XML string fragment with profile information as an
* Activity Streams noun object with the given element type.
*
* Assumes that 'activity' namespace has been previously defined.
*
+ * @fixme replace with wrappers on asActivityObject when it's got everything.
+ *
* @param string $element one of 'actor', 'subject', 'object', 'target'
* @return string
*/
@@ -202,11 +239,19 @@ class Ostatus_profile extends Memcached_DataObject
}
/**
- * Damn dirty hack!
+ * @return boolean true if this is a remote group
*/
function isGroup()
{
- return (strpos($this->feeduri, '/groups/') !== false);
+ if ($this->profile_id && !$this->group_id) {
+ return false;
+ } else if ($this->group_id && !$this->profile_id) {
+ return true;
+ } else if ($this->group_id && $this->profile_id) {
+ throw new ServerException("Invalid ostatus_profile state: both group and profile IDs set for $this->uri");
+ } else {
+ throw new ServerException("Invalid ostatus_profile state: both group and profile IDs empty for $this->uri");
+ }
}
/**
@@ -353,22 +398,24 @@ class Ostatus_profile extends Memcached_DataObject
common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml");
$salmon = new Salmon(); // ?
- $salmon->post($this->salmonuri, $xml);
+ return $salmon->post($this->salmonuri, $xml);
}
+ return false;
}
public function notifyActivity($activity)
{
if ($this->salmonuri) {
- $xml = $activity->asString(true);
+ $xml = '<?xml version="1.0" encoding="UTF-8" ?' . '>' .
+ $activity->asString(true);
$salmon = new Salmon(); // ?
- $salmon->post($this->salmonuri, $xml);
+ return $salmon->post($this->salmonuri, $xml);
}
- return;
+ return false;
}
function getBestName()
@@ -454,6 +501,7 @@ class Ostatus_profile extends Memcached_DataObject
/**
* Process an incoming post activity from this remote feed.
* @param Activity $activity
+ * @fixme break up this function, it's getting nasty long
*/
protected function processPost($activity)
{
@@ -471,7 +519,6 @@ class Ostatus_profile extends Memcached_DataObject
}
$oprofile = $this;
}
-
$sourceUri = $activity->object->id;
$dupe = Notice::staticGet('uri', $sourceUri);
@@ -508,15 +555,76 @@ class Ostatus_profile extends Memcached_DataObject
}
}
- // @fixme ensure that groups get handled correctly
+ $profile = $oprofile->localProfile();
+ $params['groups'] = array();
+ $params['replies'] = array();
+ if ($activity->context) {
+ foreach ($activity->context->attention as $recipient) {
+ $roprofile = Ostatus_profile::staticGet('uri', $recipient);
+ if ($roprofile) {
+ if ($roprofile->isGroup()) {
+ // Deliver to local recipients of this remote group.
+ // @fixme sender verification?
+ $params['groups'][] = $roprofile->group_id;
+ continue;
+ } else {
+ // Delivery to remote users is the source service's job.
+ continue;
+ }
+ }
+
+ $user = User::staticGet('uri', $recipient);
+ if ($user) {
+ // An @-reply directed to a local user.
+ // @fixme sender verification, spam etc?
+ $params['replies'][] = $recipient;
+ continue;
+ }
+
+ // @fixme we need a uri on user_group
+ // $group = User_group::staticGet('uri', $recipient);
+ $template = common_local_url('groupbyid', array('id' => '31337'));
+ $template = preg_quote($template, '/');
+ $template = str_replace('31337', '(\d+)', $template);
+ common_log(LOG_DEBUG, $template);
+ if (preg_match("/$template/", $recipient, $matches)) {
+ $id = $matches[1];
+ $group = User_group::staticGet('id', $id);
+ if ($group) {
+ // Deliver to all members of this local group.
+ // @fixme sender verification?
+ if ($profile->isMember($group)) {
+ common_log(LOG_DEBUG, "delivering to group $id $group->nickname");
+ $params['groups'][] = $group->id;
+ } else {
+ common_log(LOG_DEBUG, "not delivering to group $id $group->nickname because sender $profile->nickname is not a member");
+ }
+ continue;
+ } else {
+ common_log(LOG_DEBUG, "not delivering to missing group $id");
+ }
+ } else {
+ common_log(LOG_DEBUG, "not delivering to groups for $recipient");
+ }
+ }
+ }
- $saved = Notice::saveNew($oprofile->localProfile()->id,
- $content,
- 'ostatus',
- $params);
+ try {
+ $saved = Notice::saveNew($profile->id,
+ $content,
+ 'ostatus',
+ $params);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, "Failed saving notice entry for $sourceUri: " . $e->getMessage());
+ return;
+ }
// Record which feed this came through...
- Ostatus_source::saveNew($saved, $this, 'push');
+ try {
+ Ostatus_source::saveNew($saved, $this, 'push');
+ } catch (Exception $e) {
+ common_log(LOG_ERR, "Failed saving ostatus_source entry for $saved->notice_id: " . $e->getMessage());
+ }
}
/**
@@ -597,10 +705,23 @@ class Ostatus_profile extends Memcached_DataObject
*/
protected function updateAvatar($url)
{
+ if ($this->isGroup()) {
+ $self = $this->localGroup();
+ } else {
+ $self = $this->localProfile();
+ }
+ if (!$self) {
+ throw new ServerException(sprintf(
+ _m("Tried to update avatar for unsaved remote profile %s"),
+ $this->uri));
+ }
+
// @fixme this should be better encapsulated
// ripped from oauthstore.php (for old OMB client)
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
- copy($url, $temp_filename);
+ if (!copy($url, $temp_filename)) {
+ throw new ServerException(sprintf(_m("Unable to fetch avatar from %s"), $url));
+ }
if ($this->isGroup()) {
$id = $this->group_id;
@@ -614,13 +735,7 @@ class Ostatus_profile extends Memcached_DataObject
null,
common_timestamp());
rename($temp_filename, Avatar::path($filename));
- if ($this->isGroup()) {
- $group = $this->localGroup();
- $group->setOriginal($filename);
- } else {
- $profile = $this->localProfile();
- $profile->setOriginal($filename);
- }
+ $self->setOriginal($filename);
}
protected static function getActivityObjectAvatar($object)
@@ -747,6 +862,18 @@ class Ostatus_profile extends Memcached_DataObject
self::createActivityObjectProfile($actor, $feeduri, $salmonuri);
}
+ /**
+ * Create local ostatus_profile and profile/user_group entries for
+ * the provided remote user or group.
+ *
+ * @param ActivityObject $object
+ * @param string $feeduri
+ * @param string $salmonuri
+ * @param array $hints
+ *
+ * @fixme fold $feeduri/$salmonuri into $hints
+ * @return Ostatus_profile
+ */
protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array())
{
$homeuri = $object->id;
@@ -784,46 +911,65 @@ class Ostatus_profile extends Memcached_DataObject
}
}
- $profile = new Profile();
- $profile->nickname = $nickname;
- $profile->fullname = $object->title;
- if (!empty($object->link)) {
- $profile->profileurl = $object->link;
- } else if (array_key_exists('profileurl', $hints)) {
- $profile->profileurl = $hints['profileurl'];
- }
- $profile->created = common_sql_now();
-
- // @fixme bio
- // @fixme tags/categories
- // @fixme location?
- // @todo tags from categories
- // @todo lat/lon/location?
-
- $profile_id = $profile->insert();
-
- if (!$profile_id) {
- throw new ServerException("Can't save local profile");
- }
-
- // @fixme either need to do feed discovery here
- // or need to split out some of the feed stuff
- // so we can leave it empty until later.
-
$oprofile = new Ostatus_profile();
$oprofile->uri = $homeuri;
$oprofile->feeduri = $feeduri;
$oprofile->salmonuri = $salmonuri;
- $oprofile->profile_id = $profile_id;
$oprofile->created = common_sql_now();
$oprofile->modified = common_sql_now();
+ if ($object->type == ActivityObject::PERSON) {
+ $profile = new Profile();
+ $profile->nickname = $nickname;
+ $profile->fullname = $object->title;
+ if (!empty($object->link)) {
+ $profile->profileurl = $object->link;
+ } else if (array_key_exists('profileurl', $hints)) {
+ $profile->profileurl = $hints['profileurl'];
+ }
+ $profile->created = common_sql_now();
+
+ // @fixme bio
+ // @fixme tags/categories
+ // @fixme location?
+ // @todo tags from categories
+ // @todo lat/lon/location?
+
+ $oprofile->profile_id = $profile->insert();
+
+ if (!$oprofile->profile_id) {
+ throw new ServerException("Can't save local profile");
+ }
+ } else {
+ $group = new User_group();
+ $group->nickname = $nickname;
+ $group->fullname = $object->title;
+ // @fixme no canonical profileurl; using homepage instead for now
+ $group->homepage = $homeuri;
+ $group->created = common_sql_now();
+
+ // @fixme homepage
+ // @fixme bio
+ // @fixme tags/categories
+ // @fixme location?
+ // @todo tags from categories
+ // @todo lat/lon/location?
+
+ $oprofile->group_id = $group->insert();
+
+ if (!$oprofile->group_id) {
+ throw new ServerException("Can't save local profile");
+ }
+ }
+
$ok = $oprofile->insert();
if ($ok) {
- $oprofile->updateAvatar($avatar);
+ if ($avatar) {
+ $oprofile->updateAvatar($avatar);
+ }
return $oprofile;
} else {
throw new ServerException("Can't save OStatus profile");
diff --git a/plugins/OStatus/js/ostatus.js b/plugins/OStatus/js/ostatus.js
index 8ba424a53..1fc44b21b 100644
--- a/plugins/OStatus/js/ostatus.js
+++ b/plugins/OStatus/js/ostatus.js
@@ -52,8 +52,7 @@ SN.U.StatusNetInstance = {
SN.Init.OStatusCookie = function() {
if (SN.U.StatusNetInstance.Get() === null) {
- SN.C.I.OStatusProfile = SN.C.I.OStatusProfile || null;
- SN.U.StatusNetInstance.Set({profile: SN.C.I.OStatusProfile});
+ SN.U.StatusNetInstance.Set({profile: null});
}
};
@@ -64,11 +63,10 @@ SN.U.DialogBox = {
f.show();
}
else {
- a[0].href = (a[0].href.match(/[\\?]/) === null) ? a[0].href+'?' : a[0].href+'&';
$.ajax({
type: 'GET',
dataType: 'xml',
- url: a[0].href+'ajax=1',
+ url: a[0].href + ((a[0].href.match(/[\\?]/) === null)?'?':'&') + 'ajax=1',
beforeSend: function(formData) {
a.addClass('processing');
},
diff --git a/plugins/OStatus/lib/hubdistribqueuehandler.php b/plugins/OStatus/lib/hubdistribqueuehandler.php
index 30a427e3f..c2bd630f9 100644
--- a/plugins/OStatus/lib/hubdistribqueuehandler.php
+++ b/plugins/OStatus/lib/hubdistribqueuehandler.php
@@ -36,7 +36,7 @@ class HubDistribQueueHandler extends QueueHandler
$this->pushUser($notice);
foreach ($notice->getGroups() as $group) {
- $this->pushGroup($notice, $group->group_id);
+ $this->pushGroup($notice, $group->id);
}
return true;
}
diff --git a/plugins/OStatus/lib/magicsig.php b/plugins/OStatus/lib/magicsig.php
index af65bad04..50eb301ab 100644
--- a/plugins/OStatus/lib/magicsig.php
+++ b/plugins/OStatus/lib/magicsig.php
@@ -57,8 +57,10 @@ class MagicsigRsaSha256
$keypair = new Crypt_RSA_KeyPair($key_length);
$params['public_key'] = $keypair->getPublicKey();
$params['private_key'] = $keypair->getPrivateKey();
-
+
+ PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$this->keypair = new Crypt_RSA($params);
+ PEAR::popErrorHandling();
}
@@ -79,6 +81,8 @@ class MagicsigRsaSha256
public function fromString($text)
{
+ PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+
// remove whitespace
$text = preg_replace('/\s+/', '', $text);
@@ -86,7 +90,6 @@ class MagicsigRsaSha256
if (!preg_match('/RSA\.([^\.]+)\.([^\.]+)(.([^\.]+))?/', $text, $matches)) {
return false;
}
-
$mod = base64_url_decode($matches[1]);
$exp = base64_url_decode($matches[2]);
@@ -110,6 +113,7 @@ class MagicsigRsaSha256
}
$this->keypair = new Crypt_RSA($params);
+ PEAR::popErrorHandling();
}
public function getName()
diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php
index 53925dc3f..b5f178cc6 100644
--- a/plugins/OStatus/lib/salmon.php
+++ b/plugins/OStatus/lib/salmon.php
@@ -28,15 +28,26 @@
*/
class Salmon
{
+ /**
+ * Sign and post the given Atom entry as a Salmon message.
+ *
+ * @fixme pass through the actor for signing?
+ *
+ * @param string $endpoint_uri
+ * @param string $xml
+ * @return boolean success
+ */
public function post($endpoint_uri, $xml)
{
if (empty($endpoint_uri)) {
- return FALSE;
+ return false;
}
- $xml = $this->createMagicEnv($xml);
-
- $headers = array('Content-type: application/atom+xml');
+ if (!common_config('ostatus', 'skip_signatures')) {
+ $xml = $this->createMagicEnv($xml);
+ }
+
+ $headers = array('Content-Type: application/atom+xml');
try {
$client = new HTTPClient();
@@ -51,7 +62,7 @@ class Salmon
$response->getStatus() . ': ' . $response->getBody());
return false;
}
-
+ return true;
}
public function createMagicEnv($text)
diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php
index 09a042975..83cf0b8f8 100644
--- a/plugins/OStatus/lib/salmonaction.php
+++ b/plugins/OStatus/lib/salmonaction.php
@@ -41,7 +41,7 @@ class SalmonAction extends Action
$this->clientError(_('This method requires a POST.'));
}
- if ($_SERVER['CONTENT_TYPE'] != 'application/atom+xml') {
+ if (empty($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/atom+xml') {
$this->clientError(_('Salmon requires application/atom+xml'));
}
@@ -57,11 +57,13 @@ class SalmonAction extends Action
// Check the signature
$salmon = new Salmon;
- if (!$salmon->verifyMagicEnv($dom)) {
- common_log(LOG_DEBUG, "Salmon signature verification failed.");
- $this->clientError(_m('Salmon signature verification failed.'));
+ if (!common_config('ostatus', 'skip_signatures')) {
+ if (!$salmon->verifyMagicEnv($dom)) {
+ common_log(LOG_DEBUG, "Salmon signature verification failed.");
+ $this->clientError(_m('Salmon signature verification failed.'));
+ }
}
-
+
$this->act = new Activity($dom->documentElement);
return true;
}
@@ -101,6 +103,9 @@ class SalmonAction extends Action
case ActivityVerb::JOIN:
$this->handleJoin();
break;
+ case ActivityVerb::LEAVE:
+ $this->handleLeave();
+ break;
default:
throw new ClientException(_("Unimplemented."));
}
@@ -155,6 +160,14 @@ class SalmonAction extends Action
}
/**
+ * Hmmmm
+ */
+ function handleLeave()
+ {
+ throw new ClientException(_("Unimplemented!"));
+ }
+
+ /**
* @return Ostatus_profile
*/
function ensureProfile()