123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- <?php
- /**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Plugin to prevent use of nicknames or URLs on a blacklist
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * @category Action
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- */
- if (!defined('GNUSOCIAL')) { exit(1); }
- /**
- * Plugin to prevent use of nicknames or URLs on a blacklist
- *
- * @category Plugin
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- */
- class BlacklistPlugin extends Plugin
- {
- const PLUGIN_VERSION = '2.0.0';
- public $nicknames = array();
- public $urls = array();
- public $canAdmin = true;
- function _getNicknamePatterns()
- {
- $confNicknames = $this->_configArray('blacklist', 'nicknames');
- $dbNicknames = Nickname_blacklist::getPatterns();
- return array_merge($this->nicknames,
- $confNicknames,
- $dbNicknames);
- }
- function _getUrlPatterns()
- {
- $confURLs = $this->_configArray('blacklist', 'urls');
- $dbURLs = Homepage_blacklist::getPatterns();
- return array_merge($this->urls,
- $confURLs,
- $dbURLs);
- }
- /**
- * Database schema setup
- *
- * @return boolean hook value
- */
- function onCheckSchema()
- {
- $schema = Schema::get();
- // For storing blacklist patterns for nicknames
- $schema->ensureTable('nickname_blacklist', Nickname_blacklist::schemaDef());
- $schema->ensureTable('homepage_blacklist', Homepage_blacklist::schemaDef());
- return true;
- }
- /**
- * Retrieve an array from configuration
- *
- * Carefully checks a section.
- *
- * @param string $section Configuration section
- * @param string $setting Configuration setting
- *
- * @return array configuration values
- */
- function _configArray($section, $setting)
- {
- $config = common_config($section, $setting);
- if (empty($config)) {
- return array();
- } else if (is_array($config)) {
- return $config;
- } else if (is_string($config)) {
- return explode("\r\n", $config);
- } else {
- // TRANS: Exception thrown if the Blacklist plugin configuration is incorrect.
- // TRANS: %1$s is a configuration section, %2$s is a configuration setting.
- throw new Exception(sprintf(_m('Unknown data type for config %1$s + %2$s.'),$section, $setting));
- }
- }
- /**
- * Hook profile update to prevent blacklisted homepages or nicknames
- *
- * Throws an exception if there's a blacklisted homepage or nickname.
- *
- * @param ManagedAction $action Action being called (usually register)
- *
- * @return boolean hook value
- */
- function onStartProfileSaveForm(ManagedAction $action)
- {
- $homepage = strtolower($action->trimmed('homepage'));
- if (!empty($homepage)) {
- if (!$this->_checkUrl($homepage)) {
- // TRANS: Validation failure for URL. %s is the URL.
- $msg = sprintf(_m("You may not use homepage \"%s\"."),
- $homepage);
- throw new ClientException($msg);
- }
- }
- $nickname = strtolower($action->trimmed('nickname'));
- if (!empty($nickname)) {
- if (!$this->_checkNickname($nickname)) {
- // TRANS: Validation failure for nickname. %s is the nickname.
- $msg = sprintf(_m("You may not use nickname \"%s\"."),
- $nickname);
- throw new ClientException($msg);
- }
- }
- return true;
- }
- /**
- * Hook notice save to prevent blacklisted urls
- *
- * Throws an exception if there's a blacklisted url in the content.
- *
- * @param Notice &$notice Notice being saved
- *
- * @return boolean hook value
- */
- public function onStartNoticeSave(&$notice)
- {
- common_replace_urls_callback($notice->content,
- array($this, 'checkNoticeUrl'));
- return true;
- }
- /**
- * Helper callback for notice save
- *
- * Throws an exception if there's a blacklisted url in the content.
- *
- * @param string $url URL in the notice content
- *
- * @return boolean hook value
- */
- function checkNoticeUrl($url)
- {
- // It comes in special'd, so we unspecial it
- // before comparing against patterns
- $url = htmlspecialchars_decode($url);
- if (!$this->_checkUrl($url)) {
- // TRANS: Validation failure for URL. %s is the URL.
- $msg = sprintf(_m("You may not use URL \"%s\" in notices."),
- $url);
- throw new ClientException($msg);
- }
- return $url;
- }
- /**
- * Helper for checking URLs
- *
- * Checks an URL against our patterns for a match.
- *
- * @param string $url URL to check
- *
- * @return boolean true means it's OK, false means it's bad
- */
- private function _checkUrl($url)
- {
- $patterns = $this->_getUrlPatterns();
- foreach ($patterns as $pattern) {
- if ($pattern != '' && preg_match("/$pattern/", $url)) {
- return false;
- }
- }
- return true;
- }
- public function onUrlBlacklistTest($url)
- {
- common_debug('Checking URL against blacklist: '._ve($url));
- if (!$this->_checkUrl($url)) {
- throw new ClientException('Forbidden URL', 403);
- }
- return true;
- }
- /**
- * Helper for checking nicknames
- *
- * Checks a nickname against our patterns for a match.
- *
- * @param string $nickname nickname to check
- *
- * @return boolean true means it's OK, false means it's bad
- */
- private function _checkNickname($nickname)
- {
- $patterns = $this->_getNicknamePatterns();
- foreach ($patterns as $pattern) {
- if ($pattern != '' && preg_match("/$pattern/", $nickname)) {
- return false;
- }
- }
- return true;
- }
- /**
- * Add our actions to the URL router
- *
- * @param URLMapper $m URL mapper for this hit
- *
- * @return boolean hook return
- */
- public function onRouterInitialized(URLMapper $m)
- {
- $m->connect('panel/blacklist',
- ['action' => 'blacklistadminpanel']);
- return true;
- }
- /**
- * Plugin version data
- *
- * @param array &$versions array of version blocks
- *
- * @return boolean hook value
- */
- public function onPluginVersion(array &$versions): bool
- {
- $versions[] = array('name' => 'Blacklist',
- 'version' => self::PLUGIN_VERSION,
- 'author' => 'Evan Prodromou',
- 'homepage' =>
- GNUSOCIAL_ENGINE_REPO_URL . 'tree/master/plugins/Blacklist',
- 'description' =>
- // TRANS: Plugin description.
- _m('Keeps a blacklist of forbidden nickname '.
- 'and URL patterns.'));
- return true;
- }
- /**
- * Determines if our admin panel can be shown
- *
- * @param string $name name of the admin panel
- * @param boolean &$isOK result
- *
- * @return boolean hook value
- */
- function onAdminPanelCheck($name, &$isOK)
- {
- if ($name == 'blacklist') {
- $isOK = $this->canAdmin;
- return false;
- }
- return true;
- }
- /**
- * Add our tab to the admin panel
- *
- * @param Widget $nav Admin panel nav
- *
- * @return boolean hook value
- */
- function onEndAdminPanelNav(Menu $nav)
- {
- if (AdminPanelAction::canAdmin('blacklist')) {
- $action_name = $nav->action->trimmed('action');
- $nav->out->menuItem(common_local_url('blacklistadminpanel'),
- // TRANS: Menu item in admin panel.
- _m('MENU','Blacklist'),
- // TRANS: Tooltip for menu item in admin panel.
- _m('TOOLTIP','Blacklist configuration.'),
- $action_name == 'blacklistadminpanel',
- 'nav_blacklist_admin_panel');
- }
- return true;
- }
- function onEndDeleteUserForm(HTMLOutputter $out, User $user)
- {
- $scoped = $out->getScoped();
- if ($scoped === null || !$scoped->hasRight(Right::CONFIGURESITE)) {
- return true;
- }
- try {
- $profile = $user->getProfile();
- } catch (UserNoProfileException $e) {
- return true;
- }
- $out->elementStart('ul', 'form_data');
- $out->elementStart('li');
- $this->checkboxAndText($out,
- 'blacklistnickname',
- // TRANS: Checkbox label in the blacklist user form.
- _m('Add this nickname pattern to blacklist'),
- 'blacklistnicknamepattern',
- $this->patternizeNickname($profile->getNickname()));
- $out->elementEnd('li');
- if (!empty($profile->getHomepage())) {
- $out->elementStart('li');
- $this->checkboxAndText($out,
- 'blacklisthomepage',
- // TRANS: Checkbox label in the blacklist user form.
- _m('Add this homepage pattern to blacklist'),
- 'blacklisthomepagepattern',
- $this->patternizeHomepage($profile->getHomepage()));
- $out->elementEnd('li');
- }
- $out->elementEnd('ul');
- }
- function onEndDeleteUser(HTMLOutputter $out, User $user)
- {
- if ($out->boolean('blacklisthomepage')) {
- $pattern = $out->trimmed('blacklisthomepagepattern');
- Homepage_blacklist::ensurePattern($pattern);
- }
- if ($out->boolean('blacklistnickname')) {
- $pattern = $out->trimmed('blacklistnicknamepattern');
- Nickname_blacklist::ensurePattern($pattern);
- }
- return true;
- }
- function checkboxAndText(HTMLOutputter $out, $checkID, $label, $textID, $value)
- {
- $out->element('input', array('name' => $checkID,
- 'type' => 'checkbox',
- 'class' => 'checkbox',
- 'id' => $checkID));
- $out->text(' ');
- $out->element('label', array('class' => 'checkbox',
- 'for' => $checkID),
- $label);
- $out->text(' ');
- $out->element('input', array('name' => $textID,
- 'type' => 'text',
- 'id' => $textID,
- 'value' => $value));
- }
- function patternizeNickname($nickname)
- {
- return $nickname;
- }
- function patternizeHomepage($homepage)
- {
- $hostname = parse_url($homepage, PHP_URL_HOST);
- return $hostname;
- }
- function onStartHandleFeedEntry($activity)
- {
- return $this->_checkActivity($activity);
- }
- function onStartHandleSalmon($activity)
- {
- return $this->_checkActivity($activity);
- }
- function _checkActivity($activity)
- {
- $actor = $activity->actor;
- if (empty($actor)) {
- return true;
- }
- $homepage = strtolower($actor->link);
- if (!empty($homepage)) {
- if (!$this->_checkUrl($homepage)) {
- // TRANS: Exception thrown trying to post a notice while having set a blocked homepage URL. %s is the blocked URL.
- $msg = sprintf(_m("Users from \"%s\" are blocked."),
- $homepage);
- throw new ClientException($msg);
- }
- }
- if (!empty($actor->poco)) {
- $nickname = strtolower($actor->poco->preferredUsername);
- if (!empty($nickname)) {
- if (!$this->_checkNickname($nickname)) {
- // TRANS: Exception thrown trying to post a notice while having a blocked nickname. %s is the blocked nickname.
- $msg = sprintf(_m("Notices from nickname \"%s\" are disallowed."),
- $nickname);
- throw new ClientException($msg);
- }
- }
- }
- return true;
- }
- /**
- * Check URLs and homepages for blacklisted users.
- */
- function onStartSubscribe(Profile $subscriber, Profile $other)
- {
- foreach ([$other->getUrl(), $other->getHomepage()] as $url) {
- if (empty($url)) {
- continue;
- }
- $url = strtolower($url);
- if (!$this->_checkUrl($url)) {
- // TRANS: Client exception thrown trying to subscribe to a person with a blocked homepage or site URL. %s is the blocked URL.
- $msg = sprintf(_m("Users from \"%s\" are blocked."),
- $url);
- throw new ClientException($msg);
- }
- }
- $nickname = $other->getNickname();
- if (!empty($nickname)) {
- if (!$this->_checkNickname($nickname)) {
- // TRANS: Client exception thrown trying to subscribe to a person with a blocked nickname. %s is the blocked nickname.
- $msg = sprintf(_m("Cannot subscribe to nickname \"%s\"."),
- $nickname);
- throw new ClientException($msg);
- }
- }
- return true;
- }
- }
|