3 * StatusNet, the distributed open-source microblogging tool
5 * Plugin to prevent use of nicknames or URLs on a blacklist
9 * LICENCE: This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * @author Evan Prodromou <evan@status.net>
25 * @copyright 2010 StatusNet Inc.
26 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27 * @link http://status.net/
30 if (!defined('STATUSNET')) {
35 * Plugin to prevent use of nicknames or URLs on a blacklist
39 * @author Evan Prodromou <evan@status.net>
40 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
41 * @link http://status.net/
44 class BlacklistPlugin extends Plugin
46 const VERSION = STATUSNET_VERSION;
48 public $nicknames = array();
49 public $urls = array();
50 public $canAdmin = true;
52 private $_nicknamePatterns = array();
53 private $_urlPatterns = array();
56 * Initialize the plugin
63 $confNicknames = $this->_configArray('blacklist', 'nicknames');
65 $dbNicknames = Nickname_blacklist::getPatterns();
67 $this->_nicknamePatterns = array_merge($this->nicknames,
71 $confURLs = $this->_configArray('blacklist', 'urls');
73 $dbURLs = Homepage_blacklist::getPatterns();
75 $this->_urlPatterns = array_merge($this->urls,
81 * Database schema setup
83 * @return boolean hook value
86 function onCheckSchema()
88 $schema = Schema::get();
90 // For storing blacklist patterns for nicknames
92 $schema->ensureTable('nickname_blacklist',
93 array(new ColumnDef('pattern',
98 new ColumnDef('created',
103 $schema->ensureTable('homepage_blacklist',
104 array(new ColumnDef('pattern',
109 new ColumnDef('created',
118 * Retrieve an array from configuration
120 * Carefully checks a section.
122 * @param string $section Configuration section
123 * @param string $setting Configuration setting
125 * @return array configuration values
128 function _configArray($section, $setting)
130 $config = common_config($section, $setting);
132 if (empty($config)) {
134 } else if (is_array($config)) {
136 } else if (is_string($config)) {
137 return explode("\r\n", $config);
139 throw new Exception("Unknown data type for config $section + $setting");
144 * Hook registration to prevent blacklisted homepages or nicknames
146 * Throws an exception if there's a blacklisted homepage or nickname.
148 * @param Action $action Action being called (usually register)
150 * @return boolean hook value
153 function onStartRegistrationTry($action)
155 $homepage = strtolower($action->trimmed('homepage'));
157 if (!empty($homepage)) {
158 if (!$this->_checkUrl($homepage)) {
159 $msg = sprintf(_m("You may not register with homepage '%s'"),
161 throw new ClientException($msg);
165 $nickname = strtolower($action->trimmed('nickname'));
167 if (!empty($nickname)) {
168 if (!$this->_checkNickname($nickname)) {
169 $msg = sprintf(_m("You may not register with nickname '%s'"),
171 throw new ClientException($msg);
179 * Hook profile update to prevent blacklisted homepages or nicknames
181 * Throws an exception if there's a blacklisted homepage or nickname.
183 * @param Action $action Action being called (usually register)
185 * @return boolean hook value
188 function onStartProfileSaveForm($action)
190 $homepage = strtolower($action->trimmed('homepage'));
192 if (!empty($homepage)) {
193 if (!$this->_checkUrl($homepage)) {
194 $msg = sprintf(_m("You may not use homepage '%s'"),
196 throw new ClientException($msg);
200 $nickname = strtolower($action->trimmed('nickname'));
202 if (!empty($nickname)) {
203 if (!$this->_checkNickname($nickname)) {
204 $msg = sprintf(_m("You may not use nickname '%s'"),
206 throw new ClientException($msg);
214 * Hook notice save to prevent blacklisted urls
216 * Throws an exception if there's a blacklisted url in the content.
218 * @param Notice &$notice Notice being saved
220 * @return boolean hook value
223 function onStartNoticeSave(&$notice)
225 common_replace_urls_callback($notice->content,
226 array($this, 'checkNoticeUrl'));
231 * Helper callback for notice save
233 * Throws an exception if there's a blacklisted url in the content.
235 * @param string $url URL in the notice content
237 * @return boolean hook value
240 function checkNoticeUrl($url)
242 // It comes in special'd, so we unspecial it
243 // before comparing against patterns
245 $url = htmlspecialchars_decode($url);
247 if (!$this->_checkUrl($url)) {
248 $msg = sprintf(_m("You may not use url '%s' in notices"),
250 throw new ClientException($msg);
257 * Helper for checking URLs
259 * Checks an URL against our patterns for a match.
261 * @param string $url URL to check
263 * @return boolean true means it's OK, false means it's bad
266 private function _checkUrl($url)
268 foreach ($this->_urlPatterns as $pattern) {
269 common_debug("Checking $url against $pattern");
270 if (preg_match("/$pattern/", $url)) {
279 * Helper for checking nicknames
281 * Checks a nickname against our patterns for a match.
283 * @param string $nickname nickname to check
285 * @return boolean true means it's OK, false means it's bad
288 private function _checkNickname($nickname)
290 foreach ($this->_nicknamePatterns as $pattern) {
291 common_debug("Checking $nickname against $pattern");
292 if (preg_match("/$pattern/", $nickname)) {
301 * Add our actions to the URL router
303 * @param Net_URL_Mapper $m URL mapper for this hit
305 * @return boolean hook return
308 function onRouterInitialized($m)
310 $m->connect('admin/blacklist', array('action' => 'blacklistadminpanel'));
315 * Auto-load our classes if called
317 * @param string $cls Class to load
319 * @return boolean hook return
322 function onAutoload($cls)
324 switch (strtolower($cls))
326 case 'nickname_blacklist':
327 case 'homepage_blacklist':
328 include_once INSTALLDIR.'/plugins/Blacklist/'.ucfirst($cls).'.php';
330 case 'blacklistadminpanelaction':
331 $base = strtolower(mb_substr($cls, 0, -6));
332 include_once INSTALLDIR.'/plugins/Blacklist/'.$base.'.php';
340 * Plugin version data
342 * @param array &$versions array of version blocks
344 * @return boolean hook value
347 function onPluginVersion(&$versions)
349 $versions[] = array('name' => 'Blacklist',
350 'version' => self::VERSION,
351 'author' => 'Evan Prodromou',
353 'http://status.net/wiki/Plugin:Blacklist',
355 _m('Keep a blacklist of forbidden nickname '.
356 'and URL patterns.'));
361 * Determines if our admin panel can be shown
363 * @param string $name name of the admin panel
364 * @param boolean &$isOK result
366 * @return boolean hook value
369 function onAdminPanelCheck($name, &$isOK)
371 if ($name == 'blacklist') {
372 $isOK = $this->canAdmin;
380 * Add our tab to the admin panel
382 * @param Widget $nav Admin panel nav
384 * @return boolean hook value
387 function onEndAdminPanelNav($nav)
389 if (AdminPanelAction::canAdmin('blacklist')) {
391 $action_name = $nav->action->trimmed('action');
393 $nav->out->menuItem(common_local_url('blacklistadminpanel'),
395 _('Blacklist configuration'),
396 $action_name == 'blacklistadminpanel',
397 'nav_blacklist_admin_panel');
403 function onEndDeleteUserForm($action, $user)
405 $cur = common_current_user();
407 if (empty($cur) || !$cur->hasRight(Right::CONFIGURESITE)) {
411 $profile = $user->getProfile();
413 if (empty($profile)) {
417 $action->elementStart('ul', 'form_data');
418 $action->elementStart('li');
419 $this->checkboxAndText($action,
421 _('Add this nickname pattern to blacklist'),
422 'blacklistnicknamepattern',
423 $this->patternizeNickname($user->nickname));
424 $action->elementEnd('li');
426 if (!empty($profile->homepage)) {
427 $action->elementStart('li');
428 $this->checkboxAndText($action,
430 _('Add this homepage pattern to blacklist'),
431 'blacklisthomepagepattern',
432 $this->patternizeHomepage($profile->homepage));
433 $action->elementEnd('li');
436 $action->elementEnd('ul');
439 function onEndDeleteUser($action, $user)
441 if ($action->boolean('blacklisthomepage')) {
442 $pattern = $action->trimmed('blacklisthomepagepattern');
443 Homepage_blacklist::ensurePattern($pattern);
446 if ($action->boolean('blacklistnickname')) {
447 $pattern = $action->trimmed('blacklistnicknamepattern');
448 Nickname_blacklist::ensurePattern($pattern);
454 function checkboxAndText($action, $checkID, $label, $textID, $value)
456 $action->element('input', array('name' => $checkID,
457 'type' => 'checkbox',
458 'class' => 'checkbox',
463 $action->element('label', array('class' => 'checkbox',
469 $action->element('input', array('name' => $textID,
475 function patternizeNickname($nickname)
480 function patternizeHomepage($homepage)
482 $hostname = parse_url($homepage, PHP_URL_HOST);