Merge branch '1.0.x' into schema-x

This commit is contained in:
Brion Vibber 2010-10-15 11:40:40 -07:00
commit 4c3aebd396
42 changed files with 2161 additions and 1081 deletions

View File

@ -1160,3 +1160,9 @@ StartShowFeedLink: before showing an individual feed item
EndShowFeedLink: after showing an individual feed
- $action: action being executed
- $feed: feed to show
StartShowNoticeForm: before showing the notice form (before <form>)
- $action: action being executed
EndShowNoticeForm: after showing the notice form (after <form>)
- $action: action being executed

18
README
View File

@ -852,6 +852,8 @@ notice: A plain string that will appear on every page. A good place
be escaped.
logo: URL of an image file to use as the logo for the site. Overrides
the logo in the theme, if any.
ssllogo: URL of an image file to use as the logo on SSL pages. If unset,
theme logo is used instead.
ssl: Whether to use SSL and https:// URLs for some or all pages.
Possible values are 'always' (use it for all pages), 'never'
(don't use it for any pages), or 'sometimes' (use it for
@ -1109,6 +1111,9 @@ path: Path part of theme URLs, before the theme name. Relative to the
which means to use the site path + '/theme'.
ssl: Whether to use SSL for theme elements. Default is null, which means
guess based on site SSL settings.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
javascript
----------
@ -1120,6 +1125,9 @@ path: Path part of Javascript URLs. Defaults to null,
which means to use the site path + '/js/'.
ssl: Whether to use SSL for JavaScript files. Default is null, which means
guess based on site SSL settings.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
xmpp
----
@ -1347,6 +1355,11 @@ ssl: whether to use HTTPS for file URLs. Defaults to null, meaning to
filecommand: command to use for determining the type of a file. May be
skipped if fileinfo extension is installed. Defaults to
'/usr/bin/file'.
sslserver: if specified, this server will be used when creating HTTPS
URLs. Otherwise, the site SSL server will be used, with /file/ path.
sslpath: if this and the sslserver are specified, this path will be used
when creating HTTPS URLs. Otherwise, the attachments|path value
will be used.
group
-----
@ -1403,8 +1416,9 @@ dir: directory to write backgrounds too. Default is '/background/'
subdir of install dir.
path: path to backgrounds. Default is sub-path of install path; note
that you may need to change this if you change site-path too.
ssl: Whether or not to use HTTPS for background files. Defaults to
null, meaning to guess from site-wide SSL settings.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
ping
----

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

View File

@ -140,7 +140,7 @@ class DesignadminpanelAction extends AdminPanelAction
$themeChanged = ($this->trimmed('theme') != $oldtheme);
}
static $settings = array('theme', 'logo');
static $settings = array('theme', 'logo', 'ssllogo');
$values = array();
@ -230,6 +230,7 @@ class DesignadminpanelAction extends AdminPanelAction
function restoreDefaults()
{
$this->deleteSetting('site', 'logo');
$this->deleteSetting('site', 'ssllogo');
$this->deleteSetting('site', 'theme');
$settings = array(
@ -293,7 +294,7 @@ class DesignadminpanelAction extends AdminPanelAction
/**
* Save the custom theme if the user uploaded one.
*
*
* @return mixed custom theme name, if succesful, or null if no theme upload.
* @throws ClientException for invalid theme archives
* @throws ServerException if trouble saving the theme files
@ -331,6 +332,11 @@ class DesignadminpanelAction extends AdminPanelAction
$this->clientError(_('Invalid logo URL.'));
}
if (!empty($values['ssllogo']) &&
!Validate::uri($values['ssllogo'], array('allowed_schemes' => array('https')))) {
$this->clientError(_('Invalid SSL logo URL.'));
}
if (!in_array($values['theme'], Theme::listAvailable())) {
$this->clientError(sprintf(_("Theme not available: %s."), $values['theme']));
}
@ -444,6 +450,10 @@ class DesignAdminPanelForm extends AdminForm
$this->input('logo', _('Site logo'), 'Logo for the site (full URL)');
$this->unli();
$this->li();
$this->input('ssllogo', _('SSL logo'), 'Logo to show on SSL pages');
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');

View File

@ -139,7 +139,42 @@ class Design extends Memcached_DataObject
static function url($filename)
{
$path = common_config('background', 'path');
if (StatusNet::isHTTPS()) {
$sslserver = common_config('background', 'sslserver');
if (empty($sslserver)) {
// XXX: this assumes that background dir == site dir + /background/
// not true if there's another server
if (is_string(common_config('site', 'sslserver')) &&
mb_strlen(common_config('site', 'sslserver')) > 0) {
$server = common_config('site', 'sslserver');
} else if (common_config('site', 'server')) {
$server = common_config('site', 'server');
}
$path = common_config('site', 'path') . '/background/';
} else {
$server = $sslserver;
$path = common_config('background', 'sslpath');
if (empty($path)) {
$path = common_config('background', 'path');
}
}
$protocol = 'https';
} else {
$path = common_config('background', 'path');
$server = common_config('background', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$protocol = 'http';
}
if ($path[strlen($path)-1] != '/') {
$path .= '/';
@ -149,25 +184,6 @@ class Design extends Memcached_DataObject
$path = '/'.$path;
}
$server = common_config('background', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('background', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('background', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
return $protocol.'://'.$server.$path.$filename;
}

View File

@ -261,22 +261,41 @@ class File extends Memcached_DataObject
// TRANS: Client exception thrown if a file upload does not have a valid name.
throw new ClientException(_("Invalid filename."));
}
if(common_config('site','private')) {
if (common_config('site','private')) {
return common_local_url('getfile',
array('filename' => $filename));
}
if (StatusNet::isHTTPS()) {
$sslserver = common_config('attachments', 'sslserver');
if (empty($sslserver)) {
// XXX: this assumes that background dir == site dir + /file/
// not true if there's another server
if (is_string(common_config('site', 'sslserver')) &&
mb_strlen(common_config('site', 'sslserver')) > 0) {
$server = common_config('site', 'sslserver');
} else if (common_config('site', 'server')) {
$server = common_config('site', 'server');
}
$path = common_config('site', 'path') . '/file/';
} else {
$server = $sslserver;
$path = common_config('attachments', 'sslpath');
if (empty($path)) {
$path = common_config('attachments', 'path');
}
}
$protocol = 'https';
} else {
$path = common_config('attachments', 'path');
if ($path[strlen($path)-1] != '/') {
$path .= '/';
}
if ($path[0] != '/') {
$path = '/'.$path;
}
$server = common_config('attachments', 'server');
if (empty($server)) {
@ -285,19 +304,18 @@ class File extends Memcached_DataObject
$ssl = common_config('attachments', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('attachments', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
return $protocol.'://'.$server.$path.$filename;
}
if ($path[strlen($path)-1] != '/') {
$path .= '/';
}
if ($path[0] != '/') {
$path = '/'.$path;
}
return $protocol.'://'.$server.$path.$filename;
}
function getEnclosure(){

View File

@ -338,13 +338,27 @@ class Memcached_DataObject extends Safe_DataObject
}
$start = microtime(true);
$result = parent::_query($string);
$fail = false;
try {
$result = parent::_query($string);
} catch (Exception $e) {
$fail = $e;
}
$delta = microtime(true) - $start;
$limit = common_config('db', 'log_slow_queries');
if (($limit > 0 && $delta >= $limit) || common_config('db', 'log_queries')) {
$clean = $this->sanitizeQuery($string);
common_log(LOG_DEBUG, sprintf("DB query (%0.3fs): %s", $delta, $clean));
if ($fail) {
$msg = sprintf("FAILED DB query (%0.3fs): %s - %s", $delta, $fail->getMessage(), $clean);
} else {
$msg = sprintf("DB query (%0.3fs): %s", $delta, $clean);
}
common_log(LOG_DEBUG, $msg);
}
if ($fail) {
throw $fail;
}
return $result;
}

View File

@ -559,16 +559,27 @@ class User_group extends Memcached_DataObject
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();
if ($inst->find()) {
while ($inst->fetch()) {
$dup = clone($inst);
$dup->delete();
}
}
}
// And related groups in the other direction...
@ -584,6 +595,10 @@ class User_group extends Memcached_DataObject
if ($local) {
$local->delete();
}
// blow the cached ids
self::blow('user_group:notice_ids:%d', $this->id);
} else {
common_log(LOG_WARN, "Ambiguous user_group->delete(); skipping related tables.");
}

File diff suppressed because it is too large Load Diff

View File

@ -175,8 +175,9 @@ class Action extends HTMLOutputter // lawsuit
$this->element('link', array('rel' => 'shortcut icon',
'href' => Theme::path('favicon.ico')));
} else {
// favicon.ico should be HTTPS if the rest of the page is
$this->element('link', array('rel' => 'shortcut icon',
'href' => common_path('favicon.ico')));
'href' => common_path('favicon.ico', StatusNet::isHTTPS())));
}
if (common_config('site', 'mobile')) {
@ -397,7 +398,10 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowSiteNotice', array($this));
}
if (common_logged_in()) {
$this->showNoticeForm();
if (Event::handle('StartShowNoticeForm', array($this))) {
$this->showNoticeForm();
Event::handle('EndShowNoticeForm', array($this));
}
} else {
$this->showAnonymousMessage();
}
@ -422,11 +426,35 @@ class Action extends HTMLOutputter // lawsuit
}
$this->elementStart('a', array('class' => 'url home bookmark',
'href' => $url));
if (common_config('site', 'logo') || file_exists(Theme::file('logo.png'))) {
if (StatusNet::isHTTPS()) {
$logoUrl = common_config('site', 'ssllogo');
if (empty($logoUrl)) {
// if logo is an uploaded file, try to fall back to HTTPS file URL
$httpUrl = common_config('site', 'logo');
if (!empty($httpUrl)) {
$f = File::staticGet('url', $httpUrl);
if (!empty($f) && !empty($f->filename)) {
// this will handle the HTTPS case
$logoUrl = File::url($f->filename);
}
}
}
} else {
$logoUrl = common_config('site', 'logo');
}
if (empty($logoUrl) && file_exists(Theme::file('logo.png'))) {
// This should handle the HTTPS case internally
$logoUrl = Theme::path('logo.png');
}
if (!empty($logoUrl)) {
$this->element('img', array('class' => 'logo photo',
'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'),
'src' => $logoUrl,
'alt' => common_config('site', 'name')));
}
$this->text(' ');
$this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
$this->elementEnd('a');
@ -891,8 +919,26 @@ class Action extends HTMLOutputter // lawsuit
case 'cc': // fall through
default:
$this->elementStart('p');
$image = common_config('license', 'image');
$sslimage = common_config('license', 'sslimage');
if (StatusNet::isHTTPS()) {
if (!empty($sslimage)) {
$url = $sslimage;
} else if (preg_match('#^http://i.creativecommons.org/#', $image)) {
// CC support HTTPS on their images
$url = preg_replace('/^http/', 'https', $image);
} else {
// Better to show mixed content than no content
$url = $image;
}
} else {
$url = $image;
}
$this->element('img', array('id' => 'license_cc',
'src' => common_config('license', 'image'),
'src' => $url,
'alt' => common_config('license', 'title'),
'width' => '80',
'height' => '15'));

View File

@ -1246,23 +1246,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.
* Base action for API OAuth enpoints. Clean up the
* 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']);
function getCallback($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar($url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v));
$queryArray = explode('&', $_SERVER['QUERY_STRING']);
for ($i = 0; $i < sizeof($queryArray); $i++) {
if (substr($queryArray[$i], 0, 2) == 'p=') {
unset($queryArray[$i]);
}
}
return $url;
$_SERVER['QUERY_STRING'] = implode('&', $queryArray);
}
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
$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)) {
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;
@ -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()) {
@ -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

@ -118,9 +118,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')
);

View File

@ -37,6 +37,7 @@ $default =
'path' => $_path,
'logfile' => null,
'logo' => null,
'ssllogo' => null,
'logdebug' => false,
'fancy' => false,
'locale_path' => INSTALLDIR.'/locale',
@ -210,6 +211,8 @@ $default =
array('server' => null,
'dir' => INSTALLDIR . '/file/',
'path' => $_path . '/file/',
'sslserver' => null,
'sslpath' => null,
'ssl' => null,
'supported' => array('image/png',
'image/jpeg',

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

View File

@ -352,22 +352,57 @@ class HTMLOutputter extends XMLOutputter
*/
function script($src, $type='text/javascript')
{
if(Event::handle('StartScriptElement', array($this,&$src,&$type))) {
if (Event::handle('StartScriptElement', array($this,&$src,&$type))) {
$url = parse_url($src);
if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment']))
{
if (empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) {
// XXX: this seems like a big assumption
if (strpos($src, 'plugins/') === 0 || strpos($src, 'local/') === 0) {
$src = common_path($src) . '?version=' . STATUSNET_VERSION;
$src = common_path($src, StatusNet::isHTTPS()) . '?version=' . STATUSNET_VERSION;
}else{
} else {
$path = common_config('javascript', 'path');
if (StatusNet::isHTTPS()) {
if (empty($path)) {
$path = common_config('site', 'path') . '/js/';
$sslserver = common_config('javascript', 'sslserver');
if (empty($sslserver)) {
if (is_string(common_config('site', 'sslserver')) &&
mb_strlen(common_config('site', 'sslserver')) > 0) {
$server = common_config('site', 'sslserver');
} else if (common_config('site', 'server')) {
$server = common_config('site', 'server');
}
$path = common_config('site', 'path') . '/js/';
} else {
$server = $sslserver;
$path = common_config('javascript', 'sslpath');
if (empty($path)) {
$path = common_config('javascript', 'path');
}
}
$protocol = 'https';
} else {
$path = common_config('javascript', 'path');
if (empty($path)) {
$path = common_config('site', 'path') . '/js/';
}
$server = common_config('javascript', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$protocol = 'http';
}
if ($path[strlen($path)-1] != '/') {
@ -378,32 +413,13 @@ class HTMLOutputter extends XMLOutputter
$path = '/'.$path;
}
$server = common_config('javascript', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('javascript', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('javascript', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
$src = $protocol.'://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
}
}
$this->element('script', array('type' => $type,
'src' => $src),
' ');
'src' => $src),
' ');
Event::handle('EndScriptElement', array($this,$src,$type));
}
@ -453,7 +469,7 @@ class HTMLOutputter extends XMLOutputter
if(file_exists(Theme::file($src,$theme))){
$src = Theme::path($src, $theme);
}else{
$src = common_path($src);
$src = common_path($src, StatusNet::isHTTPS());
}
$src.= '?version=' . STATUSNET_VERSION;
}

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

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

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

@ -169,7 +169,6 @@ class StatusNet
return $sites;
}
/**
* Fire initialization events for all instantiated plugins.
*/
@ -225,7 +224,7 @@ class StatusNet
{
return self::$is_api;
}
public function setApi($mode)
{
self::$is_api = $mode;
@ -387,6 +386,18 @@ class StatusNet
}
}
}
/**
* Are we running from the web with HTTPS?
*
* @return boolean true if we're running with HTTPS; else false
*/
static function isHTTPS()
{
// There are some exceptions to this; add them here!
return $_SERVER['HTTPS'];
}
}
class NoConfigException extends Exception

View File

@ -38,7 +38,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* Themes are directories with some expected sub-directories and files
* in them. They're found in either local/theme (for locally-installed themes)
* or theme/ subdir of installation dir.
*
*
* Note that the 'local' directory can be overridden as $config['local']['path']
* and $config['local']['dir'] etc.
*
@ -104,25 +104,61 @@ class Theme
/**
* Build a full URL to the given theme's base directory, possibly
* using an offsite theme server path.
*
*
* @param string $group configuration section name to pull paths from
* @param string $fallbackSubdir default subdirectory under INSTALLDIR
* @param string $name theme name
*
*
* @return string URL
*
*
* @todo consolidate code with that for other customizable paths
*/
protected function relativeThemePath($group, $fallbackSubdir, $name)
{
$path = common_config($group, 'path');
if (StatusNet::isHTTPS()) {
if (empty($path)) {
$path = common_config('site', 'path') . '/';
if ($fallbackSubdir) {
$path .= $fallbackSubdir . '/';
$sslserver = common_config($group, 'sslserver');
if (empty($sslserver)) {
if (is_string(common_config('site', 'sslserver')) &&
mb_strlen(common_config('site', 'sslserver')) > 0) {
$server = common_config('site', 'sslserver');
} else if (common_config('site', 'server')) {
$server = common_config('site', 'server');
}
$path = common_config('site', 'path') . '/';
if ($fallbackSubdir) {
$path .= $fallbackSubdir . '/';
}
} else {
$server = $sslserver;
$path = common_config($group, 'sslpath');
if (empty($path)) {
$path = common_config($group, 'path');
}
}
$protocol = 'https';
} else {
$path = common_config($group, 'path');
if (empty($path)) {
$path = common_config('site', 'path') . '/';
if ($fallbackSubdir) {
$path .= $fallbackSubdir . '/';
}
}
$server = common_config($group, 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$protocol = 'http';
}
if ($path[strlen($path)-1] != '/') {
@ -133,27 +169,7 @@ class Theme
$path = '/'.$path;
}
$server = common_config($group, 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config($group, 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config($group, 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
$path = $protocol . '://'.$server.$path.$name;
return $path;
return $protocol.'://'.$server.$path.$name;
}
/**
@ -221,7 +237,7 @@ class Theme
/**
* Pull data from the theme's theme.ini file.
* @fixme calling getFile will fall back to default theme, this may be unsafe.
*
*
* @return associative array of strings
*/
function getMetadata()

View File

@ -41,7 +41,7 @@ class ModHelperPlugin extends Plugin
function onUserRightsCheck($profile, $right, &$result)
{
if ($right == Right::SILENCEUSER || $right == Right::SANDBOXUSER) {
if ($right == Right::SILENCEUSER) {
// Hrm.... really we should confirm that the *other* user isn't privleged. :)
if ($profile->hasRole('modhelper')) {
$result = true;

View File

@ -46,7 +46,15 @@ class UserxrdAction extends XrdAction
}
} else {
$this->user = User::staticGet('uri', $this->uri);
if (empty($this->user)) {
// try and get it by profile url
$profile = Profile::staticGet('profileurl', $this->uri);
if (!empty($profile)) {
$this->user = User::staticGet('id', $profile->id);
}
}
}
if (!$this->user) {
$this->clientError(_m('No such user.'), 404);
return false;

View File

@ -1015,22 +1015,27 @@ class Ostatus_profile extends Managed_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;
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 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));
// @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

@ -36,7 +36,8 @@ class XrdAction extends Action
function handle()
{
$nick = $this->user->nickname;
$nick = $this->user->nickname;
$profile = $this->user->getProfile();
if (empty($this->xrd)) {
$xrd = new XRD();
@ -47,10 +48,28 @@ class XrdAction extends Action
if (empty($xrd->subject)) {
$xrd->subject = Discovery::normalize($this->uri);
}
$xrd->alias[] = $this->user->uri;
// Possible aliases for the user
$uris = array($this->user->uri, $profile->profileurl);
// FIXME: Webfinger generation code should live somewhere on its own
$path = common_config('site', 'path');
if (empty($path)) {
$uris[] = sprintf('acct:%s@%s', $nick, common_config('site', 'server'));
}
foreach ($uris as $uri) {
if ($uri != $xrd->subject) {
$xrd->alias[] = $uri;
}
}
$xrd->links[] = array('rel' => Discovery::PROFILEPAGE,
'type' => 'text/html',
'href' => $this->user->uri);
'href' => $profile->profileurl);
$xrd->links[] = array('rel' => Discovery::UPDATESFROM,
'href' => common_local_url('ApiTimelineUser',
@ -66,7 +85,7 @@ class XrdAction extends Action
// XFN
$xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11',
'type' => 'text/html',
'href' => $this->user->uri);
'href' => $profile->profileurl);
// FOAF
$xrd->links[] = array('rel' => 'describedby',
'type' => 'application/rdf+xml',

View File

@ -2,7 +2,8 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
* Plugin that requires the user to have a validated email address before they can post notices
* Plugin that requires the user to have a validated email address before they
* can post notices
*
* PHP version 5
*
@ -32,44 +33,64 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Plugin for requiring a validated email before posting.
*
* Enable this plugin using addPlugin('RequireValidatedEmail');
*
* @category Plugin
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Brion Vibber <brion@status.net>
* @author Evan Prodromou <evan@status.net>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @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/
*/
class RequireValidatedEmailPlugin extends Plugin
{
// Users created before this time will be grandfathered in
// without the validation requirement.
public $grandfatherCutoff=null;
/**
* Users created before this time will be grandfathered in
* without the validation requirement.
*/
// If OpenID plugin is installed, users with a verified OpenID
// association whose provider URL matches one of these regexes
// will be considered to be sufficiently valid for our needs.
//
// For example, to trust WikiHow and Wikipedia OpenID users:
//
// addPlugin('RequireValidatedEmailPlugin', array(
// 'trustedOpenIDs' => array(
// '!^http://\w+\.wikihow\.com/!',
// '!^http://\w+\.wikipedia\.org/!',
// ),
// ));
public $trustedOpenIDs=array();
public $grandfatherCutoff = null;
function __construct()
{
parent::__construct();
}
/**
* If OpenID plugin is installed, users with a verified OpenID
* association whose provider URL matches one of these regexes
* will be considered to be sufficiently valid for our needs.
*
* For example, to trust WikiHow and Wikipedia OpenID users:
*
* addPlugin('RequireValidatedEmailPlugin', array(
* 'trustedOpenIDs' => array(
* '!^http://\w+\.wikihow\.com/!',
* '!^http://\w+\.wikipedia\.org/!',
* ),
* ));
*/
public $trustedOpenIDs = array();
/**
* Event handler for notice saves; rejects the notice
* if user's address isn't validated.
*
* @param Notice $notice
* @param Notice $notice The notice being saved
*
* @return bool hook result code
*/
function onStartNoticeSave($notice)
{
$user = User::staticGet('id', $notice->profile_id);
if (!empty($user)) { // it's a remote notice
if (!$this->validated($user)) {
throw new ClientException(_m("You must validate your email address before posting."));
$msg = _m("You must validate your email address before posting.");
throw new ClientException($msg);
}
}
return true;
@ -79,7 +100,8 @@ class RequireValidatedEmailPlugin extends Plugin
* Event handler for registration attempts; rejects the registration
* if email field is missing.
*
* @param RegisterAction $action
* @param Action $action Action being executed
*
* @return bool hook result code
*/
function onStartRegistrationTry($action)
@ -100,7 +122,8 @@ class RequireValidatedEmailPlugin extends Plugin
* Check if a user has a validated email address or has been
* otherwise grandfathered in.
*
* @param User $user
* @param User $user User to valide
*
* @return bool
*/
protected function validated($user)
@ -108,12 +131,16 @@ class RequireValidatedEmailPlugin extends Plugin
// The email field is only stored after validation...
// Until then you'll find them in confirm_address.
$knownGood = !empty($user->email) ||
$this->grandfathered($user) ||
$this->hasTrustedOpenID($user);
$this->grandfathered($user) ||
$this->hasTrustedOpenID($user);
// Give other plugins a chance to override, if they can validate
// that somebody's ok despite a non-validated email.
Event::handle('RequireValidatedEmailPlugin_Override', array($user, &$knownGood));
// FIXME: This isn't how to do it! Use Start*/End* instead
Event::handle('RequireValidatedEmailPlugin_Override',
array($user, &$knownGood));
return $knownGood;
}
@ -122,14 +149,15 @@ class RequireValidatedEmailPlugin extends Plugin
* Check if a user was created before the grandfathering cutoff.
* If so, we won't need to check for validation.
*
* @param User $user
* @return bool
* @param User $user User to check
*
* @return bool true if user is grandfathered
*/
protected function grandfathered($user)
{
if ($this->grandfatherCutoff) {
$created = strtotime($user->created . " GMT");
$cutoff = strtotime($this->grandfatherCutoff);
$cutoff = strtotime($this->grandfatherCutoff);
if ($created < $cutoff) {
return true;
}
@ -141,13 +169,20 @@ class RequireValidatedEmailPlugin extends Plugin
* Override for RequireValidatedEmail plugin. If we have a user who's
* not validated an e-mail, but did come from a trusted provider,
* we'll consider them ok.
*
* @param User $user User to check
*
* @return bool true if user has a trusted OpenID.
*/
function hasTrustedOpenID($user)
{
if ($this->trustedOpenIDs && class_exists('User_openid')) {
foreach ($this->trustedOpenIDs as $regex) {
$oid = new User_openid();
$oid->user_id = $user->id;
$oid->find();
while ($oid->fetch()) {
if (preg_match($regex, $oid->canonical)) {
@ -159,14 +194,45 @@ class RequireValidatedEmailPlugin extends Plugin
return false;
}
/**
* Add version information for this plugin.
*
* @param array &$versions Array of associative arrays of version data
*
* @return boolean hook value
*/
function onPluginVersion(&$versions)
{
$versions[] = array('name' => 'Require Validated Email',
'version' => STATUSNET_VERSION,
'author' => 'Craig Andrews, Evan Prodromou, Brion Vibber',
'homepage' => 'http://status.net/wiki/Plugin:RequireValidatedEmail',
'rawdescription' =>
_m('The Require Validated Email plugin disables posting for accounts that do not have a validated email address.'));
$versions[] =
array('name' => 'Require Validated Email',
'version' => STATUSNET_VERSION,
'author' => 'Craig Andrews, '.
'Evan Prodromou, '.
'Brion Vibber',
'homepage' =>
'http://status.net/wiki/Plugin:RequireValidatedEmail',
'rawdescription' =>
_m('Disables posting without a validated email address.'));
return true;
}
/**
* Hide the notice form if the user isn't able to post.
*
* @param Action $action action being shown
*
* @return boolean hook value
*/
function onStartShowNoticeForm($action)
{
$user = common_current_user();
if (!empty($user)) { // it's a remote notice
if (!$this->validated($user)) {
return false;
}
}
return true;
}
}

View File

@ -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));
try {
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?
$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 = $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));
$profile->setOriginal($filename);
}
}

View File

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

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,16 +22,16 @@ 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
-u --update status update
-t --oauth_token access token
-s --oauth_token_secret access token secret
-u --update status update
END_OF_VERIFY_HELP;
@ -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')) {
@ -69,47 +69,56 @@ if (empty($update)) {
exit(1);
}
$ini = parse_ini_file("oauth.ini");
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$ini = parse_ini_file("oauth.ini");
$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(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
$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']);
$ini = parse_ini_file("oauth.ini");
$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(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
$request->setConfig(
array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
)
);
return $request->get($url);
}