X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FNotice.php;h=563efaf4e53bfbc219251082bb42da2e0eb23c09;hb=c6f89b21748948fde902904f6ac9ce9afa026b84;hp=8162dddc85baa049966918797577f41ffc414195;hpb=5bea746fa81d70335717f592fadb80f3892f7d73;p=quix0rs-gnu-social.git diff --git a/classes/Notice.php b/classes/Notice.php index 8162dddc85..563efaf4e5 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -84,7 +84,7 @@ class Notice extends Managed_DataObject 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'), 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'who made the update'), 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'), - 'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8_general_ci'), + 'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8mb4_general_ci'), 'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'), 'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), @@ -313,16 +313,6 @@ class Notice extends Managed_DataObject return $notice; } - public static function getById($id) - { - $notice = new Notice(); - $notice->id = $id; - if (!$notice->find(true)) { - throw new NoResultException($notice); - } - return $notice; - } - /** * Extract #hashtags from this notice's content and save them to the database. */ @@ -342,7 +332,7 @@ class Notice extends Managed_DataObject * Record the given set of hash tags in the db for this notice. * Given tag strings will be normalized and checked for dupes. */ - function saveKnownTags($hashtags) + function saveKnownTags(array $hashtags) { //turn each into their canonical tag //this is needed to remove dupes before saving e.g. #hash.tag = #hashtag @@ -426,7 +416,7 @@ class Notice extends Managed_DataObject * @return Notice * @throws ClientException */ - static function saveNew($profile_id, $content, $source, array $options=null) { + static function saveNew($profile_id, $content, $source, array $options=array()) { $defaults = array('uri' => null, 'url' => null, 'conversation' => null, // URI of conversation @@ -437,13 +427,16 @@ class Notice extends Managed_DataObject 'object_type' => null, 'verb' => null); - if (!empty($options) && is_array($options)) { + /* + * Above type-hint is already array, so simply count it, this saves + * "some" CPU cycles. + */ + if (count($options) > 0) { $options = array_merge($defaults, $options); - extract($options); - } else { - extract($defaults); } + extract($options); + if (!isset($is_local)) { $is_local = Notice::LOCAL_PUBLIC; } @@ -549,8 +542,7 @@ class Notice extends Managed_DataObject throw new ClientException(_('You cannot repeat your own notice.')); } - if ($repeat->scope != Notice::SITE_SCOPE && - $repeat->scope != Notice::PUBLIC_SCOPE) { + if ($repeat->isPrivateScope()) { // TRANS: Client error displayed when trying to repeat a non-public notice. throw new ClientException(_('Cannot repeat a private notice.'), 403); } @@ -902,6 +894,12 @@ class Notice extends Managed_DataObject $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)) { @@ -910,12 +908,6 @@ class Notice extends Managed_DataObject $stored->conversation = $conv->id; } - $object = null; - Event::handle('StoreActivityObject', array($act, $stored, $options, &$object)); - if (empty($object)) { - throw new ServerException('No object from StoreActivityObject '.$stored->uri . ': '.$act->asString()); - } - $stored->object_type = ActivityUtils::resolveUri($object->getObjectType(), true); $stored->update($orig); } catch (Exception $e) { if (empty($stored->id)) { @@ -989,12 +981,14 @@ class Notice extends Managed_DataObject // Force the scope for private groups foreach ($groups as $group_id) { - $group = User_group::staticGet('id', $group_id); - if ($group instanceof User_group) { + try { + $group = User_group::getByID($group_id); if ($group->force_scope) { $scope |= Notice::GROUP_SCOPE; break; } + } catch (Exception $e) { + common_log(LOG_ERR, 'Notice figureOutScope threw exception: '.$e->getMessage()); } } @@ -1109,7 +1103,7 @@ class Notice extends Managed_DataObject */ function saveUrls() { if (common_config('attachments', 'process_links')) { - common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id); + common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this); } } @@ -1121,16 +1115,12 @@ class Notice extends Managed_DataObject * * @return void */ - function saveKnownUrls($urls) + function saveKnownUrls(array $urls) { if (common_config('attachments', 'process_links')) { // @fixme validation? foreach (array_unique($urls) as $url) { - try { - File::processNew($url, $this->id); - } catch (ServerException $e) { - // Could not save URL. Log it? - } + $this->saveUrl($url, $this); } } } @@ -1138,9 +1128,9 @@ class Notice extends Managed_DataObject /** * @private callback */ - function saveUrl($url, $notice_id) { + function saveUrl($url, Notice $notice) { try { - File::processNew($url, $notice_id); + File::processNew($url, $notice); } catch (ServerException $e) { // Could not save URL. Log it? } @@ -1311,7 +1301,7 @@ class Notice extends Managed_DataObject $last = $parent; continue; } - } catch (Exception $e) { + } catch (NoParentNoticeException $e) { // Latest notice has no parent } // No parent, or parent out of scope @@ -1617,7 +1607,7 @@ class Notice extends Managed_DataObject $this->saveReply($parentauthor->id); $replied[$parentauthor->id] = 1; self::blow('reply:stream:%d', $parentauthor->id); - } catch (Exception $e) { + } catch (NoParentNoticeException $e) { // Not a reply, since it has no parent! } @@ -1634,8 +1624,7 @@ class Notice extends Managed_DataObject foreach ($mention['mentioned'] as $mentioned) { // skip if they're already covered - - if (!empty($replied[$mentioned->id])) { + if (array_key_exists($mentioned->id, $replied)) { continue; } @@ -1691,12 +1680,12 @@ class Notice extends Managed_DataObject $ids[] = $reply->profile_id; } - $this->_replies[$this->id] = $ids; + $this->_setReplies($ids); return $ids; } - function _setReplies($replies) + function _setReplies(array $replies) { $this->_replies[$this->id] = $replies; } @@ -1773,11 +1762,11 @@ class Notice extends Managed_DataObject } $groups = User_group::multiGet('id', $ids); - $this->_groups[$this->id] = $groups->fetchAll(); + $this->_setGroups($groups->fetchAll()); return $this->_groups[$this->id]; } - function _setGroups($groups) + function _setGroups(array $groups) { $this->_groups[$this->id] = $groups; } @@ -1841,9 +1830,9 @@ class Notice extends Managed_DataObject $attachments = $this->attachments(); foreach ($attachments as $attachment) { - // Save local attachments + // Include local attachments in Activity if (!empty($attachment->filename)) { - $act->attachments[] = ActivityObject::fromFile($attachment); + $act->enclosures[] = $attachment->getEnclosure(); } } @@ -1852,8 +1841,8 @@ class Notice extends Managed_DataObject try { $reply = $this->getParent(); $ctx->replyToID = $reply->getUri(); - $ctx->replyToUrl = $reply->getUrl(); - } catch (Exception $e) { + $ctx->replyToUrl = $reply->getUrl(true); // true for fallback to local URL, less messy + } catch (NoParentNoticeException $e) { // This is not a reply to something } @@ -2492,6 +2481,37 @@ class Notice extends Managed_DataObject */ public function getTags() { + // Check default scope (non-private notices) + $inScope = (!$this->isPrivateScope()); + + // Get current profile + $profile = Profile::current(); + + // Is the general scope check okay and the user in logged in? + //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: inScope=' . intval($inScope) . ',profile[]=' . gettype($profile)); + if (($inScope === TRUE) && ($profile instanceof Profile)) { + /* + * Check scope, else a privacy leaks happens this way: + * + * 1) Bob and Alice follow each other and write private notices + * (this->scope=2) to each other. + * 2) Bob uses tags in his private notice to alice (which she can + * read from him). + * 3) Alice adds that notice (with tags) to her favorites + * ("faving") it. + * 4) The tags from Bob's private notice becomes visible in Alice's + * profile. + * + * This has the simple background that the scope is not being + * re-checked. This has to be done here at this point because given + * above scenario is a privacy leak as the tags may be *really* + * private (nobody else shall see them) such as initmate words or + * very political words. + */ + $inScope = $this->inScope($profile); + //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: inScope=' . intval($inScope) . ' - After inScope() has been called.'); + } + $tags = array(); $keypart = sprintf('notice:tags:%d', $this->id); @@ -2503,7 +2523,9 @@ class Notice extends Managed_DataObject } else { $tag = new Notice_tag(); $tag->notice_id = $this->id; - if ($tag->find()) { + + // Check scope for privacy-leak protection (see some lines above why) + if (($inScope === TRUE) && ($tag->find())) { while ($tag->fetch()) { $tags[] = $tag->tag; } @@ -2631,6 +2653,11 @@ class Notice extends Managed_DataObject ($this->is_local != Notice::GATEWAY)); } + public function isPrivateScope () { + return ($this->scope != Notice::SITE_SCOPE && + $this->scope != Notice::PUBLIC_SCOPE); + } + /** * Check that the given profile is allowed to read, respond to, or otherwise * act on this notice. @@ -2645,7 +2672,7 @@ class Notice extends Managed_DataObject * * @return boolean whether the profile is in the notice's scope */ - function inScope($profile) + function inScope(Profile $profile=null) { if (is_null($profile)) { $keypart = sprintf('notice:in-scope-for:%d:null', $this->id); @@ -2668,7 +2695,7 @@ class Notice extends Managed_DataObject return ($result == 1) ? true : false; } - protected function _inScope($profile) + protected function _inScope(Profile $profile=null) { if (!is_null($this->scope)) { $scope = $this->scope; @@ -2737,7 +2764,7 @@ class Notice extends Managed_DataObject return !$this->isHiddenSpam($profile); } - function isHiddenSpam($profile) { + function isHiddenSpam(Profile $profile=null) { // Hide posts by silenced users from everyone but moderators. @@ -2763,13 +2790,10 @@ class Notice extends Managed_DataObject public function getParent() { - $parent = Notice::getKV('id', $this->reply_to); - - if (!$parent instanceof Notice) { - throw new ServerException('Notice has no parent'); + if (empty($this->reply_to)) { + throw new NoParentNoticeException($this); } - - return $parent; + return self::getByID($this->reply_to); } /** @@ -2802,7 +2826,7 @@ class Notice extends Managed_DataObject return $scope; } - static function fillProfiles($notices) + static function fillProfiles(array $notices) { $map = self::getProfiles($notices); foreach ($notices as $entry=>$notice) { @@ -2819,7 +2843,7 @@ class Notice extends Managed_DataObject return array_values($map); } - static function getProfiles(&$notices) + static function getProfiles(array &$notices) { $ids = array(); foreach ($notices as $notice) { @@ -2829,7 +2853,7 @@ class Notice extends Managed_DataObject return Profile::pivotGet('id', $ids); } - static function fillGroups(&$notices) + static function fillGroups(array &$notices) { $ids = self::_idsOf($notices); $gis = Group_inbox::listGet('notice_id', $ids); @@ -2864,7 +2888,7 @@ class Notice extends Managed_DataObject return array_keys($ids); } - static function fillAttachments(&$notices) + static function fillAttachments(array &$notices) { $ids = self::_idsOf($notices); $f2pMap = File_to_post::listGet('post_id', $ids); @@ -2888,7 +2912,7 @@ class Notice extends Managed_DataObject } } - static function fillReplies(&$notices) + static function fillReplies(array &$notices) { $ids = self::_idsOf($notices); $replyMap = Reply::listGet('notice_id', $ids); @@ -2902,30 +2926,36 @@ class Notice extends Managed_DataObject } } - protected $_repeats = array(); + /** + * Checks whether the current profile is allowed (in scope) to see this notice. + * + * @return $inScope Whether the current profile is allowed to see this notice + */ + function isCurrentProfileInScope () { + // Check scope, default is allowed + $inScope = TRUE; - function getRepeats() - { - if (isset($this->_repeats[$this->id])) { - return $this->_repeats[$this->id]; - } - $repeatMap = Notice::listGet('repeat_of', array($this->id)); - $this->_repeats[$this->id] = $repeatMap[$this->id]; - return $this->_repeats[$this->id]; - } + //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . '] this->tag=' . $this->tag . ',this->id=' . $this->id . ',this->scope=' . $this->scope); - function _setRepeats($repeats) - { - $this->_repeats[$this->id] = $repeats; - } + // Is it private scope? + if ($this->isPrivateScope()) { + // 2) Get current profile + $profile = Profile::current(); - static function fillRepeats(&$notices) - { - $ids = self::_idsOf($notices); - $repeatMap = Notice::listGet('repeat_of', $ids); - foreach ($notices as $notice) { - $repeats = $repeatMap[$notice->id]; - $notice->_setRepeats($repeats); + // Is the profile not set? + if (!$profile instanceof Profile) { + // Public viewer shall not see a tag from a private dent (privacy leak) + //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . '] Not logged in (public view).'); + $inScope = FALSE; + } elseif (!$this->inScope($profile)) { + // Current profile is not in scope (not allowed to see) of notice + //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . '] profile->id=' . $profile->id . ' is not allowed to see this notice.'); + $inScope = FALSE; + } } + + // Return result + //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . '] this->tag=' . $this->tag . ',this->weight=' . $this->weight . ',inScope=' . intval($inScope) . ' - EXIT!'); + return $inScope; } }