summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/action.php5
-rw-r--r--lib/adminpanelaction.php46
-rw-r--r--lib/api.php6
-rw-r--r--lib/authenticationplugin.php231
-rw-r--r--lib/authorizationplugin.php105
-rw-r--r--lib/cache.php6
-rw-r--r--lib/command.php24
-rw-r--r--lib/common.php23
-rw-r--r--lib/default.php20
-rw-r--r--lib/htmloutputter.php2
-rw-r--r--lib/jabber.php2
-rw-r--r--lib/jsonsearchresultslist.php10
-rw-r--r--lib/mail.php7
-rw-r--r--lib/mailhandler.php275
-rw-r--r--lib/mediafile.php12
-rw-r--r--lib/noticeform.php6
-rw-r--r--lib/noticelist.php2
-rw-r--r--lib/ping.php4
-rw-r--r--lib/plugin.php11
-rw-r--r--lib/router.php17
-rw-r--r--lib/schema.php4
-rw-r--r--lib/snapshot.php8
-rw-r--r--lib/subscriptionlist.php2
-rw-r--r--lib/util.php60
24 files changed, 822 insertions, 66 deletions
diff --git a/lib/action.php b/lib/action.php
index 35df03566..6efa9163d 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -141,7 +141,7 @@ class Action extends HTMLOutputter // lawsuit
function showTitle()
{
$this->element('title', null,
- sprintf(_("%s - %s"),
+ sprintf(_("%1$s - %2$s"),
$this->title(),
common_config('site', 'name')));
}
@@ -253,6 +253,7 @@ class Action extends HTMLOutputter // lawsuit
$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));
}
@@ -736,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/api.php b/lib/api.php
index 4ed49e452..d21851d50 100644
--- a/lib/api.php
+++ b/lib/api.php
@@ -140,12 +140,14 @@ class ApiAction extends Action
// Note: some profiles don't have an associated user
+ $defaultDesign = Design::siteDesign();
+
if (!empty($user)) {
$design = $user->getDesign();
}
if (empty($design)) {
- $design = Design::siteDesign();
+ $design = $defaultDesign;
}
$color = Design::toWebColor(empty($design->backgroundcolor) ? $defaultDesign->backgroundcolor : $design->backgroundcolor);
@@ -787,7 +789,7 @@ class ApiAction extends Action
$from = $message->getFrom();
- $entry['title'] = sprintf('Message from %s to %s',
+ $entry['title'] = sprintf('Message from %1$s to %2$s',
$from->nickname, $message->getTo()->nickname);
$entry['content'] = common_xml_safe_str($message->rendered);
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
index bac3499e5..b7b34c050 100644
--- a/lib/cache.php
+++ b/lib/cache.php
@@ -120,7 +120,7 @@ class Cache
if (Event::handle('StartCacheGet', array(&$key, &$value))) {
if (array_key_exists($key, $this->_items)) {
- $value = $this->_items[$key];
+ $value = unserialize($this->_items[$key]);
}
Event::handle('EndCacheGet', array($key, &$value));
}
@@ -146,7 +146,7 @@ class Cache
if (Event::handle('StartCacheSet', array(&$key, &$value, &$flag,
&$expiry, &$success))) {
- $this->_items[$key] = $value;
+ $this->_items[$key] = serialize($value);
$success = true;
@@ -170,7 +170,7 @@ class Cache
$success = false;
if (Event::handle('StartCacheDelete', array(&$key, &$success))) {
- if (array_key_exists($key, $this->_items[$key])) {
+ if (array_key_exists($key, $this->_items)) {
unset($this->_items[$key]);
}
$success = true;
diff --git a/lib/command.php b/lib/command.php
index 67140c348..f846fb823 100644
--- a/lib/command.php
+++ b/lib/command.php
@@ -650,25 +650,17 @@ class LoginCommand extends Command
$channel->error($this->user, _('Login command is disabled'));
return;
}
- $login_token = Login_token::staticGet('user_id',$this->user->id);
- if($login_token){
- $login_token->delete();
- }
- $login_token = new Login_token();
- $login_token->user_id = $this->user->id;
- $login_token->token = common_good_rand(16);
- $login_token->created = common_sql_now();
- $result = $login_token->insert();
- if (!$result) {
- common_log_db_error($login_token, 'INSERT', __FILE__);
- $channel->error($this->user, sprintf(_('Could not create login token for %s'),
- $this->user->nickname));
- return;
+
+ try {
+ $login_token = Login_token::makeNew($this->user);
+ } catch (Exception $e) {
+ $channel->error($this->user, $e->getMessage());
}
+
$channel->output($this->user,
sprintf(_('This link is useable only once, and is good for only 2 minutes: %s'),
- common_local_url('login',
- array('user_id'=>$login_token->user_id, 'token'=>$login_token->token))));
+ common_local_url('otp',
+ array('user_id' => $login_token->user_id, 'token' => $login_token->token))));
}
}
diff --git a/lib/common.php b/lib/common.php
index b0e5c4390..7342c177a 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -197,7 +197,7 @@ function _have_config()
// XXX: Find a way to use htmlwriter for this instead of handcoded markup
if (!_have_config()) {
echo '<p>'. _('No configuration file found. ') .'</p>';
- echo '<p>'. _('I looked for configuration files in the following places: ') .'<br/> '. implode($_config_files, '<br/>');
+ echo '<p>'. _('I looked for configuration files in the following places: ') .'<br /> '. implode($_config_files, '<br />');
echo '<p>'. _('You may wish to run the installer to fix this.') .'</p>';
echo '<a href="install.php">'. _('Go to the installer.') .'</a>';
exit;
@@ -238,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 eea11eb2b..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',
@@ -229,4 +230,23 @@ $default =
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/jsonsearchresultslist.php b/lib/jsonsearchresultslist.php
index 569bfa873..0d72ddf7a 100644
--- a/lib/jsonsearchresultslist.php
+++ b/lib/jsonsearchresultslist.php
@@ -105,8 +105,14 @@ class JSONSearchResultsList
break;
}
- $item = new ResultItem($this->notice);
- array_push($this->results, $item);
+ $profile = $this->notice->getProfile();
+
+ // Don't show notices from deleted users
+
+ if (!empty($profile)) {
+ $item = new ResultItem($this->notice);
+ array_push($this->results, $item);
+ }
}
$time_end = microtime(true);
diff --git a/lib/mail.php b/lib/mail.php
index 472a88e06..c724764cc 100644
--- a/lib/mail.php
+++ b/lib/mail.php
@@ -251,11 +251,11 @@ function mail_subscribe_notify_profile($listenee, $other)
common_config('site', 'name'),
$other->profileurl,
($other->location) ?
- sprintf(_("Location: %s\n"), $other->location) : '',
+ sprintf(_("Location: %s"), $other->location) . "\n" : '',
($other->homepage) ?
- sprintf(_("Homepage: %s\n"), $other->homepage) : '',
+ sprintf(_("Homepage: %s"), $other->homepage) . "\n" : '',
($other->bio) ?
- sprintf(_("Bio: %s\n\n"), $other->bio) : '',
+ sprintf(_("Bio: %s"), $other->bio) . "\n\n" : '',
common_config('site', 'name'),
common_local_url('emailsettings'));
@@ -652,4 +652,3 @@ function mail_notify_attn($user, $notice)
common_init_locale();
mail_to_user($user, $subject, $body);
}
-
diff --git a/lib/mailhandler.php b/lib/mailhandler.php
new file mode 100644
index 000000000..85be89f18
--- /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, sprintf(_('Unsupported message type: %s'), $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/mediafile.php b/lib/mediafile.php
index 29d752f0c..e3d5b1dbc 100644
--- a/lib/mediafile.php
+++ b/lib/mediafile.php
@@ -176,7 +176,7 @@ class MediaFile
// Should never actually get here
@unlink($_FILES[$param]['tmp_name']);
- throw new ClientException(_('File exceeds user\'s quota!'));
+ throw new ClientException(_('File exceeds user\'s quota.'));
return;
}
@@ -198,7 +198,7 @@ class MediaFile
}
} else {
- throw new ClientException(_('Could not determine file\'s mime-type!'));
+ throw new ClientException(_('Could not determine file\'s MIME type.'));
return;
}
@@ -213,7 +213,7 @@ class MediaFile
// Should never actually get here
- throw new ClientException(_('File exceeds user\'s quota!'));
+ throw new ClientException(_('File exceeds user\'s quota.'));
return;
}
@@ -234,7 +234,7 @@ class MediaFile
$stream['uri'] . ' ' . $filepath));
}
} else {
- throw new ClientException(_('Could not determine file\'s mime-type!'));
+ throw new ClientException(_('Could not determine file\'s MIME type.'));
return;
}
@@ -272,7 +272,7 @@ class MediaFile
$hint = '';
}
throw new ClientException(sprintf(
- _('%s is not a supported filetype on this server.'), $filetype) . $hint);
+ _('%s is not a supported file type on this server.'), $filetype) . $hint);
}
static function respectsQuota($user, $filesize)
@@ -286,4 +286,4 @@ class MediaFile
}
}
-} \ No newline at end of file
+}
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 99865645a..02e35a8d7 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -207,10 +207,12 @@ class NoticeForm extends Form
$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-location_wrap',
+ $this->out->elementStart('div', array('id' => 'notice_data-geo_wrap',
'title' => common_local_url('geocode')));
- $this->out->checkbox('notice_data-geo', _('Share your location'), true);
+ $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/router.php b/lib/router.php
index 7ec962460..6b87ed27f 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -88,7 +88,10 @@ class Router
$m->connect('doc/:title', array('action' => 'doc'));
- $m->connect('main/login?user_id=:user_id&token=:token', array('action'=>'login'), array('user_id'=> '[0-9]+', 'token'=>'.+'));
+ $m->connect('main/otp/:user_id/:token',
+ array('action' => 'otp'),
+ array('user_id' => '[0-9]+',
+ 'token' => '.+'));
// main stuff is repetitive
@@ -101,7 +104,9 @@ class Router
'silence', 'unsilence',
'repeat',
'deleteuser',
- 'geocode');
+ 'geocode',
+ 'version',
+ );
foreach ($main as $a) {
$m->connect('main/'.$a, array('action' => $a));
@@ -440,19 +445,19 @@ class Router
// Social graph
$m->connect('api/friends/ids/:id.:format',
- array('action' => 'apiFriends',
+ array('action' => 'apiuserfriends',
'ids_only' => true));
$m->connect('api/followers/ids/:id.:format',
- array('action' => 'apiFollowers',
+ array('action' => 'apiuserfollowers',
'ids_only' => true));
$m->connect('api/friends/ids.:format',
- array('action' => 'apiFriends',
+ array('action' => 'apiuserfriends',
'ids_only' => true));
$m->connect('api/followers/ids.:format',
- array('action' => 'apiFollowers',
+ array('action' => 'apiuserfollowers',
'ids_only' => true));
// account
diff --git a/lib/schema.php b/lib/schema.php
index 6fe442d56..a7f64ebed 100644
--- a/lib/schema.php
+++ b/lib/schema.php
@@ -528,6 +528,10 @@ class Schema
$sql .= " auto_increment ";
}
+ if (!empty($cd->extra)) {
+ $sql .= "{$cd->extra} ";
+ }
+
return $sql;
}
}
diff --git a/lib/snapshot.php b/lib/snapshot.php
index 2a10c6b93..a16087ac0 100644
--- a/lib/snapshot.php
+++ b/lib/snapshot.php
@@ -173,8 +173,12 @@ class Snapshot
// XXX: Use OICU2 and OAuth to make authorized requests
$reporturl = common_config('snapshot', 'reporturl');
- $request = HTTPClient::start();
- $request->post($reporturl, null, $this->stats);
+ try {
+ $request = HTTPClient::start();
+ $request->post($reporturl, null, $this->stats);
+ } catch (Exception $e) {
+ common_log(LOG_WARNING, "Error in snapshot: " . $e->getMessage());
+ }
}
/**
diff --git a/lib/subscriptionlist.php b/lib/subscriptionlist.php
index 89f63e321..e1207774f 100644
--- a/lib/subscriptionlist.php
+++ b/lib/subscriptionlist.php
@@ -123,7 +123,7 @@ class SubscriptionListItem extends ProfileListItem
}
$this->out->elementEnd('ul');
} else {
- $this->out->text(_('(none)'));
+ $this->out->text(_('(None)'));
}
$this->out->elementEnd('dd');
$this->out->elementEnd('dl');
diff --git a/lib/util.php b/lib/util.php
index 50bd0e2ac..3e52f5db1 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -119,6 +119,11 @@ function common_language()
function common_munge_password($password, $id)
{
+ if (is_object($id) || is_object($password)) {
+ $e = new Exception();
+ common_log(LOG_ERR, __METHOD__ . ' object in param to common_munge_password ' .
+ str_replace("\n", " ", $e->getTraceAsString()));
+ }
return md5($password . $id);
}
@@ -166,15 +171,27 @@ function common_ensure_session()
if (common_config('sessions', 'handle')) {
Session::setSaveHandler();
}
+ if (array_key_exists(session_name(), $_GET)) {
+ $id = $_GET[session_name()];
+ common_log(LOG_INFO, 'Setting session from GET parameter: '.$id);
+ } else if (array_key_exists(session_name(), $_COOKIE)) {
+ $id = $_COOKIE[session_name()];
+ common_log(LOG_INFO, 'Setting session from COOKIE: '.$id);
+ }
+ if (isset($id)) {
+ session_id($id);
+ setcookie(session_name(), $id);
+ }
@session_start();
if (!isset($_SESSION['started'])) {
$_SESSION['started'] = time();
- if (!empty($c)) {
+ if (!empty($id)) {
common_log(LOG_WARNING, 'Session cookie "' . $_COOKIE[session_name()] . '" ' .
' is set but started value is null');
}
}
}
+ common_debug("Session ID = " . session_id());
}
// Three kinds of arguments:
@@ -809,20 +826,50 @@ function common_path($relative, $ssl=false)
} else if (common_config('site', 'server')) {
$serverpart = common_config('site', 'server');
} else {
- common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.');
+ common_log(LOG_ERR, 'Site server not configured, unable to determine site name.');
}
} else {
$proto = 'http';
if (common_config('site', 'server')) {
$serverpart = common_config('site', 'server');
} else {
- common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.');
+ common_log(LOG_ERR, 'Site server not configured, unable to determine site name.');
}
}
+ $relative = common_inject_session($relative, $serverpart);
+
return $proto.'://'.$serverpart.'/'.$pathpart.$relative;
}
+function common_inject_session($url, $serverpart = null)
+{
+ if (common_have_session()) {
+
+ if (empty($serverpart)) {
+ $serverpart = parse_url($url, PHP_URL_HOST);
+ }
+
+ $currentServer = $_SERVER['HTTP_HOST'];
+
+ // Are we pointing to another server (like an SSL server?)
+
+ if (!empty($currentServer) &&
+ 0 != strcasecmp($currentServer, $serverpart)) {
+ // Pass the session ID as a GET parameter
+ $sesspart = session_name() . '=' . session_id();
+ $i = strpos($url, '?');
+ if ($i === false) { // no GET params, just append
+ $url .= '?' . $sesspart;
+ } else {
+ $url = substr($url, 0, $i + 1).$sesspart.'&'.substr($url, $i + 1);
+ }
+ }
+ }
+
+ return $url;
+}
+
function common_date_string($dt)
{
// XXX: do some sexy date formatting
@@ -1010,7 +1057,12 @@ function common_profile_url($nickname)
function common_root_url($ssl=false)
{
- return common_path('', $ssl);
+ $url = common_path('', $ssl);
+ $i = strpos($url, '?');
+ if ($i !== false) {
+ $url = substr($url, 0, $i);
+ }
+ return $url;
}
// returns $bytes bytes of random data as a hexadecimal string