summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/action.php4
-rw-r--r--lib/adminpanelaction.php46
-rw-r--r--lib/authenticationplugin.php231
-rw-r--r--lib/authorizationplugin.php105
-rw-r--r--lib/cache.php182
-rw-r--r--lib/columndef.php1
-rw-r--r--lib/common.php33
-rw-r--r--lib/default.php30
-rw-r--r--lib/htmloutputter.php2
-rw-r--r--lib/jabber.php2
-rw-r--r--lib/language.php16
-rw-r--r--lib/mail.php4
-rw-r--r--lib/mailhandler.php275
-rw-r--r--lib/noticeform.php22
-rw-r--r--lib/noticelist.php2
-rw-r--r--lib/ping.php4
-rw-r--r--lib/plugin.php11
-rw-r--r--lib/repeatform.php4
-rw-r--r--lib/router.php5
-rw-r--r--lib/schema.php8
-rw-r--r--lib/util.php30
21 files changed, 960 insertions, 57 deletions
diff --git a/lib/action.php b/lib/action.php
index dac0e2583..1b4cb5cec 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -252,6 +252,8 @@ class Action extends HTMLOutputter // lawsuit
if (Event::handle('StartShowJQueryScripts', array($this))) {
$this->script('js/jquery.min.js');
$this->script('js/jquery.form.js');
+ $this->script('js/jquery.cookie.js');
+ $this->script('js/json2.js');
$this->script('js/jquery.joverlay.min.js');
Event::handle('EndShowJQueryScripts', array($this));
}
@@ -735,6 +737,8 @@ class Action extends HTMLOutputter // lawsuit
_('Privacy'));
$this->menuItem(common_local_url('doc', array('title' => 'source')),
_('Source'));
+ $this->menuItem(common_local_url('version'),
+ _('Version'));
$this->menuItem(common_local_url('doc', array('title' => 'contact')),
_('Contact'));
$this->menuItem(common_local_url('doc', array('title' => 'badge')),
diff --git a/lib/adminpanelaction.php b/lib/adminpanelaction.php
index 7997eb2b1..a6981ac61 100644
--- a/lib/adminpanelaction.php
+++ b/lib/adminpanelaction.php
@@ -70,7 +70,7 @@ class AdminPanelAction extends Action
if (!common_logged_in()) {
$this->clientError(_('Not logged in.'));
- return;
+ return false;
}
$user = common_current_user();
@@ -94,7 +94,18 @@ class AdminPanelAction extends Action
if (!$user->hasRight(Right::CONFIGURESITE)) {
$this->clientError(_('You cannot make changes to this site.'));
- return;
+ return false;
+ }
+
+ // This panel must be enabled
+
+ $name = $this->trimmed('action');
+
+ $name = mb_substr($name, 0, -10);
+
+ if (!in_array($name, common_config('admin', 'panels'))) {
+ $this->clientError(_('Changes to that panel are not allowed.'), 403);
+ return false;
}
return true;
@@ -224,7 +235,7 @@ class AdminPanelAction extends Action
$this->clientError(_('saveSettings() not implemented.'));
return;
}
-
+
/**
* Delete a design setting
*
@@ -296,20 +307,33 @@ class AdminPanelNav extends Widget
if (Event::handle('StartAdminPanelNav', array($this))) {
- $this->out->menuItem(common_local_url('siteadminpanel'), _('Site'),
- _('Basic site configuration'), $action_name == 'siteadminpanel', 'nav_site_admin_panel');
+ if ($this->canAdmin('site')) {
+ $this->out->menuItem(common_local_url('siteadminpanel'), _('Site'),
+ _('Basic site configuration'), $action_name == 'siteadminpanel', 'nav_site_admin_panel');
+ }
- $this->out->menuItem(common_local_url('designadminpanel'), _('Design'),
- _('Design configuration'), $action_name == 'designadminpanel', 'nav_design_admin_panel');
+ if ($this->canAdmin('design')) {
+ $this->out->menuItem(common_local_url('designadminpanel'), _('Design'),
+ _('Design configuration'), $action_name == 'designadminpanel', 'nav_design_admin_panel');
+ }
- $this->out->menuItem(common_local_url('useradminpanel'), _('User'),
- _('Paths configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel');
+ if ($this->canAdmin('user')) {
+ $this->out->menuItem(common_local_url('useradminpanel'), _('User'),
+ _('Paths configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel');
+ }
- $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
- _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel');
+ if ($this->canAdmin('paths')) {
+ $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
+ _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel');
+ }
Event::handle('EndAdminPanelNav', array($this));
}
$this->action->elementEnd('ul');
}
+
+ function canAdmin($name)
+ {
+ return in_array($name, common_config('admin', 'panels'));
+ }
}
diff --git a/lib/authenticationplugin.php b/lib/authenticationplugin.php
new file mode 100644
index 000000000..de479a576
--- /dev/null
+++ b/lib/authenticationplugin.php
@@ -0,0 +1,231 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Superclass for plugins that do authentication and/or authorization
+ *
+ * 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 Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @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('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Superclass for plugins that do authentication
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+abstract class AuthenticationPlugin extends Plugin
+{
+ //is this plugin authoritative for authentication?
+ public $authoritative = false;
+
+ //should accounts be automatically created after a successful login attempt?
+ public $autoregistration = false;
+
+ //can the user change their email address
+ public $password_changeable=true;
+
+ //unique name for this authentication provider
+ public $provider_name;
+
+ //------------Auth plugin should implement some (or all) of these methods------------\\
+ /**
+ * Check if a nickname/password combination is valid
+ * @param username
+ * @param password
+ * @return boolean true if the credentials are valid, false if they are invalid.
+ */
+ function checkPassword($username, $password)
+ {
+ return false;
+ }
+
+ /**
+ * Automatically register a user when they attempt to login with valid credentials.
+ * User::register($data) is a very useful method for this implementation
+ * @param username
+ * @return mixed instance of User, or false (if user couldn't be created)
+ */
+ function autoRegister($username)
+ {
+ $registration_data = array();
+ $registration_data['nickname'] = $username ;
+ return User::register($registration_data);
+ }
+
+ /**
+ * Change a user's password
+ * The old password has been verified to be valid by this plugin before this call is made
+ * @param username
+ * @param oldpassword
+ * @param newpassword
+ * @return boolean true if the password was changed, false if password changing failed for some reason
+ */
+ function changePassword($username,$oldpassword,$newpassword)
+ {
+ return false;
+ }
+
+ //------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\
+ function onInitializePlugin(){
+ if(!isset($this->provider_name)){
+ throw new Exception("must specify a provider_name for this authentication provider");
+ }
+ }
+
+ /**
+ * Internal AutoRegister event handler
+ * @param nickname
+ * @param provider_name
+ * @param user - the newly registered user
+ */
+ function onAutoRegister($nickname, $provider_name, &$user)
+ {
+ if($provider_name == $this->provider_name && $this->autoregistration){
+ $user = $this->autoregister($nickname);
+ if($user){
+ User_username::register($user,$nickname,$this->provider_name);
+ return false;
+ }
+ }
+ }
+
+ function onStartCheckPassword($nickname, $password, &$authenticatedUser){
+ //map the nickname to a username
+ $user_username = new User_username();
+ $user_username->username=$nickname;
+ $user_username->provider_name=$this->provider_name;
+ if($user_username->find() && $user_username->fetch()){
+ $username = $user_username->username;
+ $authenticated = $this->checkPassword($username, $password);
+ if($authenticated){
+ $authenticatedUser = User::staticGet('id', $user_username->user_id);
+ return false;
+ }
+ }else{
+ $user = User::staticGet('nickname', $nickname);
+ if($user){
+ //make sure a different provider isn't handling this nickname
+ $user_username = new User_username();
+ $user_username->username=$nickname;
+ if(!$user_username->find()){
+ //no other provider claims this username, so it's safe for us to handle it
+ $authenticated = $this->checkPassword($nickname, $password);
+ if($authenticated){
+ $authenticatedUser = User::staticGet('nickname', $nickname);
+ User_username::register($authenticatedUser,$nickname,$this->provider_name);
+ return false;
+ }
+ }
+ }else{
+ $authenticated = $this->checkPassword($nickname, $password);
+ if($authenticated){
+ if(! Event::handle('AutoRegister', array($nickname, $this->provider_name, &$authenticatedUser))){
+ //unlike most Event::handle lines of code, this one has a ! (not)
+ //we want to do this if the event *was* handled - this isn't a "default" implementation
+ //like most code of this form.
+ if($authenticatedUser){
+ return false;
+ }
+ }
+ }
+ }
+ }
+ if($this->authoritative){
+ return false;
+ }else{
+ //we're not authoritative, so let other handlers try
+ return;
+ }
+ }
+
+ function onStartChangePassword($user,$oldpassword,$newpassword)
+ {
+ if($this->password_changeable){
+ $user_username = new User_username();
+ $user_username->user_id=$user->id;
+ $user_username->provider_name=$this->provider_name;
+ if($user_username->find() && $user_username->fetch()){
+ $authenticated = $this->checkPassword($user_username->username, $oldpassword);
+ if($authenticated){
+ $result = $this->changePassword($user_username->username,$oldpassword,$newpassword);
+ if($result){
+ //stop handling of other handlers, because what was requested was done
+ return false;
+ }else{
+ throw new Exception(_('Password changing failed'));
+ }
+ }else{
+ if($this->authoritative){
+ //since we're authoritative, no other plugin could do this
+ throw new Exception(_('Password changing failed'));
+ }else{
+ //let another handler try
+ return null;
+ }
+ }
+ }
+ }else{
+ if($this->authoritative){
+ //since we're authoritative, no other plugin could do this
+ throw new Exception(_('Password changing is not allowed'));
+ }
+ }
+ }
+
+ function onStartAccountSettingsPasswordMenuItem($widget)
+ {
+ if($this->authoritative && !$this->password_changeable){
+ //since we're authoritative, no other plugin could change passwords, so do not render the menu item
+ return false;
+ }
+ }
+
+ function onCheckSchema() {
+ $schema = Schema::get();
+ $schema->ensureTable('user_username',
+ array(new ColumnDef('provider_name', 'varchar',
+ '255', false, 'PRI'),
+ new ColumnDef('username', 'varchar',
+ '255', false, 'PRI'),
+ new ColumnDef('user_id', 'integer',
+ null, false),
+ new ColumnDef('created', 'datetime',
+ null, false),
+ new ColumnDef('modified', 'timestamp')));
+ return true;
+ }
+
+ function onUserDeleteRelated($user, &$tables)
+ {
+ $tables[] = 'User_username';
+ return true;
+ }
+}
+
diff --git a/lib/authorizationplugin.php b/lib/authorizationplugin.php
new file mode 100644
index 000000000..733b0c065
--- /dev/null
+++ b/lib/authorizationplugin.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Superclass for plugins that do authorization
+ *
+ * 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 Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @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('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Superclass for plugins that do authorization
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+abstract class AuthorizationPlugin extends Plugin
+{
+ //is this plugin authoritative for authorization?
+ public $authoritative = false;
+
+ //------------Auth plugin should implement some (or all) of these methods------------\\
+
+ /**
+ * Is a user allowed to log in?
+ * @param user
+ * @return boolean true if the user is allowed to login, false if explicitly not allowed to login, null if we don't explicitly allow or deny login
+ */
+ function loginAllowed($user) {
+ return null;
+ }
+
+ /**
+ * Does a profile grant the user a named role?
+ * @param profile
+ * @return boolean true if the profile has the role, false if not
+ */
+ function hasRole($profile, $name) {
+ return false;
+ }
+
+ //------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\
+
+ function onStartSetUser(&$user) {
+ $loginAllowed = $this->loginAllowed($user);
+ if($loginAllowed === true){
+ return;
+ }else if($loginAllowed === false){
+ $user = null;
+ return false;
+ }else{
+ if($this->authoritative) {
+ $user = null;
+ return false;
+ }else{
+ return;
+ }
+ }
+ }
+
+ function onStartSetApiUser(&$user) {
+ return $this->onStartSetUser(&$user);
+ }
+
+ function onStartHasRole($profile, $name, &$has_role) {
+ if($this->hasRole($profile, $name)){
+ $has_role = true;
+ return false;
+ }else{
+ if($this->authoritative) {
+ $has_role = false;
+ return false;
+ }else{
+ return;
+ }
+ }
+ }
+}
+
diff --git a/lib/cache.php b/lib/cache.php
new file mode 100644
index 000000000..b7b34c050
--- /dev/null
+++ b/lib/cache.php
@@ -0,0 +1,182 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Cache interface plus default in-memory cache implementation
+ *
+ * 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 Cache
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 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/
+ */
+
+/**
+ * Interface for caching
+ *
+ * An abstract interface for caching. Because we originally used the
+ * Memcache plugin directly, the interface uses a small subset of the
+ * Memcache interface.
+ *
+ * @category Cache
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class Cache
+{
+ var $_items = array();
+ static $_inst = null;
+
+ /**
+ * Singleton constructor
+ *
+ * Use this to get the singleton instance of Cache.
+ *
+ * @return Cache cache object
+ */
+
+ static function instance()
+ {
+ if (is_null(self::$_inst)) {
+ self::$_inst = new Cache();
+ }
+
+ return self::$_inst;
+ }
+
+ /**
+ * Create a cache key from input text
+ *
+ * Builds a cache key from input text. Helps to namespace
+ * the cache area (if shared with other applications or sites)
+ * and prevent conflicts.
+ *
+ * @param string $extra the real part of the key
+ *
+ * @return string full key
+ */
+
+ static function key($extra)
+ {
+ $base_key = common_config('cache', 'base');
+
+ if (empty($base_key)) {
+ $base_key = common_keyize(common_config('site', 'name'));
+ }
+
+ return 'statusnet:' . $base_key . ':' . $extra;
+ }
+
+ /**
+ * Make a string suitable for use as a key
+ *
+ * Useful for turning primary keys of tables into cache keys.
+ *
+ * @param string $str string to turn into a key
+ *
+ * @return string keyized string
+ */
+
+ static function keyize($str)
+ {
+ $str = strtolower($str);
+ $str = preg_replace('/\s/', '_', $str);
+ return $str;
+ }
+
+ /**
+ * Get a value associated with a key
+ *
+ * The value should have been set previously.
+ *
+ * @param string $key Lookup key
+ *
+ * @return string retrieved value or null if unfound
+ */
+
+ function get($key)
+ {
+ $value = false;
+
+ if (Event::handle('StartCacheGet', array(&$key, &$value))) {
+ if (array_key_exists($key, $this->_items)) {
+ $value = unserialize($this->_items[$key]);
+ }
+ Event::handle('EndCacheGet', array($key, &$value));
+ }
+
+ return $value;
+ }
+
+ /**
+ * Set the value associated with a key
+ *
+ * @param string $key The key to use for lookups
+ * @param string $value The value to store
+ * @param integer $flag Flags to use, mostly ignored
+ * @param integer $expiry Expiry value, mostly ignored
+ *
+ * @return boolean success flag
+ */
+
+ function set($key, $value, $flag=null, $expiry=null)
+ {
+ $success = false;
+
+ if (Event::handle('StartCacheSet', array(&$key, &$value, &$flag,
+ &$expiry, &$success))) {
+
+ $this->_items[$key] = serialize($value);
+
+ $success = true;
+
+ Event::handle('EndCacheSet', array($key, $value, $flag,
+ $expiry));
+ }
+
+ return $success;
+ }
+
+ /**
+ * Delete the value associated with a key
+ *
+ * @param string $key Key to delete
+ *
+ * @return boolean success flag
+ */
+
+ function delete($key)
+ {
+ $success = false;
+
+ if (Event::handle('StartCacheDelete', array(&$key, &$success))) {
+ if (array_key_exists($key, $this->_items)) {
+ unset($this->_items[$key]);
+ }
+ $success = true;
+ Event::handle('EndCacheDelete', array($key));
+ }
+
+ return $success;
+ }
+}
diff --git a/lib/columndef.php b/lib/columndef.php
index 1bae6b33b..ac2fcd23e 100644
--- a/lib/columndef.php
+++ b/lib/columndef.php
@@ -74,6 +74,7 @@ class ColumnDef
* @param string $key type of key
* @param value $default default value
* @param value $extra unused
+ * @param boolean $auto_increment
*/
function __construct($name=null, $type=null, $size=null,
diff --git a/lib/common.php b/lib/common.php
index 7fa1910af..fb5e5919e 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -210,6 +210,18 @@ if ($_db_name != 'statusnet' && !array_key_exists('ini_'.$_db_name, $config['db'
$config['db']['ini_'.$_db_name] = INSTALLDIR.'/classes/statusnet.ini';
}
+// Backwards compatibility
+
+if (array_key_exists('memcached', $config)) {
+ if ($config['memcached']['enabled']) {
+ addPlugin('Memcache', array('servers' => $config['memcached']['server']));
+ }
+
+ if (!empty($config['memcached']['base'])) {
+ $config['cache']['base'] = $config['memcached']['base'];
+ }
+}
+
function __autoload($cls)
{
if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
@@ -226,6 +238,27 @@ function __autoload($cls)
}
}
+// Load default plugins
+
+foreach ($config['plugins']['default'] as $name => $params) {
+ if (is_null($params)) {
+ addPlugin($name);
+ } else if (is_array($params)) {
+ if (count($params) == 0) {
+ addPlugin($name);
+ } else {
+ $keys = array_keys($params);
+ if (is_string($keys[0])) {
+ addPlugin($name, $params);
+ } else {
+ foreach ($params as $paramset) {
+ addPlugin($name, $paramset);
+ }
+ }
+ }
+ }
+}
+
// XXX: how many of these could be auto-loaded on use?
// XXX: note that these files should not use config options
// at compile time since DB config options are not yet loaded.
diff --git a/lib/default.php b/lib/default.php
index 42d4623b1..fa862f3ff 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -54,6 +54,7 @@ $default =
'dupelimit' => 60, # default for same person saying the same thing
'textlimit' => 140,
'indent' => true,
+ 'use_x_sendfile' => false,
),
'db' =>
array('database' => 'YOU HAVE TO SET THIS IN config.php',
@@ -147,11 +148,8 @@ $default =
array('enabled' => true,
'consumer_key' => null,
'consumer_secret' => null),
- 'memcached' =>
- array('enabled' => false,
- 'server' => 'localhost',
- 'base' => null,
- 'port' => 11211),
+ 'cache' =>
+ array('base' => null),
'ping' =>
array('notify' => array()),
'inboxes' =>
@@ -226,9 +224,29 @@ $default =
'message' =>
array('contentlimit' => null),
'location' =>
- array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth
+ array('share' => 'user', // whether to share location; 'always', 'user', 'never'
+ 'sharedefault' => true),
'omb' =>
array('timeout' => 5), // HTTP request timeout in seconds when contacting remote hosts for OMB updates
'logincommand' =>
array('disabled' => true),
+ 'plugins' =>
+ array('default' => array('LilUrl' => array('shortenerName'=>'ur1.ca',
+ 'freeService' => true,
+ 'serviceUrl'=>'http://ur1.ca/'),
+ 'PtitUrl' => array('shortenerName' => 'ptiturl.com',
+ 'serviceUrl' => 'http://ptiturl.com/?creer=oui&action=Reduire&url=%1$s'),
+ 'SimpleUrl' => array(array('shortenerName' => 'is.gd', 'serviceUrl' => 'http://is.gd/api.php?longurl=%1$s'),
+ array('shortenerName' => 'snipr.com', 'serviceUrl' => 'http://snipr.com/site/snip?r=simple&link=%1$s'),
+ array('shortenerName' => 'metamark.net', 'serviceUrl' => 'http://metamark.net/api/rest/simple?long_url=%1$s'),
+ array('shortenerName' => 'tinyurl.com', 'serviceUrl' => 'http://tinyurl.com/api-create.php?url=%1$s')),
+ 'TightUrl' => array('shortenerName' => '2tu.us', 'freeService' => true,'serviceUrl'=>'http://2tu.us/?save=y&url=%1$s'),
+ 'Geonames' => null,
+ 'Mapstraction' => null,
+ 'Linkback' => null,
+ 'WikiHashtags' => null,
+ 'OpenID' => null),
+ ),
+ 'admin' =>
+ array('panels' => array('design', 'site', 'user', 'paths')),
);
diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php
index 2091c6e2c..31660ce95 100644
--- a/lib/htmloutputter.php
+++ b/lib/htmloutputter.php
@@ -352,7 +352,7 @@ class HTMLOutputter extends XMLOutputter
{
if(Event::handle('StartScriptElement', array($this,&$src,&$type))) {
$url = parse_url($src);
- if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
+ if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment']))
{
$src = common_path($src) . '?version=' . STATUSNET_VERSION;
}
diff --git a/lib/jabber.php b/lib/jabber.php
index 01aed8ffa..a821856a8 100644
--- a/lib/jabber.php
+++ b/lib/jabber.php
@@ -440,7 +440,7 @@ function jabber_public_notice($notice)
// XXX: should we send out non-local messages if public,localonly
// = false? I think not
- if ($public && $notice->is_local) {
+ if ($public && $notice->is_local == Notice::LOCAL_PUBLIC) {
$profile = Profile::staticGet($notice->profile_id);
if (!$profile) {
diff --git a/lib/language.php b/lib/language.php
index d8f529201..f5ee7fac5 100644
--- a/lib/language.php
+++ b/lib/language.php
@@ -32,6 +32,21 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
+// Locale category constants are usually predefined, but may not be
+// on some systems such as Win32.
+$LC_CATEGORIES = array('LC_CTYPE',
+ 'LC_NUMERIC',
+ 'LC_TIME',
+ 'LC_COLLATE',
+ 'LC_MONETARY',
+ 'LC_MESSAGES',
+ 'LC_ALL');
+foreach ($LC_CATEGORIES as $key => $name) {
+ if (!defined($name)) {
+ define($name, $key);
+ }
+}
+
if (!function_exists('gettext')) {
require_once("php-gettext/gettext.inc");
}
@@ -283,6 +298,7 @@ function get_all_languages() {
'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'),
'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'),
'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'),
+ 'fa' => array('q' => 1, 'lang' => 'fa', 'name' => 'Persian', 'direction' => 'rtl'),
'fr-fr' => array('q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'),
'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'),
'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'),
diff --git a/lib/mail.php b/lib/mail.php
index dffac3262..472a88e06 100644
--- a/lib/mail.php
+++ b/lib/mail.php
@@ -599,6 +599,10 @@ function mail_notify_attn($user, $notice)
$sender = $notice->getProfile();
+ if ($sender->id == $user->id) {
+ return;
+ }
+
if (!$sender->hasRight(Right::EMAILONREPLY)) {
return;
}
diff --git a/lib/mailhandler.php b/lib/mailhandler.php
new file mode 100644
index 000000000..32a8cd9bc
--- /dev/null
+++ b/lib/mailhandler.php
@@ -0,0 +1,275 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 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/>.
+ */
+
+require_once(INSTALLDIR . '/lib/mail.php');
+require_once(INSTALLDIR . '/lib/mediafile.php');
+require_once('Mail/mimeDecode.php');
+
+# FIXME: we use both Mail_mimeDecode and mailparse
+# Need to move everything to mailparse
+
+class MailHandler
+{
+ function __construct()
+ {
+ }
+
+ function handle_message($rawmessage)
+ {
+ list($from, $to, $msg, $attachments) = $this->parse_message($rawmessage);
+ if (!$from || !$to || !$msg) {
+ $this->error(null, _('Could not parse message.'));
+ }
+ common_log(LOG_INFO, "Mail from $from to $to with ".count($attachments) .' attachment(s): ' .substr($msg, 0, 20));
+ $user = $this->user_from_header($from);
+ if (!$user) {
+ $this->error($from, _('Not a registered user.'));
+ return false;
+ }
+ if (!$this->user_match_to($user, $to)) {
+ $this->error($from, _('Sorry, that is not your incoming email address.'));
+ return false;
+ }
+ if (!$user->emailpost) {
+ $this->error($from, _('Sorry, no incoming email allowed.'));
+ return false;
+ }
+ $response = $this->handle_command($user, $from, $msg);
+ if ($response) {
+ return true;
+ }
+ $msg = $this->cleanup_msg($msg);
+ $msg = common_shorten_links($msg);
+ if (Notice::contentTooLong($msg)) {
+ $this->error($from, sprintf(_('That\'s too long. '.
+ 'Max notice size is %d chars.'),
+ Notice::maxContent()));
+ }
+
+ $mediafiles = array();
+
+ foreach($attachments as $attachment){
+
+ $mf = null;
+
+ try {
+ $mf = MediaFile::fromFileHandle($attachment, $user);
+ } catch(ClientException $ce) {
+ $this->error($from, $ce->getMessage());
+ }
+
+ $msg .= ' ' . $mf->shortUrl();
+
+ array_push($mediafiles, $mf);
+ fclose($attachment);
+ }
+
+ $err = $this->add_notice($user, $msg, $mediafiles);
+
+ if (is_string($err)) {
+ $this->error($from, $err);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ function error($from, $msg)
+ {
+ file_put_contents("php://stderr", $msg . "\n");
+ exit(1);
+ }
+
+ function user_from_header($from_hdr)
+ {
+ $froms = mailparse_rfc822_parse_addresses($from_hdr);
+ if (!$froms) {
+ return null;
+ }
+ $from = $froms[0];
+ $addr = common_canonical_email($from['address']);
+ $user = User::staticGet('email', $addr);
+ if (!$user) {
+ $user = User::staticGet('smsemail', $addr);
+ }
+ return $user;
+ }
+
+ function user_match_to($user, $to_hdr)
+ {
+ $incoming = $user->incomingemail;
+ $tos = mailparse_rfc822_parse_addresses($to_hdr);
+ foreach ($tos as $to) {
+ if (strcasecmp($incoming, $to['address']) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function handle_command($user, $from, $msg)
+ {
+ $inter = new CommandInterpreter();
+ $cmd = $inter->handle_command($user, $msg);
+ if ($cmd) {
+ $cmd->execute(new MailChannel($from));
+ return true;
+ }
+ return false;
+ }
+
+ function respond($from, $to, $response)
+ {
+
+ $headers['From'] = $to;
+ $headers['To'] = $from;
+ $headers['Subject'] = "Command complete";
+
+ return mail_send(array($from), $headers, $response);
+ }
+
+ function log($level, $msg)
+ {
+ common_log($level, 'MailDaemon: '.$msg);
+ }
+
+ function add_notice($user, $msg, $mediafiles)
+ {
+ try {
+ $notice = Notice::saveNew($user->id, $msg, 'mail');
+ } catch (Exception $e) {
+ $this->log(LOG_ERR, $e->getMessage());
+ return $e->getMessage();
+ }
+ foreach($mediafiles as $mf){
+ $mf->attachToNotice($notice);
+ }
+ common_broadcast_notice($notice);
+ $this->log(LOG_INFO,
+ 'Added notice ' . $notice->id . ' from user ' . $user->nickname);
+ return true;
+ }
+
+ function parse_message($contents)
+ {
+ $parsed = Mail_mimeDecode::decode(array('input' => $contents,
+ 'include_bodies' => true,
+ 'decode_headers' => true,
+ 'decode_bodies' => true));
+ if (!$parsed) {
+ return null;
+ }
+
+ $from = $parsed->headers['from'];
+
+ $to = $parsed->headers['to'];
+
+ $type = $parsed->ctype_primary . '/' . $parsed->ctype_secondary;
+
+ $attachments = array();
+
+ $this->extract_part($parsed,$msg,$attachments);
+
+ return array($from, $to, $msg, $attachments);
+ }
+
+ function extract_part($parsed,&$msg,&$attachments){
+ if ($parsed->ctype_primary == 'multipart') {
+ if($parsed->ctype_secondary == 'alternative'){
+ $altmsg = $this->extract_msg_from_multipart_alternative_part($parsed);
+ if(!empty($altmsg)) $msg = $altmsg;
+ }else{
+ foreach($parsed->parts as $part){
+ $this->extract_part($part,$msg,$attachments);
+ }
+ }
+ } else if ($parsed->ctype_primary == 'text'
+ && $parsed->ctype_secondary=='plain') {
+ $msg = $parsed->body;
+ if(strtolower($parsed->ctype_parameters['charset']) != "utf-8"){
+ $msg = utf8_encode($msg);
+ }
+ }else if(!empty($parsed->body)){
+ if(common_config('attachments', 'uploads')){
+ //only save attachments if uploads are enabled
+ $attachment = tmpfile();
+ fwrite($attachment, $parsed->body);
+ $attachments[] = $attachment;
+ }
+ }
+ }
+
+ function extract_msg_from_multipart_alternative_part($parsed){
+ foreach ($parsed->parts as $part) {
+ $this->extract_part($part,$msg,$attachments);
+ }
+ //we don't want any attachments that are a result of this parsing
+ return $msg;
+ }
+
+ function unsupported_type($type)
+ {
+ $this->error(null, "Unsupported message type: " . $type);
+ }
+
+ function cleanup_msg($msg)
+ {
+ $lines = explode("\n", $msg);
+
+ $output = '';
+
+ foreach ($lines as $line) {
+ // skip quotes
+ if (preg_match('/^\s*>.*$/', $line)) {
+ continue;
+ }
+ // skip start of quote
+ if (preg_match('/^\s*On.*wrote:\s*$/', $line)) {
+ continue;
+ }
+ // probably interesting to someone, not us
+ if (preg_match('/^\s*Sent via/', $line)) {
+ continue;
+ }
+ if (preg_match('/^\s*Sent from my/', $line)) {
+ continue;
+ }
+
+ // skip everything after a sig
+ if (preg_match('/^\s*--+\s*$/', $line) ||
+ preg_match('/^\s*__+\s*$/', $line))
+ {
+ break;
+ }
+ // skip everything after Outlook quote
+ if (preg_match('/^\s*-+\s*Original Message\s*-+\s*$/', $line)) {
+ break;
+ }
+ // skip everything after weird forward
+ if (preg_match('/^\s*Begin\s+forward/', $line)) {
+ break;
+ }
+
+ $output .= ' ' . $line;
+ }
+
+ preg_replace('/\s+/', ' ', $output);
+ return trim($output);
+ }
+}
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 593a1e932..f0b704e87 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -110,6 +110,8 @@ class NoticeForm extends Form
$this->user = common_current_user();
}
+ $this->profile = $this->user->getProfile();
+
if (common_config('attachments', 'uploads')) {
$this->enctype = 'multipart/form-data';
}
@@ -198,12 +200,22 @@ class NoticeForm extends Form
$this->out->hidden('notice_return-to', $this->action, 'returnto');
}
$this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto');
- $this->out->hidden('notice_data-lat', empty($this->lat) ? null : $this->lat, 'lat');
- $this->out->hidden('notice_data-lon', empty($this->lon) ? null : $this->lon, 'lon');
- $this->out->hidden('notice_data-location_id', empty($this->location_id) ? null : $this->location_id, 'location_id');
- $this->out->hidden('notice_data-location_ns', empty($this->location_ns) ? null : $this->location_ns, 'location_ns');
- Event::handle('StartShowNoticeFormData', array($this));
+ if ($this->user->shareLocation()) {
+ $this->out->hidden('notice_data-lat', empty($this->lat) ? (empty($this->profile->lat) ? null : $this->profile->lat) : $this->lat, 'lat');
+ $this->out->hidden('notice_data-lon', empty($this->lon) ? (empty($this->profile->lon) ? null : $this->profile->lon) : $this->lon, 'lon');
+ $this->out->hidden('notice_data-location_id', empty($this->location_id) ? (empty($this->profile->location_id) ? null : $this->profile->location_id) : $this->location_id, 'location_id');
+ $this->out->hidden('notice_data-location_ns', empty($this->location_ns) ? (empty($this->profile->location_ns) ? null : $this->profile->location_ns) : $this->location_ns, 'location_ns');
+
+ $this->out->elementStart('div', array('id' => 'notice_data-geo_wrap',
+ 'title' => common_local_url('geocode')));
+ $this->out->checkbox('notice_data-geo', _('Share my location'), true);
+ $this->out->elementEnd('div');
+ $this->out->inlineScript(' var NoticeDataGeoShareDisable_text = "'._('Do not share my location.').'";'.
+ ' var NoticeDataGeoInfoMinimize_text = "'._('Hide this info').'";');
+ }
+
+ Event::handle('EndShowNoticeFormData', array($this));
}
}
diff --git a/lib/noticelist.php b/lib/noticelist.php
index 5eb2633ac..78abf34a7 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -379,7 +379,7 @@ class NoticeListItem extends Widget
function showNoticeLink()
{
- if($this->notice->is_local){
+ if($this->notice->is_local == Notice::LOCAL_PUBLIC || $this->notice->is_local == Notice::LOCAL_NONPUBLIC){
$noticeurl = common_local_url('shownotice',
array('notice' => $this->notice->id));
}else{
diff --git a/lib/ping.php b/lib/ping.php
index 5698c4038..735af9ef1 100644
--- a/lib/ping.php
+++ b/lib/ping.php
@@ -21,7 +21,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
function ping_broadcast_notice($notice) {
- if (!$notice->is_local) {
+ if ($notice->is_local != Notice::LOCAL_PUBLIC && $notice->is_local != Notice::LOCAL_NONPUBLIC) {
return true;
}
@@ -115,4 +115,4 @@ function ping_notice_tags($notice) {
return implode('|', $tags);
}
return NULL;
-} \ No newline at end of file
+}
diff --git a/lib/plugin.php b/lib/plugin.php
index de7313e59..65ccdafbb 100644
--- a/lib/plugin.php
+++ b/lib/plugin.php
@@ -104,5 +104,16 @@ class Plugin
{
$this->log(LOG_DEBUG, $msg);
}
+
+ function onPluginVersion(&$versions)
+ {
+ $cls = get_class($this);
+ $name = mb_substr($cls, 0, -6);
+
+ $versions[] = array('name' => $name,
+ 'version' => _('Unknown'));
+
+ return true;
+ }
}
diff --git a/lib/repeatform.php b/lib/repeatform.php
index 50e5d6dbe..4f1c8aa32 100644
--- a/lib/repeatform.php
+++ b/lib/repeatform.php
@@ -104,7 +104,7 @@ class RepeatForm extends Form
*/
function formLegend()
{
- $this->out->element('legend', null, _('Repeat this notice'));
+ $this->out->element('legend', null, _('Repeat this notice?'));
}
/**
@@ -129,7 +129,7 @@ class RepeatForm extends Form
function formActions()
{
$this->out->submit('repeat-submit-' . $this->notice->id,
- _('Repeat'), 'submit', null, _('Repeat this notice'));
+ _('Yes'), 'submit', null, _('Repeat this notice'));
}
/**
diff --git a/lib/router.php b/lib/router.php
index 474e05996..287d3c79f 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -100,7 +100,10 @@ class Router
'sandbox', 'unsandbox',
'silence', 'unsilence',
'repeat',
- 'deleteuser');
+ 'deleteuser',
+ 'geocode',
+ 'version',
+ );
foreach ($main as $a) {
$m->connect('main/'.$a, array('action' => $a));
diff --git a/lib/schema.php b/lib/schema.php
index a8ba91b87..a7f64ebed 100644
--- a/lib/schema.php
+++ b/lib/schema.php
@@ -523,6 +523,14 @@ class Schema
} else {
$sql .= ($cd->nullable) ? "null " : "not null ";
}
+
+ if (!empty($cd->auto_increment)) {
+ $sql .= " auto_increment ";
+ }
+
+ if (!empty($cd->extra)) {
+ $sql .= "{$cd->extra} ";
+ }
return $sql;
}
diff --git a/lib/util.php b/lib/util.php
index 00d1ab557..a4281121e 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -1409,41 +1409,17 @@ function common_session_token()
function common_cache_key($extra)
{
- $base_key = common_config('memcached', 'base');
-
- if (empty($base_key)) {
- $base_key = common_keyize(common_config('site', 'name'));
- }
-
- return 'statusnet:' . $base_key . ':' . $extra;
+ return Cache::key($extra);
}
function common_keyize($str)
{
- $str = strtolower($str);
- $str = preg_replace('/\s/', '_', $str);
- return $str;
+ return Cache::keyize($str);
}
function common_memcache()
{
- static $cache = null;
- if (!common_config('memcached', 'enabled')) {
- return null;
- } else {
- if (!$cache) {
- $cache = new Memcache();
- $servers = common_config('memcached', 'server');
- if (is_array($servers)) {
- foreach($servers as $server) {
- $cache->addServer($server);
- }
- } else {
- $cache->addServer($servers);
- }
- }
- return $cache;
- }
+ return Cache::instance();
}
function common_license_terms($uri)