*
* @param DOMDocument $feed
*/
- public function processFeed($feed)
+ public function processFeed($feed, $source)
{
$entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry');
if ($entries->length == 0) {
for ($i = 0; $i < $entries->length; $i++) {
$entry = $entries->item($i);
- $this->processEntry($entry, $feed);
+ $this->processEntry($entry, $feed, $source);
}
}
* @param DOMElement $entry
* @param DOMElement $feed for context
*/
- protected function processEntry($entry, $feed)
+ public function processEntry($entry, $feed, $source)
{
$activity = new Activity($entry, $feed);
- $debug = var_export($activity, true);
- common_log(LOG_DEBUG, $debug);
-
if ($activity->verb == ActivityVerb::POST) {
- $this->processPost($activity);
+ $this->processPost($activity, $source);
} else {
common_log(LOG_INFO, "Ignoring activity with unrecognized verb $activity->verb");
}
/**
* Process an incoming post activity from this remote feed.
* @param Activity $activity
+ * @param string $method 'push' or 'salmon'
+ * @return mixed saved Notice or false
* @fixme break up this function, it's getting nasty long
*/
- protected function processPost($activity)
+ public function processPost($activity, $method)
{
if ($this->isGroup()) {
+ // A group feed will contain posts from multiple authors.
// @fixme validate these profiles in some way!
$oprofile = self::ensureActorProfile($activity);
+ if ($oprofile->isGroup()) {
+ // Groups can't post notices in StatusNet.
+ common_log(LOG_WARNING, "OStatus: skipping post with group listed as author: $oprofile->uri in feed from $this->uri");
+ return false;
+ }
} else {
+ // Individual user feeds may contain only posts from themselves.
+ // Authorship is validated against the profile URI on upper layers,
+ // through PuSH setup or Salmon signature checks.
$actorUri = self::getActorProfileURI($activity);
if ($actorUri == $this->uri) {
// @fixme check if profile info has changed and update it
} else {
- // @fixme drop or reject the messages once we've got the canonical profile URI recorded sanely
- common_log(LOG_INFO, "OStatus: Warning: non-group post with unexpected author: $actorUri expected $this->uri");
- //return;
+ common_log(LOG_WARNING, "OStatus: skipping post with bad author: got $actorUri expected $this->uri");
+ return false;
}
$oprofile = $this;
}
- $sourceUri = $activity->object->id;
+ // The id URI will be used as a unique identifier for for the notice,
+ // protecting against duplicate saves. It isn't required to be a URL;
+ // tag: URIs for instance are found in Google Buzz feeds.
+ $sourceUri = $activity->object->id;
$dupe = Notice::staticGet('uri', $sourceUri);
-
if ($dupe) {
common_log(LOG_INFO, "OStatus: ignoring duplicate post: $sourceUri");
- return;
+ return false;
}
+ // We'll also want to save a web link to the original notice, if provided.
$sourceUrl = null;
-
if ($activity->object->link) {
$sourceUrl = $activity->object->link;
} else if ($activity->link) {
}
// Get (safe!) HTML and text versions of the content
-
- require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php');
-
- $html = $activity->object->content;
-
- $purifier = new HTMLPurifier();
-
- $rendered = $purifier->purify($html);
-
+ $rendered = $this->purify($activity->object->content);
$content = html_entity_decode(strip_tags($rendered));
- $params = array('is_local' => Notice::REMOTE_OMB,
+ $options = array('is_local' => Notice::REMOTE_OMB,
'url' => $sourceUrl,
'uri' => $sourceUri,
- 'rendered' => $rendered);
+ 'rendered' => $rendered,
+ 'replies' => array(),
+ 'groups' => array());
- $location = $activity->context->location;
+ // Check for optional attributes...
- if ($location) {
- $params['lat'] = $location->lat;
- $params['lon'] = $location->lon;
- if ($location->location_id) {
- $params['location_ns'] = $location->location_ns;
- $params['location_id'] = $location->location_id;
- }
+ if (!empty($activity->time)) {
+ $options['created'] = common_sql_date($activity->time);
}
- $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;
+ // Any individual or group attn: targets?
+ $replies = $activity->context->attention;
+ $options['groups'] = $this->filterReplies($oprofile, $replies);
+ $options['replies'] = $replies;
+
+ // Maintain direct reply associations
+ // @fixme what about conversation ID?
+ if (!empty($activity->context->replyToID)) {
+ $orig = Notice::staticGet('uri',
+ $activity->context->replyToID);
+ if (!empty($orig)) {
+ $options['reply_to'] = $orig->id;
}
-
- // @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");
+ }
+
+ $location = $activity->context->location;
+ if ($location) {
+ $options['lat'] = $location->lat;
+ $options['lon'] = $location->lon;
+ if ($location->location_id) {
+ $options['location_ns'] = $location->location_ns;
+ $options['location_id'] = $location->location_id;
}
}
}
try {
- $saved = Notice::saveNew($profile->id,
+ $saved = Notice::saveNew($oprofile->profile_id,
$content,
'ostatus',
- $params);
+ $options);
+ if ($saved) {
+ Ostatus_source::saveNew($saved, $this, $method);
+ }
} catch (Exception $e) {
- common_log(LOG_ERR, "Failed saving notice entry for $sourceUri: " . $e->getMessage());
- return;
+ common_log(LOG_ERR, "OStatus save of remote message $sourceUri failed: " . $e->getMessage());
+ throw $e;
}
+ common_log(LOG_INFO, "OStatus saved remote message $sourceUri as notice id $saved->id");
+ return $saved;
+ }
- // Record which feed this came through...
- 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());
+ /**
+ * Clean up HTML
+ */
+ protected function purify($html)
+ {
+ // @fixme disable caching or set a sane temp dir
+ require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php');
+ $purifier = new HTMLPurifier();
+ return $purifier->purify($html);
+ }
+
+ /**
+ * Filters a list of recipient ID URIs to just those for local delivery.
+ * @param Ostatus_profile local profile of sender
+ * @param array in/out &$attention_uris set of URIs, will be pruned on output
+ * @return array of group IDs
+ */
+ protected function filterReplies($sender, &$attention_uris)
+ {
+ $groups = array();
+ $replies = array();
+ foreach ($attention_uris as $recipient) {
+ // Is the recipient a local user?
+ $user = User::staticGet('uri', $recipient);
+ if ($user) {
+ // @fixme sender verification, spam etc?
+ $replies[] = $recipient;
+ continue;
+ }
+
+ // Is the recipient a remote group?
+ $oprofile = Ostatus_profile::staticGet('uri', $recipient);
+ if ($oprofile) {
+ if ($oprofile->isGroup()) {
+ // Deliver to local members of this remote group.
+ // @fixme sender verification?
+ $groups[] = $oprofile->group_id;
+ }
+ continue;
+ }
+
+ // Is the recipient a local group?
+ // @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);
+ 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 if allowed.
+ if ($sender->localProfile()->isMember($group)) {
+ $groups[] = $group->id;
+ }
+ continue;
+ }
+ }
}
+ $attention_uris = $replies;
+ return $groups;
}
/**