diff options
-rw-r--r-- | htaccess.sample | 9 | ||||
-rw-r--r-- | lib/router.php | 29 | ||||
-rw-r--r-- | plugins/Blacklist/BlacklistPlugin.php | 61 | ||||
-rw-r--r-- | plugins/Blacklist/Homepage_blacklist.php | 189 | ||||
-rw-r--r-- | plugins/Blacklist/Nickname_blacklist.php | 180 | ||||
-rw-r--r-- | plugins/Blacklist/blacklistadminpanel.php | 40 | ||||
-rw-r--r-- | plugins/OpenID/OpenIDPlugin.php | 161 |
7 files changed, 623 insertions, 46 deletions
diff --git a/htaccess.sample b/htaccess.sample index 18a868698..fa09b30f6 100644 --- a/htaccess.sample +++ b/htaccess.sample @@ -1,12 +1,17 @@ <IfModule mod_rewrite.c> RewriteEngine On - # NOTE: change this to your actual StatusNet path; may be "/". - + # NOTE: change this to your actual StatusNet base URL path, + # minus the domain part: + # + # http://example.com/ => / + # http://example.com/mublog/ => /mublog/ + # RewriteBase /mublog/ ## Uncomment these if having trouble with API authentication ## when PHP is running in CGI or FastCGI mode. + # #RewriteCond %{HTTP:Authorization} ^(.*) #RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1] diff --git a/lib/router.php b/lib/router.php index a48ee875e..a9d07276f 100644 --- a/lib/router.php +++ b/lib/router.php @@ -33,6 +33,33 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { require_once 'Net/URL/Mapper.php'; +class StatusNet_URL_Mapper extends Net_URL_Mapper { + + private static $_singleton = null; + + private function __construct() + { + } + + public static function getInstance($id = '__default__') + { + if (empty(self::$_singleton)) { + self::$_singleton = new StatusNet_URL_Mapper(); + } + return self::$_singleton; + } + + public function connect($path, $defaults = array(), $rules = array()) + { + $result = null; + if (Event::handle('StartConnectPath', array(&$path, &$defaults, &$rules, &$result))) { + $result = parent::connect($path, $defaults, $rules); + Event::handle('EndConnectPath', array($path, $defaults, $rules, $result)); + } + return $result; + } +} + /** * URL Router * @@ -69,7 +96,7 @@ class Router function initialize() { - $m = Net_URL_Mapper::getInstance(); + $m = StatusNet_URL_Mapper::getInstance(); if (Event::handle('StartInitializeRouter', array(&$m))) { diff --git a/plugins/Blacklist/BlacklistPlugin.php b/plugins/Blacklist/BlacklistPlugin.php index fb8f7306f..a7d0942da 100644 --- a/plugins/Blacklist/BlacklistPlugin.php +++ b/plugins/Blacklist/BlacklistPlugin.php @@ -62,13 +62,56 @@ class BlacklistPlugin extends Plugin { $confNicknames = $this->_configArray('blacklist', 'nicknames'); + $dbNicknames = Nickname_blacklist::getPatterns(); + $this->_nicknamePatterns = array_merge($this->nicknames, - $confNicknames); + $confNicknames, + $dbNicknames); $confURLs = $this->_configArray('blacklist', 'urls'); + $dbURLs = Homepage_blacklist::getPatterns(); + $this->_urlPatterns = array_merge($this->urls, - $confURLs); + $confURLs, + $dbURLs); + } + + /** + * Database schema setup + * + * @return boolean hook value + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing blacklist patterns for nicknames + + $schema->ensureTable('nickname_blacklist', + array(new ColumnDef('pattern', + 'varchar', + 255, + false, + 'PRI'), + new ColumnDef('created', + 'datetime', + null, + false))); + + $schema->ensureTable('homepage_blacklist', + array(new ColumnDef('pattern', + 'varchar', + 255, + false, + 'PRI'), + new ColumnDef('created', + 'datetime', + null, + false))); + + return true; } /** @@ -280,6 +323,10 @@ class BlacklistPlugin extends Plugin { switch (strtolower($cls)) { + case 'nickname_blacklist': + case 'homepage_blacklist': + include_once INSTALLDIR.'/plugins/Blacklist/'.ucfirst($cls).'.php'; + return false; case 'blacklistadminpanelaction': $base = strtolower(mb_substr($cls, 0, -6)); include_once INSTALLDIR.'/plugins/Blacklist/'.$base.'.php'; @@ -391,20 +438,14 @@ class BlacklistPlugin extends Plugin function onEndDeleteUser($action, $user) { - common_debug("Action args: " . print_r($action->args, true)); - if ($action->boolean('blacklisthomepage')) { $pattern = $action->trimmed('blacklisthomepagepattern'); - $confURLs = $this->_configArray('blacklist', 'urls'); - $confURLs[] = $pattern; - Config::save('blacklist', 'urls', implode("\r\n", $confURLs)); + Homepage_blacklist::ensurePattern($pattern); } if ($action->boolean('blacklistnickname')) { $pattern = $action->trimmed('blacklistnicknamepattern'); - $confNicknames = $this->_configArray('blacklist', 'nicknames'); - $confNicknames[] = $pattern; - Config::save('blacklist', 'nicknames', implode("\r\n", $confNicknames)); + Nickname_blacklist::ensurePattern($pattern); } return true; diff --git a/plugins/Blacklist/Homepage_blacklist.php b/plugins/Blacklist/Homepage_blacklist.php new file mode 100644 index 000000000..32080667e --- /dev/null +++ b/plugins/Blacklist/Homepage_blacklist.php @@ -0,0 +1,189 @@ +<?php +/** + * Data class for homepage blacklisting + * + * PHP version 5 + * + * @category Data + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * 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/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for Homepage blacklist + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +class Homepage_blacklist extends Memcached_DataObject +{ + public $__table = 'homepage_blacklist'; // table name + public $pattern; // string pattern + public $created; // datetime + + /** + * Get an instance by key + * + * This is a utility method to get a single instance with a given key value. + * + * @param string $k Key to use to lookup (usually 'user_id' for this class) + * @param mixed $v Value to lookup + * + * @return Homepage_blacklist object found, or null for no hits + * + */ + + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('Homepage_blacklist', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() + { + return array('pattern' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + } + + /** + * return key definitions for DB_DataObject + * + * DB_DataObject needs to know about keys that the table has; this function + * defines them. + * + * @return array key definitions + */ + + function keys() + { + return array('pattern' => 'K'); + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. + * + * @return array key definitions + */ + + function keyTypes() + { + return $this->keys(); + } + + /** + * Return a list of patterns to check + * + * @return array string patterns to check + */ + + static function getPatterns() + { + $patterns = self::cacheGet('homepage_blacklist:patterns'); + + if ($patterns === false) { + + $patterns = array(); + + $nb = new Homepage_blacklist(); + + $nb->find(); + + while ($nb->fetch()) { + $patterns[] = $nb->pattern; + } + + self::cacheSet('homepage_blacklist:patterns', $patterns); + } + + return $patterns; + } + + /** + * Save new list of patterns + * + * @return array of patterns to check + */ + + static function saveNew($newPatterns) + { + $oldPatterns = self::getPatterns(); + + // Delete stuff that's old that not in new + + $toDelete = array_diff($oldPatterns, $newPatterns); + + // Insert stuff that's in new and not in old + + $toInsert = array_diff($newPatterns, $oldPatterns); + + foreach ($toDelete as $pattern) { + $nb = Homepage_blacklist::staticGet('pattern', $pattern); + if (!empty($nb)) { + $nb->delete(); + } + } + + foreach ($toInsert as $pattern) { + $nb = new Homepage_blacklist(); + $nb->pattern = $pattern; + $nb->created = common_sql_now(); + $nb->insert(); + } + + self::blow('homepage_blacklist:patterns'); + } + + static function ensurePattern($pattern) + { + $hb = Homepage_blacklist::staticGet('pattern', $pattern); + + if (empty($nb)) { + $hb = new Homepage_blacklist(); + $hb->pattern = $pattern; + $hb->created = common_sql_now(); + $hb->insert(); + self::blow('homepage_blacklist:patterns'); + } + } +} diff --git a/plugins/Blacklist/Nickname_blacklist.php b/plugins/Blacklist/Nickname_blacklist.php new file mode 100644 index 000000000..981063144 --- /dev/null +++ b/plugins/Blacklist/Nickname_blacklist.php @@ -0,0 +1,180 @@ +<?php +/** + * Data class for nickname blacklisting + * + * PHP version 5 + * + * @category Data + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * 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/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for Nickname blacklist + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +class Nickname_blacklist extends Memcached_DataObject +{ + public $__table = 'nickname_blacklist'; // table name + public $pattern; // string pattern + public $created; // datetime + + /** + * Get an instance by key + * + * This is a utility method to get a single instance with a given key value. + * + * @param string $k Key to use to lookup + * @param mixed $v Value to lookup + * + * @return Nickname_blacklist object found, or null for no hits + * + */ + + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('Nickname_blacklist', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * @return array array of column definitions + */ + + function table() + { + return array('pattern' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + } + + /** + * return key definitions for DB_DataObject + * + * @return array key definitions + */ + + function keys() + { + return array('pattern' => 'K'); + } + + /** + * return key definitions for Memcached_DataObject + * + * @return array key definitions + */ + + function keyTypes() + { + return $this->keys(); + } + + /** + * Return a list of patterns to check + * + * @return array string patterns to check + */ + + static function getPatterns() + { + $patterns = self::cacheGet('nickname_blacklist:patterns'); + + if ($patterns === false) { + + $patterns = array(); + + $nb = new Nickname_blacklist(); + + $nb->find(); + + while ($nb->fetch()) { + $patterns[] = $nb->pattern; + } + + self::cacheSet('nickname_blacklist:patterns', $patterns); + } + + return $patterns; + } + + /** + * Save new list of patterns + * + * @return array of patterns to check + */ + + static function saveNew($newPatterns) + { + $oldPatterns = self::getPatterns(); + + // Delete stuff that's old that not in new + + $toDelete = array_diff($oldPatterns, $newPatterns); + + // Insert stuff that's in new and not in old + + $toInsert = array_diff($newPatterns, $oldPatterns); + + foreach ($toDelete as $pattern) { + $nb = Nickname_blacklist::staticGet('pattern', $pattern); + if (!empty($nb)) { + $nb->delete(); + } + } + + foreach ($toInsert as $pattern) { + $nb = new Nickname_blacklist(); + $nb->pattern = $pattern; + $nb->created = common_sql_now(); + $nb->insert(); + } + + self::blow('nickname_blacklist:patterns'); + } + + static function ensurePattern($pattern) + { + $nb = Nickname_blacklist::staticGet('pattern', $pattern); + + if (empty($nb)) { + $nb = new Nickname_blacklist(); + $nb->pattern = $pattern; + $nb->created = common_sql_now(); + $nb->insert(); + self::blow('nickname_blacklist:patterns'); + } + } +} diff --git a/plugins/Blacklist/blacklistadminpanel.php b/plugins/Blacklist/blacklistadminpanel.php index 98d07080d..b996aba8d 100644 --- a/plugins/Blacklist/blacklistadminpanel.php +++ b/plugins/Blacklist/blacklistadminpanel.php @@ -88,35 +88,24 @@ class BlacklistadminpanelAction extends AdminPanelAction function saveSettings() { - static $settings = array( - 'blacklist' => array('nicknames', 'urls'), - ); + $nickPatterns = array(); - $values = array(); + $rawNickPatterns = explode("\n", $this->trimmed('blacklist-nicknames')); - foreach ($settings as $section => $parts) { - foreach ($parts as $setting) { - $values[$section][$setting] = $this->trimmed("$section-$setting"); - } + foreach ($rawNickPatterns as $raw) { + $nickPatterns[] = trim($raw); } - // This throws an exception on validation errors + Nickname_blacklist::saveNew($nickPatterns); - $this->validate($values); + $rawUrlPatterns = explode("\n", $this->trimmed('blacklist-urls')); + $urlPatterns = array(); - // assert(all values are valid); - - $config = new Config(); - - $config->query('BEGIN'); - - foreach ($settings as $section => $parts) { - foreach ($parts as $setting) { - Config::save($section, $setting, $values[$section][$setting]); - } + foreach ($rawUrlPatterns as $raw) { + $urlPatterns[] = trim($raw); } - $config->query('COMMIT'); + Homepage_blacklist::saveNew($urlPatterns); return; } @@ -191,14 +180,19 @@ class BlacklistAdminPanelForm extends Form $this->out->elementStart('ul', 'form_data'); $this->out->elementStart('li'); + + $nickPatterns = Nickname_blacklist::getPatterns(); + $this->out->textarea('blacklist-nicknames', _m('Nicknames'), - common_config('blacklist', 'nicknames'), + implode("\r\n", $nickPatterns), _('Patterns of nicknames to block, one per line')); $this->out->elementEnd('li'); + $urlPatterns = Homepage_blacklist::getPatterns(); + $this->out->elementStart('li'); $this->out->textarea('blacklist-urls', _m('URLs'), - common_config('blacklist', 'urls'), + implode("\r\n", $urlPatterns), _('Patterns of URLs to block, one per line')); $this->out->elementEnd('li'); diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php index 1724b5f7b..270e2c624 100644 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@ -45,14 +45,7 @@ if (!defined('STATUSNET')) { class OpenIDPlugin extends Plugin { - /** - * Initializer for the plugin. - */ - - function __construct() - { - parent::__construct(); - } + public $openidOnly = false; /** * Add OpenID-related paths to the router table @@ -79,6 +72,60 @@ class OpenIDPlugin extends Plugin } /** + * In OpenID-only mode, disable paths for password stuff + * + * @param string $path path to connect + * @param array $defaults path defaults + * @param array $rules path rules + * @param array $result unused + * + * @return boolean hook return + */ + + function onStartConnectPath(&$path, &$defaults, &$rules, &$result) + { + if ($this->openidOnly) { + static $block = array('main/login', + 'main/register', + 'main/recoverpassword', + 'settings/password'); + + if (in_array($path, $block)) { + return false; + } + } + + return true; + } + + /** + * If we've been hit with password-login args, redirect + * + * @param array $args args (URL, Get, post) + * + * @return boolean hook return + */ + + function onArgsInitialize($args) + { + if ($this->openidOnly) { + if (array_key_exists('action', $args)) { + $action = trim($args['action']); + if (in_array($action, array('login', 'register'))) { + common_redirect(common_local_url('openidlogin')); + exit(0); + } else if ($action == 'passwordsettings') { + common_redirect(common_local_url('openidsettings')); + exit(0); + } else if ($action == 'recoverpassword') { + throw new ClientException('Unavailable action'); + } + } + } + return true; + } + + /** * Public XRDS output hook * * Puts the bits of code needed by some OpenID providers to show @@ -143,6 +190,69 @@ class OpenIDPlugin extends Plugin } /** + * If we're in OpenID-only mode, hide all the main menu except OpenID login. + * + * @param Action $action Action being run + * + * @return boolean hook return + */ + + function onStartPrimaryNav($action) + { + if ($this->openidOnly && !common_logged_in()) { + // TRANS: Tooltip for main menu option "Login" + $tooltip = _m('TOOLTIP', 'Login to the site'); + // TRANS: Main menu option when not logged in to log in + $action->menuItem(common_local_url('openidlogin'), + _m('MENU', 'Login'), + $tooltip, + false, + 'nav_login'); + // TRANS: Tooltip for main menu option "Help" + $tooltip = _m('TOOLTIP', 'Help me!'); + // TRANS: Main menu option for help on the StatusNet site + $action->menuItem(common_local_url('doc', array('title' => 'help')), + _m('MENU', 'Help'), + $tooltip, + false, + 'nav_help'); + if (!common_config('site', 'private')) { + // TRANS: Tooltip for main menu option "Search" + $tooltip = _m('TOOLTIP', 'Search for people or text'); + // TRANS: Main menu option when logged in or when the StatusNet instance is not private + $action->menuItem(common_local_url('peoplesearch'), + _m('MENU', 'Search'), $tooltip, false, 'nav_search'); + } + Event::handle('EndPrimaryNav', array($action)); + return false; + } + return true; + } + + /** + * Menu for login + * + * If we're in openidOnly mode, we disable the menu for all other login. + * + * @param Action &$action Action being executed + * + * @return boolean hook return + */ + + function onStartLoginGroupNav(&$action) + { + if ($this->openidOnly) { + $this->showOpenIDLoginTab($action); + // Even though we replace this code, we + // DON'T run the End* hook, to keep others from + // adding tabs. Not nice, but. + return false; + } + + return true; + } + + /** * Menu item for login * * @param Action &$action Action being executed @@ -152,18 +262,49 @@ class OpenIDPlugin extends Plugin function onEndLoginGroupNav(&$action) { + $this->showOpenIDLoginTab($action); + + return true; + } + + /** + * Show menu item for login + * + * @param Action $action Action being executed + * + * @return void + */ + + function showOpenIDLoginTab($action) + { $action_name = $action->trimmed('action'); $action->menuItem(common_local_url('openidlogin'), _m('OpenID'), _m('Login or register with OpenID'), $action_name === 'openidlogin'); + } + + /** + * Show menu item for password + * + * We hide it in openID-only mode + * + * @param Action $menu Widget for menu + * @param void &$unused Unused value + * + * @return void + */ + function onStartAccountSettingsPasswordMenuItem($menu, &$unused) { + if ($this->openidOnly) { + return false; + } return true; } /** - * Menu item for OpenID admin + * Menu item for OpenID settings * * @param Action &$action Action being executed * @@ -301,7 +442,7 @@ class OpenIDPlugin extends Plugin function onRedirectToLogin($action, $user) { - if (!empty($user) && User_openid::hasOpenID($user->id)) { + if ($this->openidOnly || (!empty($user) && User_openid::hasOpenID($user->id))) { common_redirect(common_local_url('openidlogin'), 303); return false; } |