From 06d918d575cfb112b8719b0441548d55e679fe51 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 5 Oct 2010 01:21:50 +0000 Subject: Strip out the special 'p' paramter added by index.php from $_SERVER['QUERY_STRING'] before doing OAuth requests. Required by the latest version of the OAuth lib. --- lib/apioauth.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/apioauth.php b/lib/apioauth.php index 1c87e4232..3f71de0c3 100644 --- a/lib/apioauth.php +++ b/lib/apioauth.php @@ -86,11 +86,18 @@ class ApiOauthAction extends Action } // strip out the p param added in index.php - - // XXX: should we strip anything else? Or alternatively - // only allow a known list of params? unset($_GET['p']); unset($_POST['p']); + unset($_REQUEST['p']); + + $queryArray = explode('&', $_SERVER['QUERY_STRING']); + for ($i = 0; $i < sizeof($queryArray); $i++) { + if (substr($queryArray[$i], 0, 1) == 'p=') { + unset($queryArray[$i]); + } + } + + $_SERVER['QUERY_STRING'] = implode('&', $queryString); } function getCallback($url, $params) -- cgit v1.2.3-54-g00ecf From 83566f014c378bd1b6ebad936e98e4e76b3b731b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 5 Oct 2010 16:13:07 -0700 Subject: Fix bad reference --- lib/apioauth.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/apioauth.php b/lib/apioauth.php index 3f71de0c3..6a9ab6377 100644 --- a/lib/apioauth.php +++ b/lib/apioauth.php @@ -30,7 +30,7 @@ if (!defined('STATUSNET')) { exit(1); } - +require_once INSTALLDIR . '/lib/apiaction.php'; require_once INSTALLDIR . '/lib/apioauthstore.php'; /** @@ -44,7 +44,7 @@ require_once INSTALLDIR . '/lib/apioauthstore.php'; * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ -class ApiOauthAction extends Action +class ApiOauthAction extends ApiAction { /** * Is this a read-only action? @@ -91,13 +91,14 @@ class ApiOauthAction extends Action unset($_REQUEST['p']); $queryArray = explode('&', $_SERVER['QUERY_STRING']); + for ($i = 0; $i < sizeof($queryArray); $i++) { if (substr($queryArray[$i], 0, 1) == 'p=') { unset($queryArray[$i]); } } - $_SERVER['QUERY_STRING'] = implode('&', $queryString); + $_SERVER['QUERY_STRING'] = implode('&', $queryArray); } function getCallback($url, $params) @@ -120,4 +121,5 @@ class ApiOauthAction extends Action return ($url . '&' . $k . '=' . $v); } } + } -- cgit v1.2.3-54-g00ecf From 4247be51164706af31b2d2a55807a05d89d31fb1 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 5 Oct 2010 17:09:57 -0700 Subject: Add plain text error format to clientError() --- lib/apiaction.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/apiaction.php b/lib/apiaction.php index 0ebf88282..afba8ab63 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -1244,23 +1244,29 @@ class ApiAction extends Action // Do not emit error header for JSONP if (!isset($this->callback)) { - header('HTTP/1.1 '.$code.' '.$status_string); + header('HTTP/1.1 ' . $code . ' ' . $status_string); } - if ($format == 'xml') { + switch($format) { + case 'xml': $this->initDocument('xml'); $this->elementStart('hash'); $this->element('error', null, $msg); $this->element('request', null, $_SERVER['REQUEST_URI']); $this->elementEnd('hash'); $this->endDocument('xml'); - } elseif ($format == 'json'){ + break; + case 'json': $this->initDocument('json'); $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); print(json_encode($error_array)); $this->endDocument('json'); - } else { - + break; + case 'text': + header('Content-Type: text/plain; charset=utf-8'); + print $msg; + break; + default: // If user didn't request a useful format, throw a regular client error throw new ClientException($msg, $code); } -- cgit v1.2.3-54-g00ecf From 63663dbd0e4e5c3dd1139b7bdc82ec46a9f27525 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 5 Oct 2010 17:27:55 -0700 Subject: Stab that 'p' parameter! --- lib/apioauth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/apioauth.php b/lib/apioauth.php index 6a9ab6377..b2d8faa5a 100644 --- a/lib/apioauth.php +++ b/lib/apioauth.php @@ -93,7 +93,7 @@ class ApiOauthAction extends ApiAction $queryArray = explode('&', $_SERVER['QUERY_STRING']); for ($i = 0; $i < sizeof($queryArray); $i++) { - if (substr($queryArray[$i], 0, 1) == 'p=') { + if (substr($queryArray[$i], 0, 2) == 'p=') { unset($queryArray[$i]); } } -- cgit v1.2.3-54-g00ecf From f4f56eea3ae349ed7d47ae7385036107510bf5e5 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 5 Oct 2010 17:48:32 -0700 Subject: Override new_request_token() to store OAuth 1.0a verified callback URL --- lib/apioauthstore.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'lib') diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index eca93866f..620f0947f 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -183,4 +183,30 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore throw new Exception(_('Failed to delete revoked token.')); } } + + /* + * Create a new request token. Overrided to support OAuth 1.0a callback + * + * @param OAuthConsumer $consumer the OAuth Consumer for this token + * @param string $callback the verified OAuth callback URL + * + * @return OAuthToken $token a new unauthorized OAuth request token + */ + + function new_request_token($consumer, $callback) + { + $t = new Token(); + $t->consumer_key = $consumer->key; + $t->tok = common_good_rand(16); + $t->secret = common_good_rand(16); + $t->type = 0; // request + $t->state = 0; // unauthorized + $t->verified_callback = $callback; + $t->created = DB_DataObject_Cast::dateTime(); + if (!$t->insert()) { + return null; + } else { + return new OAuthToken($t->tok, $t->secret); + } + } } -- cgit v1.2.3-54-g00ecf From f97b863fd709135fb9f7bf7c756a1c6721e3e988 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 5 Oct 2010 17:53:50 -0700 Subject: Update ApiOauthRequestTokenAction to support OAuth 1.0a --- actions/apioauthrequesttoken.php | 74 +++++++++++++++++++++++++++++++++------- lib/apioauth.php | 5 +++ 2 files changed, 67 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php index 4fa626d86..4f4c2c8fb 100644 --- a/actions/apioauthrequesttoken.php +++ b/actions/apioauthrequesttoken.php @@ -2,7 +2,7 @@ /** * StatusNet, the distributed open-source microblogging tool * - * Get an OAuth request token + * Issue temporary OAuth credentials (a request token) * * PHP version 5 * @@ -34,7 +34,7 @@ if (!defined('STATUSNET')) { require_once INSTALLDIR . '/lib/apioauth.php'; /** - * Get an OAuth request token + * Issue temporary OAuth credentials (a request token) * * @category API * @package StatusNet @@ -58,22 +58,23 @@ class ApiOauthRequestTokenAction extends ApiOauthAction { parent::prepare($args); - $this->callback = $this->arg('oauth_callback'); - - if (!empty($this->callback)) { - common_debug("callback: $this->callback"); - } + // XXX: support "force_login" parameter like Twitter? (Forces the user to enter + // their credentials to ensure the correct users account is authorized.) return true; } /** - * Class handler. + * Handle a request for temporary OAuth credentials + * + * Make sure the request is kosher, then emit a set of temporary + * credentials -- AKA an unauthorized request token. * * @param array $args array of arguments * * @return void */ + function handle($args) { parent::handle($args); @@ -85,14 +86,63 @@ class ApiOauthRequestTokenAction extends ApiOauthAction $server->add_signature_method($hmac_method); try { + $req = OAuthRequest::from_request(); + + // verify callback + if (!$this->verifyCallback($req->get_parameter('oauth_callback'))) { + throw new OAuthException( + "You must provide a valid URL or 'oob' in oauth_callback.", + 400 + ); + } + + // check signature and issue a new request token $token = $server->fetch_request_token($req); - print $token; + + // return token to the client + $this->showRequestToken($token); + } catch (OAuthException $e) { common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); - header('HTTP/1.1 401 Unauthorized'); - header('Content-Type: text/html; charset=utf-8'); - print $e->getMessage() . "\n"; + + // Return 401 for for bad credentials or signature problems, + // and 400 for missing or unsupported parameters + + $code = $e->getCode(); + $this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text'); + } + } + + /* + * Display temporary OAuth credentials + */ + + function showRequestToken($token) + { + header('Content-Type: application/x-www-form-urlencoded'); + print $token; + print '&oauth_callback_confirmed=true'; + } + + /* Make sure the callback parameter contains either a real URL + * or the string 'oob'. + * + * @todo Check for evil/banned URLs here + * + * @return boolean true or false + */ + + function verifyCallback($callback) + { + if ($callback == "oob") { + common_debug("OAuth request token requested for out of bounds client."); + return true; + } else { + return Validate::uri( + $callback, + array('allowed_schemes' => array('http', 'https')) + ); } } diff --git a/lib/apioauth.php b/lib/apioauth.php index b2d8faa5a..75b0b3c57 100644 --- a/lib/apioauth.php +++ b/lib/apioauth.php @@ -77,6 +77,11 @@ class ApiOauthAction extends ApiAction self::cleanRequest(); } + /* + * Clean up the request so the OAuth library doesn't find + * any extra parameters or anything else it's not expecting. + * I'm looking at you, p parameter. + */ static function cleanRequest() { // kill evil effects of magical slashing -- cgit v1.2.3-54-g00ecf From f71912440a17f468b1d60db2388fc6030631fce6 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 6 Oct 2010 19:06:57 -0700 Subject: - New base InfoAction for dialog box like msgs - Fix titles on error pages --- lib/clienterroraction.php | 26 +++++++++- lib/error.php | 73 +++++----------------------- lib/info.php | 118 ++++++++++++++++++++++++++++++++++++++++++++++ lib/servererroraction.php | 23 +++++++++ 4 files changed, 176 insertions(+), 64 deletions(-) create mode 100644 lib/info.php (limited to 'lib') diff --git a/lib/clienterroraction.php b/lib/clienterroraction.php index 08bced5bd..9233c9bde 100644 --- a/lib/clienterroraction.php +++ b/lib/clienterroraction.php @@ -12,7 +12,7 @@ * @link http://status.net/ * * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2008, 2009, StatusNet, Inc. + * Copyright (C) 2008-2010 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 @@ -32,7 +32,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once INSTALLDIR.'/lib/error.php'; +require_once INSTALLDIR . '/lib/error.php'; /** * Class for displaying HTTP client errors @@ -90,4 +90,26 @@ class ClientErrorAction extends ErrorAction $this->showPage(); } + + /** + * To specify additional HTTP headers for the action + * + * @return void + */ + function extraHeaders() + { + $status_string = @self::$status[$this->code]; + header('HTTP/1.1 '.$this->code.' '.$status_string); + } + + /** + * Page title. + * + * @return page title + */ + + function title() + { + return @self::$status[$this->code]; + } } diff --git a/lib/error.php b/lib/error.php index a6a29119f..762425dc4 100644 --- a/lib/error.php +++ b/lib/error.php @@ -33,6 +33,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once INSTALLDIR . '/lib/info.php'; + /** * Base class for displaying HTTP errors * @@ -42,7 +44,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @link http://status.net/ */ -class ErrorAction extends Action +class ErrorAction extends InfoAction { static $status = array(); @@ -52,7 +54,7 @@ class ErrorAction extends Action function __construct($message, $code, $output='php://output', $indent=null) { - parent::__construct($output, $indent); + parent::__construct(null, $message, $output, $indent); $this->code = $code; $this->message = $message; @@ -64,43 +66,6 @@ class ErrorAction extends Action $this->prepare($_REQUEST); } - /** - * To specify additional HTTP headers for the action - * - * @return void - */ - function extraHeaders() - { - $status_string = @self::$status[$this->code]; - header('HTTP/1.1 '.$this->code.' '.$status_string); - } - - /** - * Display content. - * - * @return nothing - */ - function showContent() - { - $this->element('div', array('class' => 'error'), $this->message); - } - - /** - * Page title. - * - * @return page title - */ - - function title() - { - return @self::$status[$this->code]; - } - - function isReadOnly($args) - { - return true; - } - function showPage() { if ($this->minimal) { @@ -116,32 +81,16 @@ class ErrorAction extends Action exit(); } - // Overload a bunch of stuff so the page isn't too bloated - - function showBody() + /** + * Display content. + * + * @return nothing + */ + function showContent() { - $this->elementStart('body', array('id' => 'error')); - $this->elementStart('div', array('id' => 'wrap')); - $this->showHeader(); - $this->showCore(); - $this->showFooter(); - $this->elementEnd('div'); - $this->elementEnd('body'); + $this->element('div', array('class' => 'error'), $this->message); } - function showCore() - { - $this->elementStart('div', array('id' => 'core')); - $this->showContentBlock(); - $this->elementEnd('div'); - } - function showHeader() - { - $this->elementStart('div', array('id' => 'header')); - $this->showLogo(); - $this->showPrimaryNav(); - $this->elementEnd('div'); - } } diff --git a/lib/info.php b/lib/info.php new file mode 100644 index 000000000..395c6522e --- /dev/null +++ b/lib/info.php @@ -0,0 +1,118 @@ + + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, 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 . + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Base class for displaying dialog box like messages to the user + * + * @category Action + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see ErrorAction + */ + +class InfoAction extends Action +{ + var $message = null; + + function __construct($title, $message, $output='php://output', $indent=null) + { + parent::__construct($output, $indent); + + $this->message = $message; + $this->title = $title; + + // XXX: hack alert: usually we aren't going to + // call this page directly, but because it's + // an action it needs an args array anyway + $this->prepare($_REQUEST); + } + + /** + * Page title. + * + * @return page title + */ + + function title() + { + return empty($this->title) ? '' : $this->title; + } + + function isReadOnly($args) + { + return true; + } + + // Overload a bunch of stuff so the page isn't too bloated + + function showBody() + { + $this->elementStart('body', array('id' => 'error')); + $this->elementStart('div', array('id' => 'wrap')); + $this->showHeader(); + $this->showCore(); + $this->showFooter(); + $this->elementEnd('div'); + $this->elementEnd('body'); + } + + function showCore() + { + $this->elementStart('div', array('id' => 'core')); + $this->showContentBlock(); + $this->elementEnd('div'); + } + + function showHeader() + { + $this->elementStart('div', array('id' => 'header')); + $this->showLogo(); + $this->showPrimaryNav(); + $this->elementEnd('div'); + } + + /** + * Display content. + * + * @return nothing + */ + function showContent() + { + $this->element('div', array('class' => 'info'), $this->message); + } + +} diff --git a/lib/servererroraction.php b/lib/servererroraction.php index 9b5a553dc..54cc99099 100644 --- a/lib/servererroraction.php +++ b/lib/servererroraction.php @@ -96,4 +96,27 @@ class ServerErrorAction extends ErrorAction $this->showPage(); } + + /** + * To specify additional HTTP headers for the action + * + * @return void + */ + function extraHeaders() + { + $status_string = @self::$status[$this->code]; + header('HTTP/1.1 '.$this->code.' '.$status_string); + } + + /** + * Page title. + * + * @return page title + */ + + function title() + { + return @self::$status[$this->code]; + } + } -- cgit v1.2.3-54-g00ecf From 69e621a3e882cd060eb4314554aada7167edd897 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 6 Oct 2010 19:20:47 -0700 Subject: - Update ApiOauthAuthorizeAction to 1.0a - Fix enumerable bugs - New page for displaying 1.0a verifier (still needs work) --- actions/apioauthauthorize.php | 231 ++++++++++++++++++++++++++++++++---------- actions/apioauthpin.php | 69 +++++++++++++ lib/apioauth.php | 27 +---- lib/apioauthstore.php | 8 ++ lib/oauthstore.php | 11 ++ lib/serverexception.php | 2 +- 6 files changed, 270 insertions(+), 78 deletions(-) create mode 100644 actions/apioauthpin.php (limited to 'lib') diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index c2fbbcdd8..6772052f2 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -32,6 +32,7 @@ if (!defined('STATUSNET')) { } require_once INSTALLDIR . '/lib/apioauth.php'; +require_once INSTALLDIR . '/lib/info.php'; /** * Authorize an OAuth request token @@ -43,9 +44,10 @@ require_once INSTALLDIR . '/lib/apioauth.php'; * @link http://status.net/ */ -class ApiOauthAuthorizeAction extends ApiOauthAction +class ApiOauthAuthorizeAction extends Action { - var $oauth_token; + var $oauthTokenParam; + var $reqToken; var $callback; var $app; var $nickname; @@ -67,12 +69,17 @@ class ApiOauthAuthorizeAction extends ApiOauthAction { parent::prepare($args); - $this->nickname = $this->trimmed('nickname'); - $this->password = $this->arg('password'); - $this->oauth_token = $this->arg('oauth_token'); - $this->callback = $this->arg('oauth_callback'); - $this->store = new ApiStatusNetOAuthDataStore(); - $this->app = $this->store->getAppByRequestToken($this->oauth_token); + $this->nickname = $this->trimmed('nickname'); + $this->password = $this->arg('password'); + $this->oauthTokenParam = $this->arg('oauth_token'); + $this->callback = $this->arg('oauth_callback'); + $this->store = new ApiStatusNetOAuthDataStore(); + + try { + $this->app = $this->store->getAppByRequestToken($this->oauthTokenParam); + } catch (Exception $e) { + $this->clientError($e->getMessage()); + } return true; } @@ -97,14 +104,30 @@ class ApiOauthAuthorizeAction extends ApiOauthAction } else { - if (empty($this->oauth_token)) { + // Make sure a oauth_token parameter was provided + if (empty($this->oauthTokenParam)) { $this->clientError(_('No oauth_token parameter provided.')); - return; + } else { + + // Check to make sure the token exists + $this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam); + + if (empty($this->reqToken)) { + $this->serverError( + _('Invalid request token.') + ); + } else { + + // Check to make sure we haven't already authorized the token + if ($this->reqToken->state != 0) { + $this->clientError("Invalid request token."); + } + } } + // make sure there's an app associated with this token if (empty($this->app)) { - $this->clientError(_('Invalid token.')); - return; + $this->clientError(_('Invalid request token.')); } $name = $this->app->name; @@ -120,8 +143,8 @@ class ApiOauthAuthorizeAction extends ApiOauthAction $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { - $this->showForm(_('There was a problem with your session token. '. - 'Try again, please.')); + $this->showForm( + _('There was a problem with your session token. Try again, please.')); return; } @@ -130,6 +153,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction $user = null; if (!common_logged_in()) { + + // XXX Force credentials check? + + // XXX OpenID + $user = common_check_user($this->nickname, $this->password); if (empty($user)) { $this->showForm(_("Invalid nickname / password!")); @@ -141,9 +169,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction if ($this->arg('allow')) { - // mark the req token as authorized + // fetch the token + $this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam); - $this->store->authorize_token($this->oauth_token); + // mark the req token as authorized + try { + $this->store->authorize_token($this->oauthTokenParam); + } catch (Exception $e) { + $this->serverError($e->getMessage()); + } // Check to see if there was a previous token associated // with this user/app and kill it. If the user is doing this she @@ -156,8 +190,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction if (!$result) { common_log_db_error($appUser, 'DELETE', __FILE__); - throw new ServerException(_('Database error deleting OAuth application user.')); - return; + $this->serverError(_('Database error deleting OAuth application user.')); } } @@ -175,20 +208,19 @@ class ApiOauthAuthorizeAction extends ApiOauthAction // granted. The OAuth app user record then gets updated // with the new access token and access type. - $appUser->token = $this->oauth_token; + $appUser->token = $this->oauthTokenParam; $appUser->created = common_sql_now(); $result = $appUser->insert(); if (!$result) { common_log_db_error($appUser, 'INSERT', __FILE__); - throw new ServerException(_('Database error inserting OAuth application user.')); - return; + $this->serverError(_('Database error inserting OAuth application user.')); } - // if we have a callback redirect and provide the token + // If we have a callback redirect and provide the token - // A callback specified in the app setup overrides whatever + // Note: A callback specified in the app setup overrides whatever // is passed in with the request. if (!empty($this->app->callback_url)) { @@ -197,40 +229,40 @@ class ApiOauthAuthorizeAction extends ApiOauthAction if (!empty($this->callback)) { - $target_url = $this->getCallback($this->callback, - array('oauth_token' => $this->oauth_token)); + $targetUrl = $this->getCallback( + $this->callback, + array( + 'oauth_token' => $this->oauthTokenParam, + 'oauth_verifier' => $this->reqToken->verifier // 1.0a + ) + ); + + // Redirect the user to the provided OAuth callback + common_redirect($targetUrl, 303); - common_redirect($target_url, 303); } else { - common_debug("callback was empty!"); + common_log( + LOG_INFO, + "No oauth_callback parameter provided for application ID " + . $this->app->id + . " when authorizing request token." + ); } - // otherwise inform the user that the rt was authorized - - $this->elementStart('p'); - - // XXX: Do OAuth 1.0a verifier code - - $this->raw(sprintf(_("The request token %s has been authorized. " . - 'Please exchange it for an access token.'), - $this->oauth_token)); + // Otherwise, inform the user that the rt was authorized + $this->showAuthorized(); - $this->elementEnd('p'); + } else if ($this->arg('cancel')) { - } else if ($this->arg('deny')) { - - $datastore = new ApiStatusNetOAuthDataStore(); - $datastore->revoke_token($this->oauth_token, 0); - - $this->elementStart('p'); - - $this->raw(sprintf(_("The request token %s has been denied and revoked."), - $this->oauth_token)); + try { + $this->store->revoke_token($this->oauthTokenParam, 0); + $this->showCanceled(); + } catch (Exception $e) { + $this->ServerError($e->getMessage()); + } - $this->elementEnd('p'); } else { $this->clientError(_('Unexpected form submission.')); - return; } } @@ -276,7 +308,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction _('Allow or deny access')); $this->hidden('token', common_session_token()); - $this->hidden('oauth_token', $this->oauth_token); + $this->hidden('oauth_token', $this->oauthTokenParam); $this->hidden('oauth_callback', $this->callback); $this->elementStart('ul', 'form_data'); @@ -321,11 +353,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction } - $this->element('input', array('id' => 'deny_submit', + $this->element('input', array('id' => 'cancel_submit', 'class' => 'submit submit form_action-primary', - 'name' => 'deny', + 'name' => 'cancel', 'type' => 'submit', - 'value' => _('Deny'))); + 'value' => _('Cancel'))); $this->element('input', array('id' => 'allow_submit', 'class' => 'submit submit form_action-secondary', @@ -348,7 +380,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction function getInstructions() { - return _('Allow or deny access to your account information.'); + return _('Authorize access to your account information.'); } /** @@ -388,4 +420,97 @@ class ApiOauthAuthorizeAction extends ApiOauthAction // NOP } + /* + * Show a nice message confirming the authorization + * operation was canceled. + * + * @return nothing + */ + + function showCanceled() + { + $info = new InfoAction( + _('Authorization canceled.'), + sprintf( + _('The request token %s has been revoked.'), + $this->oauthTokenParm + ) + ); + + $info->showPage(); + } + + /* + * Show a nice message that the authorization was successful. + * If the operation is out-of-band, show a pin. + * + * @return nothing + */ + + function showAuthorized() + { + + if ($this->reqToken->verified_callback == 'oob') { + + $pin = new ApiOauthPinAction($this->reqToken->verifier); + $pin->showPage(); + + } else { + + $info = new InfoAction( + _("Authorization succeeded."), + sprintf( + _('The request token %s has been authorized. Please exchange it for an access token using this verifier: %s'), + $this->oauthTokenParam, + $this->reqToken->verifier + ) + ); + + $info->showPage(); + } + } + + /* + * Properly format the callback URL and parameters so it's + * suitable for a redirect in the OAuth dance + * + * @param string $url the URL + * @param array $params an array of parameters + * + * @return string $url a URL to use for redirecting to + */ + + function getCallback($url, $params) + { + foreach ($params as $k => $v) { + $url = $this->appendQueryVar( + $url, + OAuthUtil::urlencode_rfc3986($k), + OAuthUtil::urlencode_rfc3986($v) + ); + } + + return $url; + } + + /* + * Append a new query parameter after any existing query + * parameters. + * + * @param string $url the URL + * @prarm string $k the parameter name + * @param string $v value of the paramter + * + * @return string $url the new URL with added parameter + */ + + function appendQueryVar($url, $k, $v) { + $url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&'); + $url = substr($url, 0, -1); + if (strpos($url, '?') === false) { + return ($url . '?' . $k . '=' . $v); + } else { + return ($url . '&' . $k . '=' . $v); + } + } } diff --git a/actions/apioauthpin.php b/actions/apioauthpin.php new file mode 100644 index 000000000..5a88b5e59 --- /dev/null +++ b/actions/apioauthpin.php @@ -0,0 +1,69 @@ +. + * + * @category Action + * @package StatusNet + * @author Zach Copley + * @copyright 2010 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/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/info.php'; + +/** + * Class for displaying an OAuth verifier pin + * + * @category Action + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiOauthPinAction extends InfoAction +{ + + function __construct($verifier) + { + $this->verifier = $verifier; + $title = _('Authorization succeeded.'); + parent::__construct($title, $title); + } + + // TODO: Check for logged in state! + + /** + * Display content. + * + * @return nothing + */ + function showContent() + { + // XXX: make this much nicer + $this->element('div', array('class' => 'info'), $this->verifier); + } + +} diff --git a/lib/apioauth.php b/lib/apioauth.php index 75b0b3c57..54cecf92a 100644 --- a/lib/apioauth.php +++ b/lib/apioauth.php @@ -34,9 +34,8 @@ require_once INSTALLDIR . '/lib/apiaction.php'; require_once INSTALLDIR . '/lib/apioauthstore.php'; /** - * Base action for API OAuth enpoints. Clean up the - * the request, and possibly some other common things - * here. + * Base action for API OAuth enpoints. Clean up the + * request. Some other common functions. * * @category API * @package StatusNet @@ -82,6 +81,7 @@ class ApiOauthAction extends ApiAction * any extra parameters or anything else it's not expecting. * I'm looking at you, p parameter. */ + static function cleanRequest() { // kill evil effects of magical slashing @@ -106,25 +106,4 @@ class ApiOauthAction extends ApiAction $_SERVER['QUERY_STRING'] = implode('&', $queryArray); } - function getCallback($url, $params) - { - foreach ($params as $k => $v) { - $url = $this->appendQueryVar($url, - OAuthUtil::urlencode_rfc3986($k), - OAuthUtil::urlencode_rfc3986($v)); - } - - return $url; - } - - function appendQueryVar($url, $k, $v) { - $url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&'); - $url = substr($url, 0, -1); - if (strpos($url, '?') === false) { - return ($url . '?' . $k . '=' . $v); - } else { - return ($url . '&' . $k . '=' . $v); - } - } - } diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index 620f0947f..4d141286b 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -202,6 +202,14 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore $t->type = 0; // request $t->state = 0; // unauthorized $t->verified_callback = $callback; + + if ($callback === 'oob') { + // six digit pin + $t->verifier = mt_rand(0, 999999); + } else { + $t->verifier = common_good_rand(8); + } + $t->created = DB_DataObject_Cast::dateTime(); if (!$t->insert()) { return null; diff --git a/lib/oauthstore.php b/lib/oauthstore.php index f3ee629fd..537667678 100644 --- a/lib/oauthstore.php +++ b/lib/oauthstore.php @@ -55,6 +55,17 @@ class StatusNetOAuthDataStore extends OAuthDataStore } } + function getTokenByKey($token_key) + { + $t = new Token(); + $t->tok = $token_key; + if ($t->find(true)) { + return $t; + } else { + return null; + } + } + // http://oauth.net/core/1.0/#nonce // "The Consumer SHALL then generate a Nonce value that is unique for // all requests with that timestamp." diff --git a/lib/serverexception.php b/lib/serverexception.php index 7dc9765ad..0dfbd04ff 100644 --- a/lib/serverexception.php +++ b/lib/serverexception.php @@ -22,7 +22,7 @@ * @category Exception * @package StatusNet * @author Evan Prodromou - * @copyright 2008 StatusNet, Inc. + * @copyright 2008-2010 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/ */ -- cgit v1.2.3-54-g00ecf From 8658e4f8c4186574ac6503428be3ed534290387d Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 7 Oct 2010 11:01:17 -0700 Subject: Use 7 digits for oob OAuth pin instead of 6 --- lib/apioauthstore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index 4d141286b..7a4fe8db5 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -205,7 +205,7 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore if ($callback === 'oob') { // six digit pin - $t->verifier = mt_rand(0, 999999); + $t->verifier = mt_rand(0, 9999999); } else { $t->verifier = common_good_rand(8); } -- cgit v1.2.3-54-g00ecf From 459727bd610df5e579311a1627ee9b0e93ac44b0 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 7 Oct 2010 18:32:27 -0700 Subject: Update ApiOauthAccessTokenAction to OAuth 1.0a --- actions/apioauthaccesstoken.php | 56 ++++++++++++++++++++++++++++++++--------- lib/apioauthstore.php | 34 +++++++++++++++---------- 2 files changed, 65 insertions(+), 25 deletions(-) (limited to 'lib') diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php index 887df4c20..b8b188b1d 100644 --- a/actions/apioauthaccesstoken.php +++ b/actions/apioauthaccesstoken.php @@ -2,7 +2,8 @@ /** * StatusNet, the distributed open-source microblogging tool * - * Exchange an authorized OAuth request token for an access token + * Action for getting OAuth token credentials (exchange an authorized + * request token for an access token) * * PHP version 5 * @@ -34,7 +35,8 @@ if (!defined('STATUSNET')) { require_once INSTALLDIR . '/lib/apioauth.php'; /** - * Exchange an authorized OAuth request token for an access token + * Action for getting OAuth token credentials (exchange an authorized + * request token for an access token) * * @category API * @package StatusNet @@ -45,6 +47,8 @@ require_once INSTALLDIR . '/lib/apioauth.php'; class ApiOauthAccessTokenAction extends ApiOauthAction { + protected $reqToken = null; + protected $verifier = null; /** * Class handler. @@ -65,30 +69,58 @@ class ApiOauthAccessTokenAction extends ApiOauthAction $atok = null; + // XXX: Insist that oauth_token and oauth_verifier be populated? + // Spec doesn't say they MUST be. + try { + $req = OAuthRequest::from_request(); + + $this->reqToken = $req->get_parameter('oauth_token'); + $this->verifier = $req->get_parameter('oauth_verifier'); + $atok = $server->fetch_access_token($req); } catch (OAuthException $e) { common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); common_debug(var_export($req, true)); - $this->outputError($e->getMessage()); - return; + $code = $e->getCode(); + $this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text'); } if (empty($atok)) { - common_debug('couldn\'t get access token.'); - print "Token exchange failed. Has the request token been authorized?\n"; + + // Token exchange failed -- log it + + list($proxy, $ip) = common_client_ip(); + + $msg = sprintf( + 'API OAuth - Failure exchanging request token for access token, ' + . 'request token = %s, verifier = %s, IP = %s, proxy = %s', + $this->reqToken, + $this->verifier, + $ip, + $proxy + ); + + common_log(LOG_WARNING, $msg); + + print "Invalid request token or verifier."; + } else { - print $atok; + $this->showAccessToken($atok); } } - function outputError($msg) + /* + * Display OAuth token credentials + * + * @param OAuthToken token the access token + */ + + function showAccessToken($token) { - header('HTTP/1.1 401 Unauthorized'); - header('Content-Type: text/html; charset=utf-8'); - print $msg . "\n"; + header('Content-Type: application/x-www-form-urlencoded'); + print $token; } } - diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index 7a4fe8db5..4b52ba1ba 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -71,29 +71,33 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore } } - function new_access_token($token, $consumer) + function new_access_token($token, $consumer, $verifier) { - common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); + common_debug( + 'new_access_token("' . $token->key . '","' . $consumer->key. '","' . $verifier . '")', + __FILE__ + ); $rt = new Token(); + $rt->consumer_key = $consumer->key; - $rt->tok = $token->key; - $rt->type = 0; // request + $rt->tok = $token->key; + $rt->type = 0; // request $app = Oauth_application::getByConsumerKey($consumer->key); + assert(!empty($app)); - if (empty($app)) { - common_debug("empty app!"); - } + if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized - if ($rt->find(true) && $rt->state == 1) { // authorized common_debug('request token found.', __FILE__); // find the associated user of the app $appUser = new Oauth_application_user(); + $appUser->application_id = $app->id; - $appUser->token = $rt->tok; + $appUser->token = $rt->tok; + $result = $appUser->find(true); if (!empty($result)) { @@ -106,10 +110,12 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore // go ahead and make the access token $at = new Token(); - $at->consumer_key = $consumer->key; - $at->tok = common_good_rand(16); - $at->secret = common_good_rand(16); - $at->type = 1; // access + $at->consumer_key = $consumer->key; + $at->tok = common_good_rand(16); + $at->secret = common_good_rand(16); + $at->type = 1; // access + $at->verifier = $verifier; + $at->verified_callback = $rt->verified_callback; // 1.0a $at->created = DB_DataObject_Cast::dateTime(); if (!$at->insert()) { @@ -217,4 +223,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore return new OAuthToken($t->tok, $t->secret); } } + + } -- cgit v1.2.3-54-g00ecf From 9d9e80ea2135c09d911bb60ce01852d77fbb3365 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 12 Oct 2010 13:00:03 -0700 Subject: Followup to IIS installer tweaks in [9bb48c36]: * skip 0-byte config files when initializing, go ahead and redirect to installer if no non-0-byte files * tweak warning on installer.php to let you know if you have a 0-byte config.php that's not writable, as opposed to generally already having a config.php with data in it --- lib/installer.php | 6 +++++- lib/statusnet.php | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/installer.php b/lib/installer.php index 2eff2d85a..c046eadea 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -85,7 +85,11 @@ abstract class Installer $config = INSTALLDIR.'/config.php'; if (file_exists($config)) { if (!is_writable($config) || filesize($config) > 0) { - $this->warning('Config file "config.php" already exists.'); + if (filesize($config) == 0) { + $this->warning('Config file "config.php" already exists and is empty, but is not writable.'); + } else { + $this->warning('Config file "config.php" already exists.'); + } $pass = false; } } diff --git a/lib/statusnet.php b/lib/statusnet.php index 7212a4a47..7cb831696 100644 --- a/lib/statusnet.php +++ b/lib/statusnet.php @@ -336,8 +336,11 @@ class StatusNet foreach ($config_files as $_config_file) { if (@file_exists($_config_file)) { - include($_config_file); - self::$have_config = true; + // Ignore 0-byte config files + if (filesize($_config_file) > 0) { + include($_config_file); + self::$have_config = true; + } } } -- cgit v1.2.3-54-g00ecf From f4f16af8ac6c2bb1d5561bd85b4908fd3f9e1dbb Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 12 Oct 2010 15:49:20 -0700 Subject: Add a basic group deletion for moderator users. --- actions/deletegroup.php | 226 ++++++++++++++++++++++++++++++++++++++++++++++++ actions/showgroup.php | 6 ++ classes/Profile.php | 1 + lib/deletegroupform.php | 123 ++++++++++++++++++++++++++ lib/right.php | 1 + lib/router.php | 2 +- 6 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 actions/deletegroup.php create mode 100644 lib/deletegroupform.php (limited to 'lib') diff --git a/actions/deletegroup.php b/actions/deletegroup.php new file mode 100644 index 000000000..acb309e1d --- /dev/null +++ b/actions/deletegroup.php @@ -0,0 +1,226 @@ +. + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @author Brion Vibber + * @copyright 2008-2010 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/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Delete a group + * + * This is the action for deleting a group. + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @author Brion Vibber + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * @fixme merge more of this code with related variants + */ + +class DeletegroupAction extends RedirectingAction +{ + var $group = null; + + /** + * Prepare to run + * + * @fixme merge common setup code with other group actions + * @fixme allow group admins to delete their own groups + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to delete a group.')); + return false; + } + + $nickname_arg = $this->trimmed('nickname'); + $id = intval($this->arg('id')); + if ($id) { + $this->group = User_group::staticGet('id', $id); + } else if ($nickname_arg) { + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('leavegroup', $args), 301); + return false; + } + + $local = Local_group::staticGet('nickname', $nickname); + + if (!$local) { + $this->clientError(_('No such group.'), 404); + return false; + } + + $this->group = User_group::staticGet('id', $local->group_id); + } else { + $this->clientError(_('No nickname or ID.'), 404); + return false; + } + + if (!$this->group) { + $this->clientError(_('No such group.'), 404); + return false; + } + + $cur = common_current_user(); + if (!$cur->hasRight(Right::DELETEGROUP)) { + $this->clientError(_('You are not allowed to delete this group.'), 403); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On POST, delete the group. + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + if ($this->arg('no')) { + $this->returnToPrevious(); + return; + } elseif ($this->arg('yes')) { + $this->handlePost(); + return; + } + } + $this->showPage(); + } + + function handlePost() + { + $cur = common_current_user(); + + try { + if (Event::handle('StartDeleteGroup', array($this->group))) { + $this->group->delete(); + Event::handle('EndDeleteGroup', array($this->group)); + } + } catch (Exception $e) { + $this->serverError(sprintf(_('Could not delete group %2$s.'), + $this->group->nickname)); + } + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, sprintf(_('Deleted group %2$s'), + $this->group->nickname)); + $this->elementEnd('head'); + $this->elementStart('body'); + // @fixme add a sensible AJAX response form! + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + // @fixme if we could direct to the page on which this group + // would have shown... that would be awesome + common_redirect(common_local_url('groups'), + 303); + } + } + + function title() { + return _('Delete group'); + } + + function showContent() { + $this->areYouSureForm(); + } + + /** + * Confirm with user. + * Ripped from DeleteuserAction + * + * Shows a confirmation form. + * + * @fixme refactor common code for things like this + * @return void + */ + function areYouSureForm() + { + $id = $this->group->id; + $this->elementStart('form', array('id' => 'deletegroup-' . $id, + 'method' => 'post', + 'class' => 'form_settings form_entity_block', + 'action' => common_local_url('deletegroup', array('id' => $this->group->id)))); + $this->elementStart('fieldset'); + $this->hidden('token', common_session_token()); + $this->element('legend', _('Delete group')); + if (Event::handle('StartDeleteGroupForm', array($this, $this->group))) { + $this->element('p', null, + _('Are you sure you want to delete this group? '. + 'This will clear all data about the group from the '. + 'database, without a backup. ' . + 'Public posts to this group will still appear in ' . + 'individual timelines.')); + foreach ($this->args as $k => $v) { + if (substr($k, 0, 9) == 'returnto-') { + $this->hidden($k, $v); + } + } + Event::handle('EndDeleteGroupForm', array($this, $this->group)); + } + $this->submit('form_action-no', + // TRANS: Button label on the delete group form. + _m('BUTTON','No'), + 'submit form_action-primary', + 'no', + // TRANS: Submit button title for 'No' when deleting a group. + _('Do not delete this group')); + $this->submit('form_action-yes', + // TRANS: Button label on the delete group form. + _m('BUTTON','Yes'), + 'submit form_action-secondary', + 'yes', + // TRANS: Submit button title for 'Yes' when deleting a group. + _('Delete this group')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } +} \ No newline at end of file diff --git a/actions/showgroup.php b/actions/showgroup.php index 17c37e4d7..9a12bafaf 100644 --- a/actions/showgroup.php +++ b/actions/showgroup.php @@ -316,6 +316,12 @@ class ShowgroupAction extends GroupDesignAction Event::handle('EndGroupSubscribe', array($this, $this->group)); } $this->elementEnd('li'); + if ($cur->hasRight(Right::DELETEGROUP)) { + $this->elementStart('li', 'entity_delete'); + $df = new DeleteGroupForm($this, $this->group); + $df->show(); + $this->elementEnd('li'); + } $this->elementEnd('ul'); $this->elementEnd('div'); } diff --git a/classes/Profile.php b/classes/Profile.php index 3844077e6..12ce5d9b6 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -854,6 +854,7 @@ class Profile extends Memcached_DataObject case Right::SANDBOXUSER: case Right::SILENCEUSER: case Right::DELETEUSER: + case Right::DELETEGROUP: $result = $this->hasRole(Profile_role::MODERATOR); break; case Right::CONFIGURESITE: diff --git a/lib/deletegroupform.php b/lib/deletegroupform.php new file mode 100644 index 000000000..9d8012d33 --- /dev/null +++ b/lib/deletegroupform.php @@ -0,0 +1,123 @@ +. + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @author Sarven Capadisli + * @author Brion Vibber + * @copyright 2009, 2010 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/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Form for deleting a group + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @author Sarven Capadisli + * @author Brion Vibber + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see UnsubscribeForm + * @fixme merge a bunch of this stuff with similar form types to reduce boilerplate + */ + +class DeleteGroupForm extends Form +{ + /** + * group for user to delete + */ + + var $group = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param group $group group to join + */ + + function __construct($out=null, $group=null) + { + parent::__construct($out); + + $this->group = $group; + } + + /** + * ID of the form + * + * @return string ID of the form + */ + + function id() + { + return 'group-delete-' . $this->group->id; + } + + /** + * class of the form + * + * @return string of the form class + */ + + function formClass() + { + return 'form_group_delete'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('deletegroup', + array('id' => $this->group->id)); + } + + function formData() + { + $this->out->hidden($this->id() . '-returnto-action', 'groupbyid', 'returnto-action'); + $this->out->hidden($this->id() . '-returnto-id', $this->group->id, 'returnto-id'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Delete')); + } +} diff --git a/lib/right.php b/lib/right.php index deb451fde..bacbea5f2 100644 --- a/lib/right.php +++ b/lib/right.php @@ -60,5 +60,6 @@ class Right const MAKEGROUPADMIN = 'makegroupadmin'; const GRANTROLE = 'grantrole'; const REVOKEROLE = 'revokerole'; + const DELETEGROUP = 'deletegroup'; } diff --git a/lib/router.php b/lib/router.php index 00b299373..b1cc8d529 100644 --- a/lib/router.php +++ b/lib/router.php @@ -276,7 +276,7 @@ class Router $m->connect('group/new', array('action' => 'newgroup')); - foreach (array('edit', 'join', 'leave') as $v) { + foreach (array('edit', 'join', 'leave', 'delete') as $v) { $m->connect('group/:nickname/'.$v, array('action' => $v.'group'), array('nickname' => '[a-zA-Z0-9]+')); -- cgit v1.2.3-54-g00ecf From 5270e931311af0f644ebd4302833ad3bb89b81d4 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 12 Oct 2010 16:20:09 -0700 Subject: Spelling - OAuth not Oath --- lib/apioauthstore.php | 2 +- lib/connectsettingsaction.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index 4b52ba1ba..f3bf0b857 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -101,7 +101,7 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore $result = $appUser->find(true); if (!empty($result)) { - common_debug("Oath app user found."); + common_debug("Ouath app user found."); } else { common_debug("Oauth app user not found. app id $app->id token $rt->tok"); return null; diff --git a/lib/connectsettingsaction.php b/lib/connectsettingsaction.php index bb2e86176..325276c5f 100644 --- a/lib/connectsettingsaction.php +++ b/lib/connectsettingsaction.php @@ -116,9 +116,9 @@ class ConnectSettingsNav extends Widget } $menu['oauthconnectionssettings'] = array( - // TRANS: Menu item for OAth connection settings. + // TRANS: Menu item for OuAth connection settings. _m('MENU','Connections'), - // TRANS: Tooltip for connected applications (Connections through OAth) menu item. + // TRANS: Tooltip for connected applications (Connections through OAuth) menu item. _('Authorized connected applications') ); -- cgit v1.2.3-54-g00ecf From bca215563ff7aaee5c253b3a55cadc3b02116ef9 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 13 Oct 2010 11:04:41 -0700 Subject: Clean up remote avatar temporary files if we fail before saving them into avatars directory (OMB core, OStatus, WikiHowProfile, YammerImport) --- lib/oauthstore.php | 19 +++++++++----- plugins/OStatus/classes/Ostatus_profile.php | 35 ++++++++++++++----------- plugins/WikiHowProfile/WikiHowProfilePlugin.php | 31 +++++++++++++--------- plugins/YammerImport/lib/yammerimporter.php | 27 +++++++++++-------- 4 files changed, 66 insertions(+), 46 deletions(-) (limited to 'lib') diff --git a/lib/oauthstore.php b/lib/oauthstore.php index 537667678..1c8e72500 100644 --- a/lib/oauthstore.php +++ b/lib/oauthstore.php @@ -328,13 +328,18 @@ class StatusNetOAuthDataStore extends OAuthDataStore function add_avatar($profile, $url) { $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); - copy($url, $temp_filename); - $imagefile = new ImageFile($profile->id, $temp_filename); - $filename = Avatar::filename($profile->id, - image_type_to_extension($imagefile->type), - null, - common_timestamp()); - rename($temp_filename, Avatar::path($filename)); + try { + copy($url, $temp_filename); + $imagefile = new ImageFile($profile->id, $temp_filename); + $filename = Avatar::filename($profile->id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + } catch (Exception $e) { + unlink($temp_filename); + throw $e; + } return $profile->setOriginal($filename); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 47aee15f8..03fcb71df 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1053,22 +1053,27 @@ class Ostatus_profile extends Memcached_DataObject // @fixme this should be better encapsulated // ripped from oauthstore.php (for old OMB client) $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); - if (!copy($url, $temp_filename)) { - throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url)); - } + try { + if (!copy($url, $temp_filename)) { + throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url)); + } - if ($this->isGroup()) { - $id = $this->group_id; - } else { - $id = $this->profile_id; - } - // @fixme should we be using different ids? - $imagefile = new ImageFile($id, $temp_filename); - $filename = Avatar::filename($id, - image_type_to_extension($imagefile->type), - null, - common_timestamp()); - rename($temp_filename, Avatar::path($filename)); + if ($this->isGroup()) { + $id = $this->group_id; + } else { + $id = $this->profile_id; + } + // @fixme should we be using different ids? + $imagefile = new ImageFile($id, $temp_filename); + $filename = Avatar::filename($id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + } catch (Exception $e) { + unlink($temp_filename); + throw $e; + } // @fixme hardcoded chmod is lame, but seems to be necessary to // keep from accidentally saving images from command-line (queues) // that can't be read from web server, which causes hard-to-notice diff --git a/plugins/WikiHowProfile/WikiHowProfilePlugin.php b/plugins/WikiHowProfile/WikiHowProfilePlugin.php index fa683c483..753dff5a3 100644 --- a/plugins/WikiHowProfile/WikiHowProfilePlugin.php +++ b/plugins/WikiHowProfile/WikiHowProfilePlugin.php @@ -174,20 +174,25 @@ class WikiHowProfilePlugin extends Plugin // @fixme this should be better encapsulated // ripped from OStatus via oauthstore.php (for old OMB client) $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); - if (!copy($url, $temp_filename)) { - throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url)); - } - - $profile = $user->getProfile(); - $id = $profile->id; - // @fixme should we be using different ids? + try { + if (!copy($url, $temp_filename)) { + throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url)); + } - $imagefile = new ImageFile($id, $temp_filename); - $filename = Avatar::filename($id, - image_type_to_extension($imagefile->type), - null, - common_timestamp()); - rename($temp_filename, Avatar::path($filename)); + $profile = $user->getProfile(); + $id = $profile->id; + // @fixme should we be using different ids? + + $imagefile = new ImageFile($id, $temp_filename); + $filename = Avatar::filename($id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + } catch (Exception $e) { + unlink($temp_filename); + throw $e; + } $profile->setOriginal($filename); } } diff --git a/plugins/YammerImport/lib/yammerimporter.php b/plugins/YammerImport/lib/yammerimporter.php index 80cbcff8e..93bc96d52 100644 --- a/plugins/YammerImport/lib/yammerimporter.php +++ b/plugins/YammerImport/lib/yammerimporter.php @@ -436,18 +436,23 @@ class YammerImporter // @fixme this should be better encapsulated // ripped from oauthstore.php (for old OMB client) $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); - if (!copy($url, $temp_filename)) { - throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url)); - } + try { + if (!copy($url, $temp_filename)) { + throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url)); + } - $id = $dest->id; - // @fixme should we be using different ids? - $imagefile = new ImageFile($id, $temp_filename); - $filename = Avatar::filename($id, - image_type_to_extension($imagefile->type), - null, - common_timestamp()); - rename($temp_filename, Avatar::path($filename)); + $id = $dest->id; + // @fixme should we be using different ids? + $imagefile = new ImageFile($id, $temp_filename); + $filename = Avatar::filename($id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + } catch (Exception $e) { + unlink($temp_filename); + throw $e; + } // @fixme hardcoded chmod is lame, but seems to be necessary to // keep from accidentally saving images from command-line (queues) // that can't be read from web server, which causes hard-to-notice -- cgit v1.2.3-54-g00ecf