diff options
-rw-r--r-- | actions/login.php | 74 | ||||
-rw-r--r-- | actions/otp.php | 145 | ||||
-rw-r--r-- | actions/register.php | 1 | ||||
-rw-r--r-- | classes/File.php | 7 | ||||
-rw-r--r-- | classes/File_oembed.php | 8 | ||||
-rw-r--r-- | classes/Login_token.php | 27 | ||||
-rw-r--r-- | classes/Memcached_DataObject.php | 169 | ||||
-rw-r--r-- | classes/Status_network.php | 25 | ||||
-rw-r--r-- | lib/api.php | 4 | ||||
-rw-r--r-- | lib/command.php | 76 | ||||
-rw-r--r-- | lib/router.php | 5 | ||||
-rw-r--r-- | lib/snapshot.php | 8 | ||||
-rw-r--r-- | lib/util.php | 53 | ||||
-rw-r--r-- | plugins/OpenID/finishopenidlogin.php | 1 |
14 files changed, 396 insertions, 207 deletions
diff --git a/actions/login.php b/actions/login.php index c775fa692..8ea3c800b 100644 --- a/actions/login.php +++ b/actions/login.php @@ -76,15 +76,10 @@ class LoginAction extends Action { parent::handle($args); - $disabled = common_config('logincommand','disabled'); - $disabled = isset($disabled) && $disabled; - if (common_is_real_login()) { $this->clientError(_('Already logged in.')); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->checkLogin(); - } else if (!$disabled && isset($args['user_id']) && isset($args['token'])){ - $this->checkLogin($args['user_id'],$args['token']); } else { common_ensure_session(); $this->showForm(); @@ -103,46 +98,30 @@ class LoginAction extends Action function checkLogin($user_id=null, $token=null) { - if(isset($token) && isset($user_id)){ - //Token based login (from the LoginCommand) - $login_token = Login_token::staticGet('user_id',$user_id); - if($login_token && $login_token->token == $token){ - if($login_token->modified > time()+2*60){ - //token has expired - //delete the token as it is useless - $login_token->delete(); - $this->showForm(_('Invalid or expired token.')); - return; - }else{ - //delete the token so it cannot be reused - $login_token->delete(); - //it's a valid token - let them log in - $user = User::staticGet('id', $user_id); - //$user = User::staticGet('nickname', "candrews"); - } - }else{ - $this->showForm(_('Invalid or expired token.')); - return; - } - }else{ - // Regular form submission login - - // XXX: login throttle - - // CSRF protection - token set in NoticeForm - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->clientError(_('There was a problem with your session token. '. - 'Try again, please.')); - return; - } - - $nickname = $this->trimmed('nickname'); - $password = $this->arg('password'); - - $user = common_check_user($nickname, $password); + // XXX: login throttle + + // CSRF protection - token set in NoticeForm + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $st = common_session_token(); + if (empty($token)) { + common_log(LOG_WARNING, 'No token provided by client.'); + } else if (empty($st)) { + common_log(LOG_WARNING, 'No session token stored.'); + } else { + common_log(LOG_WARNING, 'Token = ' . $token . ' and session token = ' . $st); + } + + $this->clientError(_('There was a problem with your session token. '. + 'Try again, please.')); + return; } + $nickname = $this->trimmed('nickname'); + $password = $this->arg('password'); + + $user = common_check_user($nickname, $password); + if (!$user) { $this->showForm(_('Incorrect username or password.')); return; @@ -165,6 +144,7 @@ class LoginAction extends Action if ($url) { // We don't have to return to it again common_set_returnto(null); + $url = common_inject_session($url); } else { $url = common_local_url('all', array('nickname' => @@ -240,9 +220,9 @@ class LoginAction extends Action function showContent() { $this->elementStart('form', array('method' => 'post', - 'id' => 'form_login', - 'class' => 'form_settings', - 'action' => common_local_url('login'))); + 'id' => 'form_login', + 'class' => 'form_settings', + 'action' => common_local_url('login'))); $this->elementStart('fieldset'); $this->element('legend', null, _('Login to site')); $this->elementStart('ul', 'form_data'); @@ -255,7 +235,7 @@ class LoginAction extends Action $this->elementStart('li'); $this->checkbox('rememberme', _('Remember me'), false, _('Automatically login in the future; ' . - 'not for shared computers!')); + 'not for shared computers!')); $this->elementEnd('li'); $this->elementEnd('ul'); $this->submit('submit', _('Login')); diff --git a/actions/otp.php b/actions/otp.php new file mode 100644 index 000000000..acf84aee8 --- /dev/null +++ b/actions/otp.php @@ -0,0 +1,145 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Allow one-time password login + * + * 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 Login + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Allow one-time password login + * + * This action will automatically log in the user identified by the user_id + * parameter. A login_token record must be constructed beforehand, typically + * by code where the user is already authenticated. + * + * @category Login + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class OtpAction extends Action +{ + var $user; + var $token; + var $rememberme; + var $returnto; + var $lt; + + function prepare($args) + { + parent::prepare($args); + + if (common_is_real_login()) { + $this->clientError(_('Already logged in.')); + return false; + } + + $id = $this->trimmed('user_id'); + + if (empty($id)) { + $this->clientError(_('No user ID specified.')); + return false; + } + + $this->user = User::staticGet('id', $id); + + if (empty($this->user)) { + $this->clientError(_('No such user.')); + return false; + } + + $this->token = $this->trimmed('token'); + + if (empty($this->token)) { + $this->clientError(_('No login token specified.')); + return false; + } + + $this->lt = Login_token::staticGet('user_id', $id); + + if (empty($this->lt)) { + $this->clientError(_('No login token requested.')); + return false; + } + + if ($this->lt->token != $this->token) { + $this->clientError(_('Invalid login token specified.')); + return false; + } + + if ($this->lt->modified > time() + Login_token::TIMEOUT) { + //token has expired + //delete the token as it is useless + $this->lt->delete(); + $this->lt = null; + $this->clientError(_('Login token expired.')); + return false; + } + + $this->rememberme = $this->boolean('rememberme'); + $this->returnto = $this->trimmed('returnto'); + + return true; + } + + function handle($args) + { + parent::handle($args); + + // success! + if (!common_set_user($this->user)) { + $this->serverError(_('Error setting user. You are probably not authorized.')); + return; + } + + // We're now logged in; disable the lt + + $this->lt->delete(); + $this->lt = null; + + if ($this->rememberme) { + common_rememberme($this->user); + } + + if (!empty($this->returnto)) { + $url = $this->returnto; + // We don't have to return to it again + common_set_returnto(null); + } else { + $url = common_local_url('all', + array('nickname' => + $this->user->nickname)); + } + + common_redirect($url, 303); + } +} diff --git a/actions/register.php b/actions/register.php index 96015c219..698137346 100644 --- a/actions/register.php +++ b/actions/register.php @@ -259,6 +259,7 @@ class RegisterAction extends Action // Re-init language env in case it changed (not yet, but soon) common_init_language(); + $this->showSuccess(); } else { $this->showForm(_('Invalid username or password.')); diff --git a/classes/File.php b/classes/File.php index 6173f31d6..c527c4ffe 100644 --- a/classes/File.php +++ b/classes/File.php @@ -80,7 +80,14 @@ class File extends Memcached_DataObject if (isset($redir_data['type']) && (('text/html' === substr($redir_data['type'], 0, 9) || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21))) && ($oembed_data = File_oembed::_getOembed($given_url))) { + + $fo = File_oembed::staticGet('file_id', $file_id); + + if (empty($fo)) { File_oembed::saveNew($oembed_data, $file_id); + } else { + common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file $file_id", __FILE__); + } } return $x; } diff --git a/classes/File_oembed.php b/classes/File_oembed.php index e41ccfd09..11f160718 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -115,7 +115,13 @@ class File_oembed extends Memcached_DataObject } $file_oembed->insert(); if (!empty($data->thumbnail_url)) { - File_thumbnail::saveNew($data, $file_id); + $ft = File_thumbnail::staticGet('file_id', $file_id); + if (!empty($ft)) { + common_log(LOG_WARNING, "Strangely, a File_thumbnail object exists for new file $file_id", + __FILE__); + } else { + File_thumbnail::saveNew($data, $file_id); + } } } } diff --git a/classes/Login_token.php b/classes/Login_token.php index 746cd7f22..51dc61262 100644 --- a/classes/Login_token.php +++ b/classes/Login_token.php @@ -40,6 +40,8 @@ class Login_token extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + const TIMEOUT = 120; // seconds after which to timeout the token + /* DB_DataObject calculates the sequence key(s) by taking the first key returned by the keys() function. In this case, the keys() function returns user_id as the first key. user_id is not a sequence, but @@ -52,4 +54,29 @@ class Login_token extends Memcached_DataObject { return array(false,false); } + + function makeNew($user) + { + $login_token = Login_token::staticGet('user_id', $user->id); + + if (!empty($login_token)) { + $login_token->delete(); + } + + $login_token = new Login_token(); + + $login_token->user_id = $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__); + throw new Exception(sprintf(_('Could not create login token for %s'), + $user->nickname)); + } + + return $login_token; + } } diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 400b05f97..21f6781c2 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -19,6 +19,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + class Memcached_DataObject extends DB_DataObject { /** @@ -66,6 +68,7 @@ class Memcached_DataObject extends DB_DataObject // Clear this out so we don't accidentally break global // state in *this* process. $this->_DB_resultid = null; + // We don't have any local DBO refs, so clear these out. $this->_link_loaded = false; } @@ -90,42 +93,30 @@ class Memcached_DataObject extends DB_DataObject unset($i); } $i = Memcached_DataObject::getcached($cls, $k, $v); - if ($i === false) { // false == cache miss + if ($i) { + return $i; + } else { $i = DB_DataObject::factory($cls); if (empty($i)) { - $i = false; - return $i; + return false; } $result = $i->get($k, $v); if ($result) { - // Hit! $i->encache(); + return $i; } else { - // save the fact that no such row exists - $c = self::memcache(); - if (!empty($c)) { - $ck = self::cachekey($cls, $k, $v); - $c->set($ck, null); - } - $i = false; + return false; } } - return $i; } - /** - * @fixme Should this return false on lookup fail to match staticGet? - */ - function pkeyGet($cls, $kv) + function &pkeyGet($cls, $kv) { $i = Memcached_DataObject::multicache($cls, $kv); - if ($i !== false) { // false == cache miss + if ($i) { return $i; } else { - $i = DB_DataObject::factory($cls); - if (empty($i)) { - return false; - } + $i = new $cls(); foreach ($kv as $k => $v) { $i->$k = $v; } @@ -133,11 +124,6 @@ class Memcached_DataObject extends DB_DataObject $i->encache(); } else { $i = null; - $c = self::memcache(); - if (!empty($c)) { - $ck = self::multicacheKey($cls, $kv); - $c->set($ck, null); - } } return $i; } @@ -146,9 +132,6 @@ class Memcached_DataObject extends DB_DataObject function insert() { $result = parent::insert(); - if ($result) { - $this->encache(); // in case of cached negative lookups - } return $result; } @@ -188,23 +171,21 @@ class Memcached_DataObject extends DB_DataObject if (!$c) { return false; } else { - return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v)); + $obj = $c->get(Memcached_DataObject::cacheKey($cls, $k, $v)); + if (0 == strcasecmp($cls, 'User')) { + // Special case for User + if (is_object($obj->id)) { + common_log(LOG_ERR, "User " . $obj->nickname . " was cached with User as ID; deleting"); + $c->delete(Memcached_DataObject::cacheKey($cls, $k, $v)); + return false; + } + } + return $obj; } } function keyTypes() { - // ini-based classes return number-indexed arrays. handbuilt - // classes return column => keytype. Make this uniform. - - $keys = $this->keys(); - - $keyskeys = array_keys($keys); - - if (is_string($keyskeys[0])) { - return $keys; - } - global $_DB_DATAOBJECT; if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) { $this->databaseStructure(); @@ -216,90 +197,73 @@ class Memcached_DataObject extends DB_DataObject function encache() { $c = $this->memcache(); - if (!$c) { return false; - } - - $keys = $this->_allCacheKeys(); - - foreach ($keys as $key) { - $c->set($key, $this); + } else if ($this->tableName() == 'user' && is_object($this->id)) { + // Special case for User bug + $e = new Exception(); + common_log(LOG_ERR, __METHOD__ . ' caching user with User object as ID ' . + str_replace("\n", " ", $e->getTraceAsString())); + return false; + } else { + $pkey = array(); + $pval = array(); + $types = $this->keyTypes(); + ksort($types); + foreach ($types as $key => $type) { + if ($type == 'K') { + $pkey[] = $key; + $pval[] = $this->$key; + } else { + $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this); + } + } + # XXX: should work for both compound and scalar pkeys + $pvals = implode(',', $pval); + $pkeys = implode(',', $pkey); + $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this); } } function decache() { $c = $this->memcache(); - if (!$c) { return false; - } - - $keys = $this->_allCacheKeys(); - - foreach ($keys as $key) { - $c->delete($key, $this); - } - } - - function _allCacheKeys() - { - $ckeys = array(); - - $types = $this->keyTypes(); - ksort($types); - - $pkey = array(); - $pval = array(); - - foreach ($types as $key => $type) { - - assert(!empty($key)); - - if ($type == 'U') { - if (empty($this->$key)) { - continue; + } else { + $pkey = array(); + $pval = array(); + $types = $this->keyTypes(); + ksort($types); + foreach ($types as $key => $type) { + if ($type == 'K') { + $pkey[] = $key; + $pval[] = $this->$key; + } else { + $c->delete($this->cacheKey($this->tableName(), $key, $this->$key)); } - $ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key); - } else if ($type == 'K' || $type == 'N') { - $pkey[] = $key; - $pval[] = $this->$key; - } else { - throw new Exception("Unknown key type $key => $type for " . $this->tableName()); } + # should work for both compound and scalar pkeys + # XXX: comma works for now but may not be safe separator for future keys + $pvals = implode(',', $pval); + $pkeys = implode(',', $pkey); + $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals)); } - - assert(count($pkey) > 0); - - // XXX: should work for both compound and scalar pkeys - $pvals = implode(',', $pval); - $pkeys = implode(',', $pkey); - - $ckeys[] = $this->cacheKey($this->tableName(), $pkeys, $pvals); - - return $ckeys; } function multicache($cls, $kv) { ksort($kv); - $c = self::memcache(); + $c = Memcached_DataObject::memcache(); if (!$c) { return false; } else { - return $c->get(self::multicacheKey($cls, $kv)); + $pkeys = implode(',', array_keys($kv)); + $pvals = implode(',', array_values($kv)); + return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals)); } } - static function multicacheKey($cls, $kv) - { - ksort($kv); - $pkeys = implode(',', array_keys($kv)); - $pvals = implode(',', array_values($kv)); - return self::cacheKey($cls, $pkeys, $pvals); - } - function getSearchEngine($table) { require_once INSTALLDIR.'/lib/search_engines.php'; @@ -334,8 +298,7 @@ class Memcached_DataObject extends DB_DataObject $key_part = common_keyize($cls).':'.md5($qry); $ckey = common_cache_key($key_part); $stored = $c->get($ckey); - - if ($stored !== false) { + if ($stored) { return new ArrayWrapper($stored); } diff --git a/classes/Status_network.php b/classes/Status_network.php index b3117640d..776f6abb0 100644 --- a/classes/Status_network.php +++ b/classes/Status_network.php @@ -150,9 +150,19 @@ class Status_network extends DB_DataObject } if (!empty($sn)) { - if (!empty($sn->hostname) && 0 != strcasecmp($sn->hostname, $servername)) { - $sn->redirectToHostname(); + + // Redirect to the right URL + + if (!empty($sn->hostname) && + empty($_SERVER['HTTPS']) && + 0 != strcasecmp($sn->hostname, $servername)) { + $sn->redirectTo('http://'.$sn->hostname.$_SERVER['REQUEST_URI']); + } else if (!empty($_SERVER['HTTPS']) && + 0 != strcasecmp($sn->hostname, $servername) && + 0 != strcasecmp($sn->nickname.'.'.$wildcard, $servername)) { + $sn->redirectTo('https://'.$sn->nickname.'.'.$wildcard.$_SERVER['REQUEST_URI']); } + $dbhost = (empty($sn->dbhost)) ? 'localhost' : $sn->dbhost; $dbuser = (empty($sn->dbuser)) ? $sn->nickname : $sn->dbuser; $dbpass = $sn->dbpass; @@ -160,7 +170,11 @@ class Status_network extends DB_DataObject $config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname"; - $config['site']['name'] = $sn->sitename; + $config['site']['name'] = $sn->sitename; + + if (!empty($sn->hostname)) { + $config['site']['server'] = $sn->hostname; + } if (!empty($sn->theme)) { $config['site']['theme'] = $sn->theme; @@ -179,11 +193,8 @@ class Status_network extends DB_DataObject // (C) 2006 by Heiko Richler http://www.richler.de/ // LGPL - function redirectToHostname() + function redirectTo($destination) { - $destination = 'http://'.$this->hostname; - $destination .= $_SERVER['REQUEST_URI']; - $old = 'http'. (($_SERVER['HTTPS'] == 'on') ? 'S' : ''). '://'. diff --git a/lib/api.php b/lib/api.php index a6aea5d6d..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); diff --git a/lib/command.php b/lib/command.php index 5a1a8bf33..f846fb823 100644 --- a/lib/command.php +++ b/lib/command.php @@ -85,7 +85,7 @@ class NudgeCommand extends Command { $recipient = User::staticGet('nickname', $this->other); if(! $recipient){ - $channel->error($this->user, sprintf(_('Could not find a user with nickname %s.'), + $channel->error($this->user, sprintf(_('Could not find a user with nickname %s'), $this->other)); }else{ if ($recipient->id == $this->user->id) { @@ -96,7 +96,7 @@ class NudgeCommand extends Command } // XXX: notify by IM // XXX: notify by SMS - $channel->output($this->user, sprintf(_('Nudge sent to %s.'), + $channel->output($this->user, sprintf(_('Nudge sent to %s'), $recipient->nickname)); } } @@ -149,7 +149,7 @@ class FavCommand extends Command $notice = Notice::staticGet(substr($this->other,1)); if (!$notice) { - $channel->error($this->user, _('Notice with that id does not exist.')); + $channel->error($this->user, _('Notice with that id does not exist')); return; } $recipient = $notice->getProfile(); @@ -165,7 +165,7 @@ class FavCommand extends Command } $notice = $recipient->getCurrentNotice(); if (!$notice) { - $channel->error($this->user, _('User has no last notice.')); + $channel->error($this->user, _('User has no last notice')); return; } } @@ -214,7 +214,7 @@ class JoinCommand extends Command } if ($cur->isMember($group)) { - $channel->error($cur, _('You are already a member of that group.')); + $channel->error($cur, _('You are already a member of that group')); return; } if (Group_block::isBlocked($group, $cur->getProfile())) { @@ -231,12 +231,12 @@ class JoinCommand extends Command $result = $member->insert(); if (!$result) { common_log_db_error($member, 'INSERT', __FILE__); - $channel->error($cur, sprintf(_('Could not join user %1$s to group %2$s.'), + $channel->error($cur, sprintf(_('Could not join user %s to group %s'), $cur->nickname, $group->nickname)); return; } - $channel->output($cur, sprintf(_('%1$s joined group %2$s'), + $channel->output($cur, sprintf(_('%s joined group %s'), $cur->nickname, $group->nickname)); } @@ -281,12 +281,12 @@ class DropCommand extends Command $result = $member->delete(); if (!$result) { common_log_db_error($member, 'INSERT', __FILE__); - $channel->error($cur, sprintf(_('Could not remove user %1$s to group %2$s.'), + $channel->error($cur, sprintf(_('Could not remove user %s to group %s'), $cur->nickname, $group->nickname)); return; } - $channel->output($cur, sprintf(_('%1$s left group %2$s'), + $channel->output($cur, sprintf(_('%s left group %s'), $cur->nickname, $group->nickname)); } @@ -315,7 +315,7 @@ class WhoisCommand extends Command $whois = sprintf(_("%1\$s (%2\$s)"), $recipient->nickname, $recipient->profileurl); if ($recipient->fullname) { - $whois .= "\n" . sprintf(_('Full name: %s'), $recipient->fullname); + $whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname); } if ($recipient->location) { $whois .= "\n" . sprintf(_('Location: %s'), $recipient->location); @@ -355,7 +355,7 @@ class MessageCommand extends Command $this->text = common_shorten_links($this->text); if (Message::contentTooLong($this->text)) { - $channel->error($this->user, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d.'), + $channel->error($this->user, sprintf(_('Message too long - maximum is %d characters, you sent %d'), Message::maxContent(), mb_strlen($this->text))); return; } @@ -373,7 +373,7 @@ class MessageCommand extends Command $message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source()); if ($message) { $message->notify(); - $channel->output($this->user, sprintf(_('Direct message to %s sent.'), $this->other)); + $channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other)); } else { $channel->error($this->user, _('Error sending direct message.')); } @@ -396,7 +396,7 @@ class RepeatCommand extends Command $notice = Notice::staticGet(substr($this->other,1)); if (!$notice) { - $channel->error($this->user, _('Notice with that id does not exist.')); + $channel->error($this->user, _('Notice with that id does not exist')); return; } $recipient = $notice->getProfile(); @@ -412,19 +412,19 @@ class RepeatCommand extends Command } $notice = $recipient->getCurrentNotice(); if (!$notice) { - $channel->error($this->user, _('User has no last notice.')); + $channel->error($this->user, _('User has no last notice')); return; } } if($this->user->id == $notice->profile_id) { - $channel->error($this->user, _('Cannot repeat your own notice.')); + $channel->error($this->user, _('Cannot repeat your own notice')); return; } if ($recipient->hasRepeated($notice->id)) { - $channel->error($this->user, _('Already repeated that notice.')); + $channel->error($this->user, _('Already repeated that notice')); return; } @@ -432,7 +432,7 @@ class RepeatCommand extends Command if ($repeat) { common_broadcast_notice($repeat); - $channel->output($this->user, sprintf(_('Notice from %s repeated.'), $recipient->nickname)); + $channel->output($this->user, sprintf(_('Notice from %s repeated'), $recipient->nickname)); } else { $channel->error($this->user, _('Error repeating notice.')); } @@ -457,7 +457,7 @@ class ReplyCommand extends Command $notice = Notice::staticGet(substr($this->other,1)); if (!$notice) { - $channel->error($this->user, _('Notice with that id does not exist.')); + $channel->error($this->user, _('Notice with that id does not exist')); return; } $recipient = $notice->getProfile(); @@ -473,7 +473,7 @@ class ReplyCommand extends Command } $notice = $recipient->getCurrentNotice(); if (!$notice) { - $channel->error($this->user, _('User has no last notice.')); + $channel->error($this->user, _('User has no last notice')); return; } } @@ -488,7 +488,7 @@ class ReplyCommand extends Command $this->text = common_shorten_links($this->text); if (Notice::contentTooLong($this->text)) { - $channel->error($this->user, sprintf(_('Notice too long - maximum is %1$d characters, you sent %2$d.'), + $channel->error($this->user, sprintf(_('Notice too long - maximum is %d characters, you sent %d'), Notice::maxContent(), mb_strlen($this->text))); return; } @@ -497,7 +497,7 @@ class ReplyCommand extends Command array('reply_to' => $notice->id)); if ($notice) { - $channel->output($this->user, sprintf(_('Reply to %s sent.'), $recipient->nickname)); + $channel->output($this->user, sprintf(_('Reply to %s sent'), $recipient->nickname)); } else { $channel->error($this->user, _('Error saving notice.')); } @@ -529,7 +529,7 @@ class GetCommand extends Command } $notice = $target->getCurrentNotice(); if (!$notice) { - $channel->error($this->user, _('User has no last notice.')); + $channel->error($this->user, _('User has no last notice')); return; } $notice_content = $notice->content; @@ -553,7 +553,7 @@ class SubCommand extends Command { if (!$this->other) { - $channel->error($this->user, _('Specify the name of the user to subscribe to.')); + $channel->error($this->user, _('Specify the name of the user to subscribe to')); return; } @@ -581,7 +581,7 @@ class UnsubCommand extends Command function execute($channel) { if(!$this->other) { - $channel->error($this->user, _('Specify the name of the user to unsubscribe from.')); + $channel->error($this->user, _('Specify the name of the user to unsubscribe from')); return; } @@ -647,28 +647,20 @@ class LoginCommand extends Command $disabled = common_config('logincommand','disabled'); $disabled = isset($disabled) && $disabled; if($disabled) { - $channel->error($this->user, _('Login command is disabled.')); + $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)))); + sprintf(_('This link is useable only once, and is good for only 2 minutes: %s'), + common_local_url('otp', + array('user_id' => $login_token->user_id, 'token' => $login_token->token)))); } } diff --git a/lib/router.php b/lib/router.php index 785e78fd0..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 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/util.php b/lib/util.php index 50bd0e2ac..1237d718b 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 diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php index 987fa9213..d25ce696c 100644 --- a/plugins/OpenID/finishopenidlogin.php +++ b/plugins/OpenID/finishopenidlogin.php @@ -363,6 +363,7 @@ class FinishopenidloginAction extends Action if ($url) { # We don't have to return to it again common_set_returnto(null); + $url = common_inject_session($url); } else { $url = common_local_url('all', array('nickname' => |