Пример #1
0
 /**
  * Retrieve Salmon keypair first by checking local database, but
  * if it's not found, attempt discovery if it has been requested.
  *
  * @param Profile $profile      The profile we're looking up keys for.
  * @param boolean $discovery    Network discovery if no local cache?
  */
 public function getKeyPair(Profile $profile, $discovery = false)
 {
     if (!$profile->isLocal()) {
         common_debug('Getting magic-public-key for non-local profile id==' . $profile->getID());
     }
     $magicsig = Magicsig::getKV('user_id', $profile->getID());
     if ($discovery && !$magicsig instanceof Magicsig) {
         if (!$profile->isLocal()) {
             common_debug('magic-public-key not found, will do discovery for profile id==' . $profile->getID());
         }
         // Throws exception on failure, but does not try to _load_ the keypair string.
         $keypair = $this->discoverKeyPair($profile);
         $magicsig = new Magicsig();
         $magicsig->user_id = $profile->getID();
         $magicsig->importKeys($keypair);
         // save the public key for this profile in our database.
         // TODO: If the profile generates a new key remotely, we must be able to replace
         //       this (of course after callback-verification).
         $magicsig->insert();
     } elseif (!$magicsig instanceof Magicsig) {
         // No discovery request, so we'll give up.
         throw new ServerException(sprintf('No public key found for profile (id==%d)', $profile->id));
     }
     assert($magicsig->publicKey instanceof Crypt_RSA);
     return $magicsig;
 }
Пример #2
0
 /**
  * Build common remote-profile options structure.
  * Currently only adds output for remote profiles, nothing for local users.
  *
  * @param HTMLOutputter $out
  * @param Profile $profile
  */
 protected function showProfileOptions(HTMLOutputter $out, Profile $profile)
 {
     if (!$profile->isGroup() && !$profile->isLocal()) {
         $target = common_local_url('userbyid', array('id' => $profile->getID()));
         // TRANS: Label for access to remote profile options.
         $label = _m('Remote profile options...');
         $out->elementStart('div', 'remote-profile-options');
         $out->element('a', array('href' => $target), $label);
         $out->elementEnd('div');
     }
 }
Пример #3
0
 function onEndUnsubscribe(Profile $profile, Profile $other)
 {
     // Only do this if config is enabled
     if (!$this->StopFollowUser) {
         return true;
     }
     if (!$profile->isLocal()) {
         return true;
     }
     // TRANS: Text for "stopped following" item in activity plugin.
     // TRANS: %1$s is a profile URL, %2$s is a profile name,
     // TRANS: %3$s is a profile URL, %4$s is a profile name.
     $rendered = sprintf(_m('<a href="%1$s">%2$s</a> stopped following <a href="%3$s">%4$s</a>.'), $profile->getUrl(), $profile->getBestName(), $other->getUrl(), $other->getBestName());
     // TRANS: Text for "stopped following" item in activity plugin.
     // TRANS: %1$s is a profile name, %2$s is a profile URL,
     // TRANS: %3$s is a profile name, %4$s is a profile URL.
     $content = sprintf(_m('%1$s (%2$s) stopped following %3$s (%4$s).'), $profile->getBestName(), $profile->getUrl(), $other->getBestName(), $other->getUrl());
     $uri = TagURI::mint('stop-following:%d:%d:%s', $profile->id, $other->id, common_date_iso8601(common_sql_now()));
     $notice = Notice::saveNew($profile->id, $content, ActivityPlugin::SOURCE, array('rendered' => $rendered, 'urls' => array(), 'replies' => array($other->getUri()), 'uri' => $uri, 'verb' => ActivityVerb::UNFOLLOW, 'object_type' => ActivityObject::PERSON));
     return true;
 }
Пример #4
0
 /**
  * Saves an attention for a profile (user or group) which means
  * it shows up in their home feed and such.
  */
 function saveAttention(Profile $target, $reason = null)
 {
     if ($target->isGroup()) {
         // FIXME: Make sure we check (for both local and remote) users are in the groups they send to!
         // legacy notification method, will still be in use for quite a while I think
         $this->addToGroupInbox($target->getGroup());
     } else {
         if ($target->hasBlocked($this->getProfile())) {
             common_log(LOG_INFO, "Not saving reply to profile {$target->id} ({$uri}) from sender {$sender->id} because of a block.");
             return false;
         }
     }
     if ($target->isLocal()) {
         // legacy notification method, will still be in use for quite a while I think
         $this->saveReply($target->getID());
     }
     $att = Attention::saveNew($this, $target, $reason);
     self::blow('reply:stream:%d', $target->getID());
     return true;
 }
 public function onEndProfilePageActionsElements(HTMLOutputter $out, Profile $profile)
 {
     $scoped = Profile::current();
     if (!$scoped instanceof Profile) {
         return true;
     }
     if ($profile->isLocal() && $scoped->mutuallySubscribed($profile)) {
         $out->elementStart('li', 'entity_send-a-message');
         $out->element('a', array('href' => common_local_url('newmessage', array('to' => $profile->id)), 'title' => _('Send a direct message to this user.')), _m('BUTTON', 'Message'));
         $out->elementEnd('li');
     }
     return true;
 }
Пример #6
0
 protected function addWebFingerPersonLinks(XML_XRD $xrd, Profile $target)
 {
     $xrd->links[] = new XML_XRD_Element_Link(Discovery::UPDATESFROM, common_local_url('ApiTimelineUser', array('id' => $target->id, 'format' => 'atom')), 'application/atom+xml');
     // Get this profile's keypair
     $magicsig = Magicsig::getKV('user_id', $target->id);
     if (!$magicsig instanceof Magicsig && $target->isLocal()) {
         $magicsig = Magicsig::generate($target->getUser());
     }
     if (!$magicsig instanceof Magicsig) {
         return false;
         // value doesn't mean anything, just figured I'd indicate this function didn't do anything
     }
     if (Event::handle('StartAttachPubkeyToUserXRD', array($magicsig, $xrd, $target))) {
         $xrd->links[] = new XML_XRD_Element_Link(Magicsig::PUBLICKEYREL, 'data:application/magic-public-key,' . $magicsig->toString());
         // The following event handles plugins like Diaspora which add their own version of the Magicsig pubkey
         Event::handle('EndAttachPubkeyToUserXRD', array($magicsig, $xrd, $target));
     }
 }
Пример #7
0
 static function saveActivity(Activity $act, Profile $actor, array $options = array())
 {
     // First check if we're going to let this Activity through from the specific actor
     if (!$actor->hasRight(Right::NEWNOTICE)) {
         common_log(LOG_WARNING, "Attempted post from user disallowed to post: " . $actor->getNickname());
         // TRANS: Client exception thrown when a user tries to post while being banned.
         throw new ClientException(_m('You are banned from posting notices on this site.'), 403);
     }
     if (common_config('throttle', 'enabled') && !self::checkEditThrottle($actor->id)) {
         common_log(LOG_WARNING, 'Excessive posting by profile #' . $actor->id . '; throttled.');
         // TRANS: Client exception thrown when a user tries to post too many notices in a given time frame.
         throw new ClientException(_m('Too many notices too fast; take a breather ' . 'and post again in a few minutes.'));
     }
     // Get ActivityObject properties
     if (!empty($act->id)) {
         // implied object
         $options['uri'] = $act->id;
         $options['url'] = $act->link;
     } else {
         $actobj = count($act->objects) == 1 ? $act->objects[0] : null;
         if (!is_null($actobj) && !empty($actobj->id)) {
             $options['uri'] = $actobj->id;
             if ($actobj->link) {
                 $options['url'] = $actobj->link;
             } elseif (preg_match('!^https?://!', $actobj->id)) {
                 $options['url'] = $actobj->id;
             }
         }
     }
     $defaults = array('groups' => array(), 'is_local' => self::LOCAL_PUBLIC, 'mentions' => array(), 'reply_to' => null, 'repeat_of' => null, 'scope' => null, 'source' => 'unknown', 'tags' => array(), 'uri' => null, 'url' => null, 'urls' => array(), 'distribute' => true);
     // options will have default values when nothing has been supplied
     $options = array_merge($defaults, $options);
     foreach (array_keys($defaults) as $key) {
         // Only convert the keynames we specify ourselves from 'defaults' array into variables
         ${$key} = $options[$key];
     }
     extract($options, EXTR_SKIP);
     $stored = new Notice();
     if (!empty($uri)) {
         $stored->uri = $uri;
         if ($stored->find()) {
             common_debug('cannot create duplicate Notice URI: ' . $stored->uri);
             throw new Exception('Notice URI already exists');
         }
     }
     $stored->profile_id = $actor->id;
     $stored->source = $source;
     $stored->uri = $uri;
     $stored->url = $url;
     $stored->verb = $act->verb;
     // Use the local user's shortening preferences, if applicable.
     $stored->rendered = $actor->isLocal() ? $actor->shortenLinks($act->content) : $act->content;
     $stored->content = common_strip_html($stored->rendered);
     $autosource = common_config('public', 'autosource');
     // Sandboxed are non-false, but not 1, either
     if (!$actor->hasRight(Right::PUBLICNOTICE) || $source && $autosource && in_array($source, $autosource)) {
         $stored->is_local = Notice::LOCAL_NONPUBLIC;
     }
     // Maybe a missing act-time should be fatal if the actor is not local?
     if (!empty($act->time)) {
         $stored->created = common_sql_date($act->time);
     } else {
         $stored->created = common_sql_now();
     }
     $reply = null;
     if ($act->context instanceof ActivityContext && !empty($act->context->replyToID)) {
         $reply = self::getKV('uri', $act->context->replyToID);
     }
     if (!$reply instanceof Notice && $act->target instanceof ActivityObject) {
         $reply = self::getKV('uri', $act->target->id);
     }
     if ($reply instanceof Notice) {
         if (!$reply->inScope($actor)) {
             // TRANS: Client error displayed when trying to reply to a notice a the target has no access to.
             // TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
             throw new ClientException(sprintf(_m('%1$s has no right to reply to notice %2$d.'), $actor->getNickname(), $reply->id), 403);
         }
         $stored->reply_to = $reply->id;
         $stored->conversation = $reply->conversation;
         // If the original is private to a group, and notice has no group specified,
         // make it to the same group(s)
         if (empty($groups) && $reply->scope & Notice::GROUP_SCOPE) {
             $groups = array();
             $replyGroups = $reply->getGroups();
             foreach ($replyGroups as $group) {
                 if ($actor->isMember($group)) {
                     $groups[] = $group->id;
                 }
             }
         }
         if (is_null($scope)) {
             $scope = $reply->scope;
         }
     }
     if ($act->context instanceof ActivityContext) {
         $location = $act->context->location;
         if ($location) {
             $stored->lat = $location->lat;
             $stored->lon = $location->lon;
             if ($location->location_id) {
                 $stored->location_ns = $location->location_ns;
                 $stored->location_id = $location->location_id;
             }
         }
     } else {
         $act->context = new ActivityContext();
     }
     $stored->scope = self::figureOutScope($actor, $groups, $scope);
     foreach ($act->categories as $cat) {
         if ($cat->term) {
             $term = common_canonical_tag($cat->term);
             if (!empty($term)) {
                 $tags[] = $term;
             }
         }
     }
     foreach ($act->enclosures as $href) {
         // @todo FIXME: Save these locally or....?
         $urls[] = $href;
     }
     if (Event::handle('StartNoticeSave', array(&$stored))) {
         // XXX: some of these functions write to the DB
         try {
             $stored->insert();
             // throws exception on error
             $orig = clone $stored;
             // for updating later in this try clause
             $object = null;
             Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
             if (empty($object)) {
                 throw new ServerException('Unsuccessful call to StoreActivityObject ' . $stored->uri . ': ' . $act->asString());
             }
             // If it's not part of a conversation, it's
             // the beginning of a new conversation.
             if (empty($stored->conversation)) {
                 // $act->context->conversation will be null if it was not provided
                 $conv = Conversation::create($stored, $act->context->conversation);
                 $stored->conversation = $conv->id;
             }
             $stored->update($orig);
         } catch (Exception $e) {
             if (empty($stored->id)) {
                 common_debug('Failed to save stored object entry in database (' . $e->getMessage() . ')');
             } else {
                 common_debug('Failed to store activity object in database (' . $e->getMessage() . '), deleting notice id ' . $stored->id);
                 $stored->delete();
             }
             throw $e;
         }
     }
     if (!$stored instanceof Notice) {
         throw new ServerException('StartNoticeSave did not give back a Notice');
     }
     // Save per-notice metadata...
     $mentions = array();
     $groups = array();
     // This event lets plugins filter out non-local recipients (attentions we don't care about)
     // Used primarily for OStatus (and if we don't federate, all attentions would be local anyway)
     Event::handle('GetLocalAttentions', array($actor, $act->context->attention, &$mentions, &$groups));
     if (!empty($mentions)) {
         $stored->saveKnownReplies($mentions);
     } else {
         $stored->saveReplies();
     }
     if (!empty($tags)) {
         $stored->saveKnownTags($tags);
     } else {
         $stored->saveTags();
     }
     // Note: groups may save tags, so must be run after tags are saved
     // to avoid errors on duplicates.
     // Note: groups should always be set.
     $stored->saveKnownGroups($groups);
     if (!empty($urls)) {
         $stored->saveKnownUrls($urls);
     } else {
         $stored->saveUrls();
     }
     if ($distribute) {
         // Prepare inbox delivery, may be queued to background.
         $stored->distribute();
     }
     return $stored;
 }