Merge remote branch 'gitorious/0.9.x' into 0.9.x

This commit is contained in:
Evan Prodromou 2010-10-13 15:18:32 -04:00
commit ddb60a8191
36 changed files with 2189 additions and 927 deletions

View File

@ -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);
$this->clientError(_("Invalid request token or verifier.", 400, 'text'));
} 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;
}
}

View File

@ -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;
@ -69,10 +71,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$this->nickname = $this->trimmed('nickname');
$this->password = $this->arg('password');
$this->oauth_token = $this->arg('oauth_token');
$this->oauthTokenParam = $this->arg('oauth_token');
$this->callback = $this->arg('oauth_callback');
$this->store = new ApiStatusNetOAuthDataStore();
$this->app = $this->store->getAppByRequestToken($this->oauth_token);
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
// Otherwise, inform the user that the rt was authorized
$this->showAuthorized();
$this->elementStart('p');
} else if ($this->arg('cancel')) {
// XXX: Do OAuth 1.0a verifier code
try {
$this->store->revoke_token($this->oauthTokenParam, 0);
$this->showCanceled();
} catch (Exception $e) {
$this->ServerError($e->getMessage());
}
$this->raw(sprintf(_("The request token %s has been authorized. " .
'Please exchange it for an access token.'),
$this->oauth_token));
$this->elementEnd('p');
} 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));
$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,107 @@ 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()
{
$title = sprintf(
_("You have successfully authorized %s."),
$this->app->name
);
$msg = sprintf(
_('Please return to %s and enter the following security code to complete the process.'),
$this->app->name
);
if ($this->reqToken->verified_callback == 'oob') {
$pin = new ApiOauthPinAction($title, $msg, $this->reqToken->verifier);
$pin->showPage();
} else {
// NOTE: This would only happen if an application registered as
// a web application but sent in 'oob' for the oauth_callback
// parameter. Usually web apps will send in a callback and
// not use the pin-based workflow.
$info = new InfoAction(
$title,
$msg,
$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);
}
}
}

67
actions/apioauthpin.php Normal file
View File

@ -0,0 +1,67 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action for displaying an OAuth verifier pin
*
* 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 Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @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
*
* XXX: I'm pretty sure we don't need to check the logged in state here. -- Zach
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @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($title, $message, $verifier)
{
$this->verifier = $verifier;
$this->title = $title;
parent::__construct($title, $message);
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'info'), $this->message);
$this->element('div', array('id' => 'oauth_pin'), $this->verifier);
}
}

View File

@ -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,78 @@ 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;
common_log(
LOG_INFO,
sprintf(
"API OAuth - Issued request token %s for consumer %s with oauth_callback %s",
$token->key,
$req->get_parameter('oauth_consumer_key'),
"'" . $req->get_parameter('oauth_callback') ."'"
)
);
// 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.");
// XXX: Should we throw an error if a client is registered as a
// web application but requests the pin based workflow? For now I'm
// allowing the workflow to proceed and issuing a pin. --Zach
return true;
} else {
return Validate::uri(
$callback,
array('allowed_schemes' => array('http', 'https'))
);
}
}

226
actions/deletegroup.php Normal file
View File

@ -0,0 +1,226 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Delete a group
*
* 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 Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Brion Vibber <brion@status.net>
* @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 <evan@status.net>
* @author Brion Vibber <brion@status.net>
* @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');
}
}

View File

@ -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');
}

View File

@ -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:

View File

@ -547,4 +547,46 @@ class User_group extends Memcached_DataObject
$group->query('COMMIT');
return $group;
}
/**
* Handle cascading deletion, on the model of notice and profile.
*
* This should handle freeing up cached entries for the group's
* id, nickname, URI, and aliases. There may be other areas that
* are not de-cached in the UI, including the sidebar lists on
* GroupsAction
*/
function delete()
{
if ($this->id) {
// Safe to delete in bulk for now
$related = array('Group_inbox',
'Group_block',
'Group_member',
'Related_group');
Event::handle('UserGroupDeleteRelated', array($this, &$related));
foreach ($related as $cls) {
$inst = new $cls();
$inst->group_id = $this->id;
$inst->delete();
}
// And related groups in the other direction...
$inst = new Related_group();
$inst->related_group_id = $this->id;
$inst->delete();
// Aliases and the local_group entry need to be cleared explicitly
// or we'll miss clearing some cache keys; that can make it hard
// to create a new group with one of those names or aliases.
$this->setAliases(array());
$local = Local_group::staticGet('group_id', $this->id);
if ($local) {
$local->delete();
}
} else {
common_log(LOG_WARN, "Ambiguous user_group->delete(); skipping related tables.");
}
parent::delete();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -30,13 +30,12 @@
if (!defined('STATUSNET')) {
exit(1);
}
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.
* request. Some other common functions.
*
* @category API
* @package StatusNet
@ -44,7 +43,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?
@ -77,6 +76,12 @@ class ApiOauthAction extends Action
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
@ -86,31 +91,19 @@ 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, 2) == 'p=') {
unset($queryArray[$i]);
}
}
function getCallback($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar($url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v));
$_SERVER['QUERY_STRING'] = implode('&', $queryArray);
}
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);
}
}
}

View File

@ -71,33 +71,37 @@ 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
$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;
$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;
@ -110,6 +114,8 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
$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()) {
@ -183,4 +189,40 @@ 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;
if ($callback === 'oob') {
// six digit pin
$t->verifier = mt_rand(0, 9999999);
} else {
$t->verifier = common_good_rand(8);
}
$t->created = DB_DataObject_Cast::dateTime();
if (!$t->insert()) {
return null;
} else {
return new OAuthToken($t->tok, $t->secret);
}
}
}

View File

@ -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];
}
}

View File

@ -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')
);

123
lib/deletegroupform.php Normal file
View File

@ -0,0 +1,123 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for joining a group
*
* 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 Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @author Brion Vibber <brion@status.net>
* @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 <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @author Brion Vibber <brion@status.net>
* @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'));
}
}

View File

@ -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');
}
}

118
lib/info.php Normal file
View File

@ -0,0 +1,118 @@
<?php
/**
* Information action
*
* PHP version 5
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 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 <http://www.gnu.org/licenses/>.
*/
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 <zach@status.net>
* @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);
}
}

View File

@ -85,7 +85,11 @@ abstract class Installer
$config = INSTALLDIR.'/config.php';
if (file_exists($config)) {
if (!is_writable($config) || filesize($config) > 0) {
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;
}
}

View File

@ -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."
@ -317,6 +328,7 @@ class StatusNetOAuthDataStore extends OAuthDataStore
function add_avatar($profile, $url)
{
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
try {
copy($url, $temp_filename);
$imagefile = new ImageFile($profile->id, $temp_filename);
$filename = Avatar::filename($profile->id,
@ -324,6 +336,10 @@ class StatusNetOAuthDataStore extends OAuthDataStore
null,
common_timestamp());
rename($temp_filename, Avatar::path($filename));
} catch (Exception $e) {
unlink($temp_filename);
throw $e;
}
return $profile->setOriginal($filename);
}

View File

@ -60,5 +60,6 @@ class Right
const MAKEGROUPADMIN = 'makegroupadmin';
const GRANTROLE = 'grantrole';
const REVOKEROLE = 'revokerole';
const DELETEGROUP = 'deletegroup';
}

View File

@ -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]+'));

View File

@ -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];
}
}

View File

@ -22,7 +22,7 @@
* @category Exception
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @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/
*/

View File

@ -336,10 +336,13 @@ class StatusNet
foreach ($config_files as $_config_file) {
if (@file_exists($_config_file)) {
// Ignore 0-byte config files
if (filesize($_config_file) > 0) {
include($_config_file);
self::$have_config = true;
}
}
}
if (!self::$have_config) {
throw new NoConfigException("No configuration file found.",

View File

@ -0,0 +1,54 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* @package ModHelperPlugin
* @maintainer Brion Vibber <brion@status.net>
*/
class ModHelperPlugin extends Plugin
{
function onPluginVersion(&$versions)
{
$versions[] = array('name' => 'ModHelper',
'version' => STATUSNET_VERSION,
'author' => 'Brion Vibber',
'homepage' => 'http://status.net/wiki/Plugin:ModHelper',
'rawdescription' =>
_m('Lets users who have been manually marked as "modhelper"s silence accounts.'));
return true;
}
function onUserRightsCheck($profile, $right, &$result)
{
if ($right == Right::SILENCEUSER) {
// Hrm.... really we should confirm that the *other* user isn't privleged. :)
if ($profile->hasRole('modhelper')) {
$result = true;
return false;
}
}
return true;
}
}

View File

@ -1053,6 +1053,7 @@ 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');
try {
if (!copy($url, $temp_filename)) {
throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url));
}
@ -1069,6 +1070,10 @@ class Ostatus_profile extends Memcached_DataObject
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

View File

@ -174,6 +174,7 @@ 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');
try {
if (!copy($url, $temp_filename)) {
throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url));
}
@ -188,6 +189,10 @@ class WikiHowProfilePlugin extends Plugin
null,
common_timestamp());
rename($temp_filename, Avatar::path($filename));
} catch (Exception $e) {
unlink($temp_filename);
throw $e;
}
$profile->setOriginal($filename);
}
}

View File

@ -436,6 +436,7 @@ 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');
try {
if (!copy($url, $temp_filename)) {
throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url));
}
@ -448,6 +449,10 @@ class YammerImporter
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

View File

@ -1,22 +1,160 @@
Some very rough test scripts for hitting up the OAuth endpoints.
Note: this works best if you register an OAuth application, leaving
the callback URL blank.
These instructions assume you understand the basics of how OAuth
works. You may want to read up about it first. Here are some good
resources for learning about OAuth:
Put your instance info and consumer key and secret in oauth.ini
http://hueniverse.com/oauth/
http://tools.ietf.org/html/rfc5849
Example usage:
--------------
To use these scripts (and OAuth in general) first you will need to
register and OAuth client application with your StatusNet instance:
php getrequesttoken.php
http://example.status.net/settings/oauthapps
Gets a request token, token secret and a url to authorize it. Once
you authorize the request token you can exchange it for an access token...
oauth.ini
---------
php exchangetokens.php --oauth_token=b9a79548a88c1aa9a5bea73103c6d41d --token_secret=4a47d9337fc0202a14ab552e17a3b657
Using oauth.ini.sample as a guide, put your StatusNet OAuth endpoints
and consumer key and secret in a file called oauth.ini and save it
in the same directory as these scripts.
Once you have your access token, go ahead and try a protected API
resource:
fetch_temp_creds.php
--------------------
php verifycreds.php --oauth_token=cf2de7665f0dda0a82c2dc39b01be7f9 --token_secret=4524c3b712200138e1a4cff2e9ca83d8
Will fetch a request token, token secret and a URL to authorize the
token. Once you authorize the request token, you can exchange it
for an access token.
example usage:
$ php fetch_temp_creds.php
Request Token
- oauth_token = 89d481e376edc622f08da5791e6a4446
- oauth_token_secret = 6d028bcd1ea125cbed7da2f254219885
Authorize URL
http://example.status.net/api/oauth/authorize?oauth_token=89d481e376edc622f08da5791e6a4446
Now paste the Authorize URL into your browser and authorize your temporary credentials.
fetch_token_creds.php
---------------------
After you have authorized your request token, you will be presented
with a verifier code, or pin, in your browser, which you will need
to get an access token. Make sure you copy it into a text buffer
or write it down or something. Then call fetch_token_credentials.php
to exchange your temporary credentials for real token credentials.
example usage:
$ php fetch_token_creds.php -t 89d481e376edc622f08da5791e6a4446 -s 6d028bcd1ea125cbed7da2f254219885 -v 305162
Access Token
- oauth_token = 9b354df102d8e2b4621122c85d8d045c
- oauth_token_secret = 1800a88f1574b47d595214a74e5b1ec5
oauth_verify_credentials.php
----------------------------
Now you should have real token credentials (an OAuth access token)
and you can access protected API resources. This is an example
script that calls /api/account/verify_credentials.xml.
example usage:
$ php oauth_verify_creds.php -t 80305cd15c5c69834364ac02d7f9178c -s 673e3b2978b1b92c8edbfe172505fee1
<?xml version="1.0" encoding="UTF-8"?>
<user xmlns:statusnet="http://status.net/schema/api/1/">
<id>23</id>
<name>zach</name>
<screen_name>zach</screen_name>
<location></location>
<description></description>
<profile_image_url>http://example.status.net/theme/default/default-avatar-stream.png</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>0</followers_count>
<profile_background_color></profile_background_color>
<profile_text_color></profile_text_color>
<profile_link_color></profile_link_color>
<profile_sidebar_fill_color></profile_sidebar_fill_color>
<profile_sidebar_border_color></profile_sidebar_border_color>
<friends_count>0</friends_count>
<created_at>Thu Sep 30 23:11:00 +0000 2010</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url></profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>4</statuses_count>
<following>true</following>
<statusnet:blocking>false</statusnet:blocking>
<notifications>true</notifications>
<status>
<text>gar</text>
<truncated>false</truncated>
<created_at>Wed Oct 06 23:40:14 +0000 2010</created_at>
<in_reply_to_status_id></in_reply_to_status_id>
<source>web</source>
<id>7</id>
<in_reply_to_user_id></in_reply_to_user_id>
<in_reply_to_screen_name></in_reply_to_screen_name>
<geo></geo>
<favorited>false</favorited>
<statusnet:html>gar</statusnet:html>
</status>
<statusnet:profile_url>http://example.status.net/statusnet/zach</statusnet:profile_url>
</user>
oauth_post_notice.php
---------------------
This is another test script that lets you post a notice via OAuth.
example usage:
$ php oauth_post_notice.php -t 80305cd15c5c69834364ac02d7f9178c -s 673e3b2978b1b92c8edbfe172505fee1 -u 'Test test test...'
<?xml version="1.0" encoding="UTF-8"?>
<status xmlns:statusnet="http://status.net/schema/api/1/">
<text>Test test test...</text>
<truncated>false</truncated>
<created_at>Fri Oct 08 02:37:35 +0000 2010</created_at>
<in_reply_to_status_id></in_reply_to_status_id>
<source>&lt;a href=&quot;http://banana.com&quot; rel=&quot;nofollow&quot;&gt;Banana&lt;/a&gt;</source>
<id>8</id>
<in_reply_to_user_id></in_reply_to_user_id>
<in_reply_to_screen_name></in_reply_to_screen_name>
<geo></geo>
<favorited>false</favorited>
<user>
<id>23</id>
<name>zach</name>
<screen_name>zach</screen_name>
<location></location>
<description></description>
<profile_image_url>http://example.status.net/statusnet/theme/default/default-avatar-stream.png</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>0</followers_count>
<profile_background_color></profile_background_color>
<profile_text_color></profile_text_color>
<profile_link_color></profile_link_color>
<profile_sidebar_fill_color></profile_sidebar_fill_color>
<profile_sidebar_border_color></profile_sidebar_border_color>
<friends_count>0</friends_count>
<created_at>Thu Sep 30 23:11:00 +0000 2010</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url></profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>5</statuses_count>
<following>true</following>
<statusnet:blocking>false</statusnet:blocking>
<notifications>true</notifications>
<statusnet:profile_url>http://example.status.net/statusnet/zach</statusnet:profile_url>
</user>
<statusnet:html>Test test test...</statusnet:html>
</status>

View File

@ -1,105 +0,0 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a 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/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$at_endpoint = $ini['apiroot'] . $ini['access_token_url'];
$shortoptions = 't:s:';
$longoptions = array('oauth_token=', 'token_secret=');
$helptext = <<<END_OF_ETOKENS_HELP
exchangetokens.php [options]
Exchange an authorized OAuth request token for an access token
-t --oauth_token authorized request token
-s --token_secret authorized request token secret
END_OF_ETOKENS_HELP;
require_once INSTALLDIR . '/scripts/commandline.inc';
$token = null;
$token_secret = null;
if (have_option('t', 'oauth_token')) {
$token = get_option_value('oauth_token');
}
if (have_option('s', 'token_secret')) {
$token_secret = get_option_value('s', 'token_secret');
}
if (empty($token)) {
print "Please specify a request token.\n";
exit(1);
}
if (empty($token_secret)) {
print "Please specify a request token secret.\n";
exit(1);
}
$rt = new OAuthToken($token, $token_secret);
common_debug("Exchange request token = " . var_export($rt, true));
$parsed = parse_url($at_endpoint);
$params = array();
parse_str($parsed['query'], $params);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $rt, "GET", $at_endpoint, $params);
$req_req->sign_request($hmac_method, $test_consumer, $rt);
$r = httpRequest($req_req->to_url());
common_debug("Exchange request token = " . var_export($rt, true));
common_debug("Exchange tokens URL: " . $req_req->to_url());
$body = $r->getBody();
$token_stuff = array();
parse_str($body, $token_stuff);
print 'Access token : ' . $token_stuff['oauth_token'] . "\n";
print 'Access token secret : ' . $token_stuff['oauth_token_secret'] . "\n";
function httpRequest($url)
{
$request = HTTPClient::start();
$request->setConfig(array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
return $request->get($url);
}

106
tests/oauth/fetch_temp_creds.php Executable file
View File

@ -0,0 +1,106 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a 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 <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/scripts/commandline.inc';
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
// Check to make sure we have everything we need from the ini file
foreach(array('consumer_key', 'consumer_secret', 'apiroot', 'request_token_url') as $inikey) {
if (empty($ini[$inikey])) {
print "You forgot to specify a $inikey in your oauth.ini file.\n";
exit(1);
}
}
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . $ini['request_token_url'];
$parsed = parse_url($endpoint);
$params = array();
parse_str($parsed['query'], $params);
$params['oauth_callback'] = 'oob'; // out-of-band
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
try {
$req = OAuthRequest::from_consumer_and_token(
$consumer,
null,
"POST",
$endpoint,
$params
);
$req->sign_request($hmac_method, $consumer, NULL);
$r = httpRequest($endpoint, $req->to_postdata());
} catch (Exception $e) {
// oh noez
print $e->getMessage();
print "\nOAuth Request:\n";
var_dump($req);
exit(1);
}
$body = $r->getBody();
$tokenStuff = array();
parse_str($body, $tokenStuff);
$tok = $tokenStuff['oauth_token'];
$confirmed = $tokenStuff['oauth_callback_confirmed'];
if (empty($tokenStuff['oauth_token'])
|| empty($tokenStuff['oauth_token_secret'])
|| empty($confirmed)
|| $confirmed != 'true')
{
print "Error! HTTP response body: $body\n";
exit(1);
}
$authurl = $ini['apiroot'] . $ini['authorize_url'] . '?oauth_token=' . $tok;
print "Request Token\n";
print ' - oauth_token = ' . $tokenStuff['oauth_token'] . "\n";
print ' - oauth_token_secret = ' . $tokenStuff['oauth_token_secret'] . "\n";
print "Authorize URL\n $authurl\n\n";
print "Now paste the Authorize URL into your browser and authorize your temporary credentials.\n";
function httpRequest($endpoint, $poststr)
{
$request = HTTPClient::start();
$request->setConfig(
array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
)
);
// Turn signed request query string back into an array
parse_str($poststr, $postdata);
return $request->post($endpoint, null, $postdata);
}

146
tests/oauth/fetch_token_creds.php Executable file
View File

@ -0,0 +1,146 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a 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/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
// Check to make sure we have everything we need from the ini file
foreach(array('consumer_key', 'consumer_secret', 'apiroot', 'access_token_url') as $inikey) {
if (empty($ini[$inikey])) {
print "You forgot to specify a $inikey in your oauth.ini file.\n";
exit(1);
}
}
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . $ini['access_token_url'];
$shortoptions = 't:s:v:';
$longoptions = array('oauth_token=', 'oauth_token_secret=', 'oauth_verifier=');
$helptext = <<<END_OF_ETOKENS_HELP
fetch_token_creds.php [options]
Exchange authorized OAuth temporary credentials for token credentials
(an authorized request token for an access token)
-t --oauth_token authorized request token
-s --oauth_token_secret authorized request token secret
-v --oauth_verifier authorized request token verifier
END_OF_ETOKENS_HELP;
require_once INSTALLDIR . '/scripts/commandline.inc';
$token = $secret = $verifier = null;
if (have_option('t', 'oauth_token')) {
$token = get_option_value('t', 'oauth_token');
}
if (have_option('s', 'oauth_token_secret')) {
$secret = get_option_value('s', 'oauth_token_secret');
}
if (have_option('v', 'oauth_verifier')) {
$verifier = get_option_value('v', 'oauth_verifier');
}
if (empty($token)) {
print "Please specify the request token (--help for help).\n";
exit(1);
}
if (empty($secret)) {
print "Please specify the request token secret (--help for help).\n";
exit(1);
}
if (empty($verifier)) {
print "Please specify the request token verifier (--help for help).\n";
exit(1);
}
$rtok = new OAuthToken($token, $secret);
$parsed = parse_url($endpoint);
parse_str($parsed['query'], $params);
$params['oauth_verifier'] = $verifier; // 1.0a
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
try {
$oauthReq = OAuthRequest::from_consumer_and_token(
$consumer,
$rtok,
"POST",
$endpoint,
$params
);
$oauthReq->sign_request($hmac_method, $consumer, $rtok);
$httpReq = httpRequest($endpoint, $oauthReq->to_postdata());
$body = $httpReq->getBody();
} catch (Exception $e) {
// oh noez
print $e->getMessage();
print "\nOAuth Request:\n";
var_dump($oauthReq);
exit(1);
}
$tokenStuff = array();
parse_str($body, $tokenStuff);
if (empty($tokenStuff['oauth_token']) || empty($tokenStuff['oauth_token_secret'])) {
print "Error! HTTP response body: $body\n";
exit(1);
}
print "Access Token\n";
print ' - oauth_token = ' . $tokenStuff['oauth_token'] . "\n";
print ' - oauth_token_secret = ' . $tokenStuff['oauth_token_secret'] . "\n";
function httpRequest($endpoint, $poststr)
{
$request = HTTPClient::start();
$request->setConfig(
array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
)
);
parse_str($poststr, $postdata);
return $request->post($endpoint, null, $postdata);
}

View File

@ -1,71 +0,0 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a 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/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/scripts/commandline.inc';
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$rt_endpoint = $ini['apiroot'] . $ini['request_token_url'];
$parsed = parse_url($rt_endpoint);
$params = array();
parse_str($parsed['query'], $params);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, NULL, "GET", $rt_endpoint, $params);
$req_req->sign_request($hmac_method, $test_consumer, NULL);
$r = httpRequest($req_req->to_url());
$body = $r->getBody();
$token_stuff = array();
parse_str($body, $token_stuff);
$authurl = $ini['apiroot'] . $ini['authorize_url'] . '?oauth_token=' . $token_stuff['oauth_token'];
print 'Request token : ' . $token_stuff['oauth_token'] . "\n";
print 'Request token secret : ' . $token_stuff['oauth_token_secret'] . "\n";
print "Authorize URL : $authurl\n";
//var_dump($req_req);
function httpRequest($url)
{
$request = HTTPClient::start();
$request->setConfig(array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
return $request->get($url);
}

View File

@ -1,5 +1,5 @@
; Setup OAuth info here
apiroot = "http://YOURSTATUSNET/api"
apiroot = "https://YOURSTATUSNET/api"
request_token_url = "/oauth/request_token"
authorize_url = "/oauth/authorize"

View File

@ -22,15 +22,15 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php';
$shortoptions = 'o:s:u:';
$shortoptions = 't:s:u:';
$longoptions = array('oauth_token=', 'token_secret=', 'update=');
$helptext = <<<END_OF_VERIFY_HELP
statusupdate.php [options]
Update your status using OAuth
oauth_post_notice.php [options]
Update your status via OAuth
-o --oauth_token access token
-s --token_secret access token secret
-t --oauth_token access token
-s --oauth_token_secret access token secret
-u --update status update
@ -42,12 +42,12 @@ $update = null;
require_once INSTALLDIR . '/scripts/commandline.inc';
if (have_option('o', 'oauth_token')) {
$token = get_option_value('oauth_token');
if (have_option('t', 'oauth_token')) {
$token = get_option_value('t', 'oauth_token');
}
if (have_option('s', 'token_secret')) {
$token_secret = get_option_value('s', 'token_secret');
if (have_option('s', 'oauth_token_secret')) {
$token_secret = get_option_value('s', 'oauth_token_secret');
}
if (have_option('u', 'update')) {
@ -70,46 +70,55 @@ if (empty($update)) {
}
$ini = parse_ini_file("oauth.ini");
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . '/statuses/update.xml';
print "$endpoint\n";
$at = new OAuthToken($token, $token_secret);
$atok = new OAuthToken($token, $token_secret);
$parsed = parse_url($endpoint);
$params = array();
parse_str($parsed['query'], $params);
$params['status'] = $update;
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $at, 'POST', $endpoint, $params);
$req_req->sign_request($hmac_method, $test_consumer, $at);
try {
$r = httpRequest($req_req->to_url());
$oauthReq = OAuthRequest::from_consumer_and_token(
$consumer,
$atok,
'POST',
$endpoint,
$params
);
$body = $r->getBody();
$oauthReq->sign_request($hmac_method, $consumer, $atok);
print "$body\n";
$httpReq = httpRequest($endpoint, $oauthReq->to_postdata());
//print $req_req->to_url() . "\n\n";
print $httpReq->getBody();
function httpRequest($url)
} catch (Exception $e) {
print "Error! . $e->getMessage() . 'HTTP reponse body: " . $httpReq->getBody();
exit(1);
}
function httpRequest($endpoint, $poststr)
{
$request = HTTPClient::start();
$request->setConfig(array(
$request->setConfig(
array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
)
);
return $request->post($url);
// Turn signed request query string back into an array
parse_str($poststr, $postdata);
return $request->post($endpoint, null, $postdata);
}

View File

@ -22,15 +22,15 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php';
$shortoptions = 'o:s:';
$longoptions = array('oauth_token=', 'token_secret=');
$shortoptions = 't:s:';
$longoptions = array('oauth_token=', 'oauth_token_secret=');
$helptext = <<<END_OF_VERIFY_HELP
verifycreds.php [options]
Use an access token to verify credentials thru the api
oauth_verify_creds.php [options]
Access /api/account/verify_credentials.xml with OAuth
-o --oauth_token access token
-s --token_secret access token secret
-t --oauth_token access token
-s --oauth_token_secret access token secret
END_OF_VERIFY_HELP;
@ -39,63 +39,69 @@ $token_secret = null;
require_once INSTALLDIR . '/scripts/commandline.inc';
if (have_option('o', 'oauth_token')) {
$token = get_option_value('oauth_token');
if (have_option('t', 'oauth_token')) {
$token = get_option_value('t', 'oauth_token');
}
if (have_option('s', 'token_secret')) {
$token_secret = get_option_value('s', 'token_secret');
$token_secret = get_option_value('s', 'oauth_token_secret');
}
if (empty($token)) {
print "Please specify an access token.\n";
print "Please specify an access token (--help for help).\n";
exit(1);
}
if (empty($token_secret)) {
print "Please specify an access token secret.\n";
print "Please specify an access token secret (--help for help).\n";
exit(1);
}
$ini = parse_ini_file("oauth.ini");
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . '/account/verify_credentials.xml';
print "$endpoint\n";
$at = new OAuthToken($token, $token_secret);
$atok = new OAuthToken($token, $token_secret);
$parsed = parse_url($endpoint);
$params = array();
parse_str($parsed['query'], $params);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
try {
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $at, "GET", $endpoint, $params);
$req_req->sign_request($hmac_method, $test_consumer, $at);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$r = httpRequest($req_req->to_url());
$oauthReq = OAuthRequest::from_consumer_and_token(
$consumer,
$atok,
"GET",
$endpoint,
$params
);
$body = $r->getBody();
$oauthReq->sign_request($hmac_method, $consumer, $atok);
print "$body\n";
$httpReq = httpRequest($oauthReq->to_url());
//print $req_req->to_url() . "\n\n";
} catch (Exception $e) {
print "Error! HTTP response body: " . $httpReq->getBody();
exit(1);
}
print $httpReq->getBody();
function httpRequest($url)
{
$request = HTTPClient::start();
$request->setConfig(array(
$request->setConfig(
array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
)
);
return $request->get($url);
}