public static function fromUrl($url, $depth = 0)
 {
     common_debug('MentionURL: trying to find a profile for ' . $url);
     $url = preg_replace('#https?://#', 'https://', $url);
     try {
         $profile = Profile::fromUri($url);
     } catch (UnknownUriException $ex) {
     }
     if (!$profile instanceof Profile) {
         $profile = self::findProfileByProfileURL($url);
     }
     $url = str_replace('https://', 'http://', $url);
     if (!$profile instanceof Profile) {
         try {
             $profile = Profile::fromUri($url);
         } catch (UnknownUriException $ex) {
         }
     }
     if (!$profile instanceof Profile) {
         $profile = self::findProfileByProfileURL($url);
     }
     if (!$profile instanceof Profile) {
         $hcard = mention_url_representative_hcard($url);
         if (!$hcard) {
             return null;
         }
         $mention_profile = new Mention_url_profile();
         $mention_profile->query('BEGIN');
         $profile = new Profile();
         $profile->profileurl = $hcard['url'][0];
         $profile->fullname = $hcard['name'][0];
         preg_match('/\\/([^\\/]+)\\/*$/', $profile->profileurl, $matches);
         if (!$hcard['nickname']) {
             $hcard['nickname'] = array($matches[1]);
         }
         $profile->nickname = $hcard['nickname'][0];
         $profile->created = common_sql_now();
         $mention_profile->profile_id = $profile->insert();
         if (!$mention_profile->profile_id) {
             $mention_profile->query('ROLLBACK');
             return null;
         }
         $mention_profile->profileurl = $profile->profileurl;
         if (!$mention_profile->insert()) {
             $mention_profile->query('ROLLBACK');
             if ($depth > 0) {
                 return null;
             } else {
                 return self::fromUrl($url, $depth + 1);
             }
         } else {
             $mention_profile->query('COMMIT');
         }
     }
     return $profile;
 }
Exemplo n.º 2
0
 protected function doPreparation()
 {
     $id = $this->trimmed('id');
     $uri = $this->trimmed('uri');
     if (!empty($id)) {
         $this->target = Profile::getKV('id', $id);
         if (!$this->target instanceof Profile) {
             // TRANS: Client error displayed when referring to non-existing profile ID.
             $this->clientError(_('No profile with that ID.'));
         }
     } elseif (!empty($uri)) {
         $this->target = Profile::fromUri($uri);
     } else {
         // TRANS: Client error displayed when trying to tag a user but no ID or profile is provided.
         $this->clientError(_('No profile identifier provided.'));
     }
     if (!$this->scoped->canTag($this->target)) {
         // TRANS: Client error displayed when trying to tag a user that cannot be tagged.
         $this->clientError(_('You cannot tag this user.'));
     }
     $this->formOpts = $this->target;
     return true;
 }
Exemplo n.º 3
0
 function subscribeProfile($user, $author, $activity)
 {
     $profile = $user->getProfile();
     if ($activity->objects[0]->id == $author->id) {
         if (!$this->trusted) {
             // TRANS: Client exception thrown when trying to force a subscription for an untrusted user.
             throw new ClientException(_('Cannot force subscription for untrusted user.'));
         }
         $other = $activity->actor;
         $otherUser = User::getKV('uri', $other->id);
         if (!$otherUser instanceof User) {
             // TRANS: Client exception thrown when trying to force a remote user to subscribe.
             throw new Exception(_('Cannot force remote user to subscribe.'));
         }
         $otherProfile = $otherUser->getProfile();
         // XXX: don't do this for untrusted input!
         Subscription::ensureStart($otherProfile, $profile);
     } else {
         if (empty($activity->actor) || $activity->actor->id == $author->id) {
             $other = $activity->objects[0];
             try {
                 $otherProfile = Profile::fromUri($other->id);
                 // TRANS: Client exception thrown when trying to subscribe to an unknown profile.
             } catch (UnknownUriException $e) {
                 // Let's convert it to a client exception instead of server.
                 throw new ClientException(_('Unknown profile.'));
             }
             Subscription::ensureStart($profile, $otherProfile);
         } else {
             // TRANS: Client exception thrown when trying to import an event not related to the importing user.
             throw new Exception(_('This activity seems unrelated to our user.'));
         }
     }
 }
 /**
  * Add a new subscription
  *
  * Handling the POST method for AtomPub
  *
  * @return void
  */
 function addSubscription()
 {
     if (empty($this->auth_user) || $this->auth_user->id != $this->_profile->id) {
         // TRANS: Client exception thrown when trying to subscribe another user.
         throw new ClientException(_("Cannot add someone else's" . " subscription."), 403);
     }
     $xml = file_get_contents('php://input');
     $dom = DOMDocument::loadXML($xml);
     if ($dom->documentElement->namespaceURI != Activity::ATOM || $dom->documentElement->localName != 'entry') {
         // TRANS: Client error displayed when not using an Atom entry.
         $this->clientError(_('Atom post must be an Atom entry.'));
     }
     $activity = new Activity($dom->documentElement);
     $sub = null;
     if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
         if ($activity->verb != ActivityVerb::FOLLOW) {
             // TRANS: Client error displayed when not using the follow verb.
             $this->clientError(_('Can only handle Follow activities.'));
         }
         $person = $activity->objects[0];
         if ($person->type != ActivityObject::PERSON) {
             // TRANS: Client exception thrown when subscribing to an object that is not a person.
             $this->clientError(_('Can only follow people.'));
         }
         // XXX: OStatus discovery (maybe)
         try {
             $profile = Profile::fromUri($person->id);
         } catch (UnknownUriException $e) {
             // TRANS: Client exception thrown when subscribing to a non-existing profile.
             // TRANS: %s is the unknown profile ID.
             $this->clientError(sprintf(_('Unknown profile %s.'), $person->id));
         }
         try {
             $sub = Subscription::start($this->_profile, $profile);
         } catch (AlreadyFulfilledException $e) {
             // 409 Conflict
             $this->clientError($e->getMessage(), 409);
         }
         Event::handle('EndAtomPubNewActivity', array($activity, $sub));
     }
     if (!empty($sub)) {
         $act = $sub->asActivity();
         header('Content-Type: application/atom+xml; charset=utf-8');
         header('Content-Location: ' . $act->selfLink);
         $this->startXML();
         $this->raw($act->asString(true, true, true));
         $this->endXML();
     }
 }
Exemplo n.º 5
0
$notice_id = get_option_value('--notice');
$notice = Notice::getKV('id', $notice_id);
$profile = $notice->getProfile();
$entry = $notice->asAtomEntry(true);
echo "== Original entry ==\n\n";
print $entry;
print "\n\n";
$magic_env = MagicEnvelope::signAsUser($entry, $profile->getUser());
$envxml = $magic_env->toXML();
echo "== Signed envelope ==\n\n";
print $envxml;
print "\n\n";
echo "== Testing local verification ==\n\n";
$magic_env = new MagicEnvelope($envxml);
$activity = new Activity($magic_env->getPayload()->documentElement);
$actprofile = Profile::fromUri($activity->actor->id);
$ok = $magic_env->verify($actprofile);
if ($ok) {
    print "OK\n\n";
} else {
    print "FAIL\n\n";
}
if (have_option('--verify')) {
    $url = 'http://www.madebymonsieur.com/ostatus_discovery/magic_env/validate/';
    echo "== Testing remote verification ==\n\n";
    print "Sending for verification to {$url} ...\n";
    $client = new HTTPClient();
    $response = $client->post($url, array(), array('magic_env' => $envxml));
    print $response->getStatus() . "\n\n";
    print $response->getBody() . "\n\n";
}
Exemplo n.º 6
0
 function postNote($activity)
 {
     $note = $activity->objects[0];
     // Use summary as fallback for content
     if (!empty($note->content)) {
         $sourceContent = $note->content;
     } else {
         if (!empty($note->summary)) {
             $sourceContent = $note->summary;
         } else {
             if (!empty($note->title)) {
                 $sourceContent = $note->title;
             } else {
                 // @fixme fetch from $sourceUrl?
                 // TRANS: Client error displayed when posting a notice without content through the API.
                 // TRANS: %d is the notice ID (number).
                 $this->clientError(sprintf(_('No content for notice %d.'), $note->id));
             }
         }
     }
     // Get (safe!) HTML and text versions of the content
     $rendered = common_purify($sourceContent);
     $content = common_strip_html($rendered);
     $shortened = $this->auth_user->shortenLinks($content);
     $options = array('is_local' => Notice::LOCAL_PUBLIC, 'rendered' => $rendered, 'replies' => array(), 'groups' => array(), 'tags' => array(), 'urls' => array());
     // accept remote URI (not necessarily a good idea)
     common_debug("Note ID is {$note->id}");
     if (!empty($note->id)) {
         $notice = Notice::getKV('uri', trim($note->id));
         if (!empty($notice)) {
             // TRANS: Client error displayed when using another format than AtomPub.
             // TRANS: %s is the notice URI.
             $this->clientError(sprintf(_('Notice with URI "%s" already exists.'), $note->id));
         }
         common_log(LOG_NOTICE, "Saving client-supplied notice URI '{$note->id}'");
         $options['uri'] = $note->id;
     }
     // accept remote create time (also maybe not such a good idea)
     if (!empty($activity->time)) {
         common_log(LOG_NOTICE, "Saving client-supplied create time {$activity->time}");
         $options['created'] = common_sql_date($activity->time);
     }
     // Check for optional attributes...
     if ($activity->context instanceof ActivityContext) {
         foreach ($activity->context->attention as $uri => $type) {
             try {
                 $profile = Profile::fromUri($uri);
                 if ($profile->isGroup()) {
                     $options['groups'][] = $profile->id;
                 } else {
                     $options['replies'][] = $uri;
                 }
             } catch (UnknownUriException $e) {
                 common_log(LOG_WARNING, sprintf('AtomPub post with unknown attention URI %s', $uri));
             }
         }
         // Maintain direct reply associations
         // @fixme what about conversation ID?
         if (!empty($activity->context->replyToID)) {
             $orig = Notice::getKV('uri', $activity->context->replyToID);
             if (!empty($orig)) {
                 $options['reply_to'] = $orig->id;
             }
         }
         $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;
             }
         }
     }
     // Atom categories <-> hashtags
     foreach ($activity->categories as $cat) {
         if ($cat->term) {
             $term = common_canonical_tag($cat->term);
             if ($term) {
                 $options['tags'][] = $term;
             }
         }
     }
     // Atom enclosures -> attachment URLs
     foreach ($activity->enclosures as $href) {
         // @fixme save these locally or....?
         $options['urls'][] = $href;
     }
     $saved = Notice::saveNew($this->target->id, $content, 'atompub', $options);
     return $saved;
 }
Exemplo n.º 7
0
 /**
  * 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.
  *
  * Mail notifications etc will be handled later.
  *
  * @param array  $uris   Array of unique identifier URIs for recipients
  */
 function saveKnownReplies(array $uris)
 {
     if (empty($uris)) {
         return;
     }
     $sender = $this->getProfile();
     foreach (array_unique($uris) as $uri) {
         try {
             $profile = Profile::fromUri($uri);
         } catch (UnknownUriException $e) {
             common_log(LOG_WARNING, "Unable to determine profile for URI '{$uri}'");
             continue;
         }
         if ($profile->hasBlocked($sender)) {
             common_log(LOG_INFO, "Not saving reply to profile {$profile->id} ({$uri}) from sender {$sender->id} because of a block.");
             continue;
         }
         $this->saveReply($profile->getID());
         self::blow('reply:stream:%d', $profile->getID());
     }
 }
Exemplo n.º 8
0
 function subscribeProfile($user, $author, $activity)
 {
     $profile = $user->getProfile();
     if ($activity->objects[0]->id == $author->id) {
         if (!$this->trusted) {
             // TRANS: Client exception thrown when trying to force a subscription for an untrusted user.
             throw new ClientException(_("Cannot force subscription for untrusted user."));
         }
         $other = $activity->actor;
         $otherUser = User::staticGet('uri', $other->id);
         if (!empty($otherUser)) {
             $otherProfile = $otherUser->getProfile();
         } else {
             // TRANS: Client exception thrown when trying to for a remote user to subscribe.
             throw new Exception(_("Cannot force remote user to subscribe."));
         }
         // XXX: don't do this for untrusted input!
         Subscription::start($otherProfile, $profile);
     } else {
         if (empty($activity->actor) || $activity->actor->id == $author->id) {
             $other = $activity->objects[0];
             $otherProfile = Profile::fromUri($other->id);
             if (empty($otherProfile)) {
                 // TRANS: Client exception thrown when trying to subscribe to an unknown profile.
                 throw new ClientException(_("Unknown profile."));
             }
             Subscription::start($profile, $otherProfile);
         } else {
             // TRANS: Client exception thrown when trying to import an event not related to the importing user.
             throw new Exception(_("This activity seems unrelated to our user."));
         }
     }
 }
Exemplo n.º 9
0
 function moveActivity($act, $sink, $user, $remote)
 {
     if (empty($user)) {
         // TRANS: Exception thrown if a non-existing user is provided. %s is a user ID.
         throw new Exception(sprintf(_('No such user "%s".'), $act->actor->id));
     }
     switch ($act->verb) {
         /*        case ActivityVerb::FAVORITE:
                   $this->log(LOG_INFO,
                              "Moving favorite of {$act->objects[0]->id} by ".
                              "{$act->actor->id} to {$remote->nickname}.");
                   // push it, then delete local
                   $sink->postActivity($act);
                   $notice = Notice::getKV('uri', $act->objects[0]->id);
                   if (!empty($notice)) {
                       $fave = Fave::pkeyGet(array('user_id' => $user->id,
                                                   'notice_id' => $notice->id));
                       $fave->delete();
                   }
                   break;*/
         case ActivityVerb::POST:
             $this->log(LOG_INFO, "Moving notice {$act->objects[0]->id} by " . "{$act->actor->id} to {$remote->nickname}.");
             // XXX: send a reshare, not a post
             $sink->postActivity($act);
             $notice = Notice::getKV('uri', $act->objects[0]->id);
             if (!empty($notice)) {
                 $notice->delete();
             }
             break;
         case ActivityVerb::JOIN:
             $this->log(LOG_INFO, "Moving group join of {$act->objects[0]->id} by " . "{$act->actor->id} to {$remote->nickname}.");
             $sink->postActivity($act);
             $group = User_group::getKV('uri', $act->objects[0]->id);
             if (!empty($group)) {
                 $user->leaveGroup($group);
             }
             break;
         case ActivityVerb::FOLLOW:
             if ($act->actor->id === $user->getUri()) {
                 $this->log(LOG_INFO, "Moving subscription to {$act->objects[0]->id} by " . "{$act->actor->id} to {$remote->nickname}.");
                 $sink->postActivity($act);
                 try {
                     $other = Profile::fromUri($act->objects[0]->id);
                     Subscription::cancel($user->getProfile(), $other);
                 } catch (UnknownUriException $e) {
                     // Can't cancel subscription if we don't know who to alert
                 }
             } else {
                 $otherUser = User::getKV('uri', $act->actor->id);
                 if (!empty($otherUser)) {
                     $this->log(LOG_INFO, "Changing sub to {$act->objects[0]->id}" . "by {$act->actor->id} to {$remote->nickname}.");
                     $otherProfile = $otherUser->getProfile();
                     Subscription::ensureStart($otherProfile, $remote);
                     Subscription::cancel($otherProfile, $user->getProfile());
                 } else {
                     $this->log(LOG_NOTICE, "Not changing sub to {$act->objects[0]->id}" . "by remote {$act->actor->id} " . "to {$remote->nickname}.");
                 }
             }
             break;
     }
 }
Exemplo n.º 10
0
 /**
  * Save a bookmark from an activity
  *
  * @param Activity $activity Activity to save
  * @param Profile  $actor    Profile to use as author
  * @param array    $options  Options to pass to bookmark-saving code
  *
  * @return Notice resulting notice
  */
 function saveNoticeFromActivity(Activity $activity, Profile $actor, array $options = array())
 {
     $bookmark = $activity->objects[0];
     $relLinkEls = ActivityUtils::getLinks($bookmark->element, 'related');
     if (count($relLinkEls) < 1) {
         // TRANS: Client exception thrown when a bookmark is formatted incorrectly.
         throw new ClientException(_m('Expected exactly 1 link ' . 'rel=related in a Bookmark.'));
     }
     if (count($relLinkEls) > 1) {
         common_log(LOG_WARNING, "Got too many link rel=related in a Bookmark.");
     }
     $linkEl = $relLinkEls[0];
     $url = $linkEl->getAttribute('href');
     $tags = array();
     foreach ($activity->categories as $category) {
         $tags[] = common_canonical_tag($category->term);
     }
     if (!empty($activity->time)) {
         $options['created'] = common_sql_date($activity->time);
     }
     // Fill in location if available
     $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;
         }
     }
     $options['groups'] = array();
     $options['replies'] = array();
     // TODO: context->attention
     foreach ($activity->context->attention as $attnUrl => $type) {
         try {
             $other = Profile::fromUri($attnUrl);
             if ($other->isGroup()) {
                 $options['groups'][] = $other->id;
             } else {
                 $options['replies'][] = $attnUrl;
             }
         } catch (UnknownUriException $e) {
             // We simply don't know this URI, despite lookup attempts.
         }
     }
     // Maintain direct reply associations
     // @fixme what about conversation ID?
     if (!empty($activity->context->replyToID)) {
         $orig = Notice::getKV('uri', $activity->context->replyToID);
         if (!empty($orig)) {
             $options['reply_to'] = $orig->id;
         }
     }
     return Bookmark::saveNew($actor, $bookmark->title, $url, $tags, $bookmark->summary, $options);
 }
Exemplo n.º 11
0
function linkback_profile($entry, $mf2, $response, $target)
{
    if (isset($entry['properties']['author']) && isset($entry['properties']['author'][0]['properties'])) {
        $author = $entry['properties']['author'][0]['properties'];
    } else {
        $author = linkback_hcard($mf2, $response->getEffectiveUrl());
    }
    if (!$author) {
        $author = array('name' => array($entry['name']));
    }
    if (!$author['url']) {
        $author['url'] = array($response->getEffectiveUrl());
    }
    $user = User::getKV('uri', $author['url'][0]);
    if ($user instanceof User) {
        common_log(LOG_INFO, "Linkback: ignoring linkback from local user: {$url}");
        return true;
    }
    try {
        $profile = Profile::fromUri($author['url'][0]);
    } catch (UnknownUriException $ex) {
    }
    if (!$profile instanceof Profile) {
        $profile = Profile::getKV('profileurl', $author['url'][0]);
    }
    if (!$profile instanceof Profile) {
        $profile = new Profile();
        $profile->profileurl = $author['url'][0];
        $profile->fullname = $author['name'][0];
        $profile->nickname = $author['nickname'] ? $author['nickname'][0] : str_replace(' ', '', $author['name'][0]);
        $profile->created = common_sql_now();
        $profile->insert();
    }
    return array($profile, $author);
}