forked from GNUsocial/gnu-social
Replace own OMB stack with libomb.
This commit is contained in:
parent
ee9bfa7158
commit
ec88d2650e
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Access token class.
|
||||
* Access token class
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
@ -32,10 +32,11 @@ if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
|
||||
/**
|
||||
* Access token class.
|
||||
* Access token class
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
@ -47,28 +48,23 @@ require_once INSTALLDIR.'/lib/omb.php';
|
||||
class AccesstokenAction extends Action
|
||||
{
|
||||
/**
|
||||
* Class handler.
|
||||
* Class handler
|
||||
*
|
||||
* @param array $args query arguments
|
||||
*
|
||||
* @return boolean false if user doesn't exist
|
||||
*/
|
||||
* @return nothing
|
||||
*
|
||||
**/
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
try {
|
||||
common_debug('getting request from env variables', __FILE__);
|
||||
common_remove_magic_from_request();
|
||||
$req = OAuthRequest::from_request('POST', common_local_url('accesstoken'));
|
||||
common_debug('getting a server', __FILE__);
|
||||
$server = omb_oauth_server();
|
||||
common_debug('fetching the access token', __FILE__);
|
||||
$token = $server->fetch_access_token($req);
|
||||
common_debug('got this token: "'.print_r($token, true).'"', __FILE__);
|
||||
common_debug('printing the access token', __FILE__);
|
||||
print $token;
|
||||
} catch (OAuthException $e) {
|
||||
$srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
|
||||
omb_oauth_server());
|
||||
$srv->writeAccessToken();
|
||||
} catch (Exception $e) {
|
||||
$this->serverError($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,5 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
/**
|
||||
* Handler for remote subscription finish callback
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*
|
||||
* Laconica - a distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 2009, Control Yourself, Inc.
|
||||
*
|
||||
@ -15,285 +26,116 @@
|
||||
*
|
||||
* 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('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_consumer.php';
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
|
||||
/**
|
||||
* Handler for remote subscription finish callback
|
||||
*
|
||||
* When a remote user subscribes a local user, a redirect to this action is
|
||||
* issued after the remote user authorized his service to subscribe.
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
|
||||
require_once(INSTALLDIR.'/lib/omb.php');
|
||||
|
||||
class FinishremotesubscribeAction extends Action
|
||||
{
|
||||
|
||||
/**
|
||||
* Class handler.
|
||||
*
|
||||
* @param array $args query arguments
|
||||
*
|
||||
* @return nothing
|
||||
*
|
||||
**/
|
||||
function handle($args)
|
||||
{
|
||||
|
||||
parent::handle($args);
|
||||
|
||||
if (common_logged_in()) {
|
||||
$this->clientError(_('You can use the local subscription!'));
|
||||
return;
|
||||
}
|
||||
/* Restore session data. RemotesubscribeAction should have stored
|
||||
this entry. */
|
||||
$service = unserialize($_SESSION['oauth_authorization_request']);
|
||||
|
||||
$omb = $_SESSION['oauth_authorization_request'];
|
||||
|
||||
if (!$omb) {
|
||||
if (!$service) {
|
||||
$this->clientError(_('Not expecting this response!'));
|
||||
return;
|
||||
}
|
||||
|
||||
common_debug('stored request: '.print_r($omb,true), __FILE__);
|
||||
common_debug('stored request: '. print_r($service, true), __FILE__);
|
||||
|
||||
common_remove_magic_from_request();
|
||||
$req = OAuthRequest::from_request('POST', common_local_url('finishuserauthorization'));
|
||||
|
||||
$token = $req->get_parameter('oauth_token');
|
||||
|
||||
# I think this is the success metric
|
||||
|
||||
if ($token != $omb['token']) {
|
||||
$this->clientError(_('Not authorized.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$version = $req->get_parameter('omb_version');
|
||||
|
||||
if ($version != OMB_VERSION_01) {
|
||||
$this->clientError(_('Unknown version of OMB protocol.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$nickname = $req->get_parameter('omb_listener_nickname');
|
||||
|
||||
if (!$nickname) {
|
||||
$this->clientError(_('No nickname provided by remote server.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$profile_url = $req->get_parameter('omb_listener_profile');
|
||||
|
||||
if (!$profile_url) {
|
||||
$this->clientError(_('No profile URL returned by server.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Validate::uri($profile_url, array('allowed_schemes' => array('http', 'https')))) {
|
||||
$this->clientError(_('Invalid profile URL returned by server.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($profile_url == common_local_url('showstream', array('nickname' => $nickname))) {
|
||||
$this->clientError(_('You can use the local subscription!'));
|
||||
return;
|
||||
}
|
||||
|
||||
common_debug('listenee: "'.$omb['listenee'].'"', __FILE__);
|
||||
|
||||
$user = User::staticGet('nickname', $omb['listenee']);
|
||||
/* Create user objects for both users. Do it early for request
|
||||
validation. */
|
||||
$listenee = $service->getListeneeURI();
|
||||
$user = User::staticGet('uri', $listenee);
|
||||
|
||||
if (!$user) {
|
||||
$this->clientError(_('User being listened to doesn\'t exist.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$other = User::staticGet('uri', $omb['listener']);
|
||||
$other = User::staticGet('uri', $service->getListenerURI());
|
||||
|
||||
if ($other) {
|
||||
$this->clientError(_('You can use the local subscription!'));
|
||||
return;
|
||||
}
|
||||
|
||||
$fullname = $req->get_parameter('omb_listener_fullname');
|
||||
$homepage = $req->get_parameter('omb_listener_homepage');
|
||||
$bio = $req->get_parameter('omb_listener_bio');
|
||||
$location = $req->get_parameter('omb_listener_location');
|
||||
$avatar_url = $req->get_parameter('omb_listener_avatar');
|
||||
|
||||
list($newtok, $newsecret) = $this->access_token($omb);
|
||||
|
||||
if (!$newtok || !$newsecret) {
|
||||
$this->clientError(_('Couldn\'t convert request tokens to access tokens.'));
|
||||
/* Perform the handling itself via libomb. */
|
||||
try {
|
||||
$service->finishAuthorization($listenee);
|
||||
} catch (OAuthException $e) {
|
||||
if ($e->getMessage() == 'The authorized token does not equal the ' .
|
||||
'submitted token.') {
|
||||
$this->clientError(_('Not authorized.'));
|
||||
return;
|
||||
} else {
|
||||
$this->clientError(_('Couldn\'t convert request token to ' .
|
||||
'access token.'));
|
||||
return;
|
||||
}
|
||||
} catch (OMB_RemoteServiceException $e) {
|
||||
$this->clientError(_('Unknown version of OMB protocol.'));
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
common_debug('Got exception ' . print_r($e, true), __FILE__);
|
||||
$this->clientError($e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
# XXX: possible attack point; subscribe and return someone else's profile URI
|
||||
/* The service URLs are not accessible from datastore, so setting them
|
||||
after insertion of the profile. */
|
||||
$remote = Remote_profile::staticGet('uri', $service->getListenerURI());
|
||||
|
||||
$remote = Remote_profile::staticGet('uri', $omb['listener']);
|
||||
$orig_remote = clone($remote);
|
||||
|
||||
if ($remote) {
|
||||
$exists = true;
|
||||
$profile = Profile::staticGet($remote->id);
|
||||
$orig_remote = clone($remote);
|
||||
$orig_profile = clone($profile);
|
||||
# XXX: compare current postNotice and updateProfile URLs to the ones
|
||||
# stored in the DB to avoid (possibly...) above attack
|
||||
} else {
|
||||
$exists = false;
|
||||
$remote = new Remote_profile();
|
||||
$remote->uri = $omb['listener'];
|
||||
$profile = new Profile();
|
||||
}
|
||||
$remote->postnoticeurl =
|
||||
$service->getServiceURI(OMB_ENDPOINT_POSTNOTICE);
|
||||
$remote->updateprofileurl =
|
||||
$service->getServiceURI(OMB_ENDPOINT_UPDATEPROFILE);
|
||||
|
||||
$profile->nickname = $nickname;
|
||||
$profile->profileurl = $profile_url;
|
||||
|
||||
if (!is_null($fullname)) {
|
||||
$profile->fullname = $fullname;
|
||||
}
|
||||
if (!is_null($homepage)) {
|
||||
$profile->homepage = $homepage;
|
||||
}
|
||||
if (!is_null($bio)) {
|
||||
$profile->bio = $bio;
|
||||
}
|
||||
if (!is_null($location)) {
|
||||
$profile->location = $location;
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
$profile->update($orig_profile);
|
||||
} else {
|
||||
$profile->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
$id = $profile->insert();
|
||||
if (!$id) {
|
||||
$this->serverError(_('Error inserting new profile'));
|
||||
return;
|
||||
}
|
||||
$remote->id = $id;
|
||||
}
|
||||
|
||||
if ($avatar_url) {
|
||||
if (!$this->add_avatar($profile, $avatar_url)) {
|
||||
$this->serverError(_('Error inserting avatar'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$remote->postnoticeurl = $omb['post_notice_url'];
|
||||
$remote->updateprofileurl = $omb['update_profile_url'];
|
||||
|
||||
if ($exists) {
|
||||
if (!$remote->update($orig_remote)) {
|
||||
if (!$remote->update($orig_remote)) {
|
||||
$this->serverError(_('Error updating remote profile'));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$remote->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
if (!$remote->insert()) {
|
||||
$this->serverError(_('Error inserting remote profile'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($user->hasBlocked($profile)) {
|
||||
$this->clientError(_('That user has blocked you from subscribing.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$sub = new Subscription();
|
||||
|
||||
$sub->subscriber = $remote->id;
|
||||
$sub->subscribed = $user->id;
|
||||
|
||||
$sub_exists = false;
|
||||
|
||||
if ($sub->find(true)) {
|
||||
$sub_exists = true;
|
||||
$orig_sub = clone($sub);
|
||||
} else {
|
||||
$sub_exists = false;
|
||||
$sub->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
}
|
||||
|
||||
$sub->token = $newtok;
|
||||
$sub->secret = $newsecret;
|
||||
|
||||
if ($sub_exists) {
|
||||
$result = $sub->update($orig_sub);
|
||||
} else {
|
||||
$result = $sub->insert();
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
|
||||
$this->clientError(_('Couldn\'t insert new subscription.'));
|
||||
return;
|
||||
}
|
||||
|
||||
# Notify user, if necessary
|
||||
|
||||
mail_subscribe_notify_profile($user, $profile);
|
||||
|
||||
# Clear the data
|
||||
/* Clear the session data. */
|
||||
unset($_SESSION['oauth_authorization_request']);
|
||||
|
||||
# If we show subscriptions in reverse chron order, this should
|
||||
# show up close to the top of the page
|
||||
|
||||
/* If we show subscriptions in reverse chronological order, the new one
|
||||
should show up close to the top of the page. */
|
||||
common_redirect(common_local_url('subscribers', array('nickname' =>
|
||||
$user->nickname)),
|
||||
303);
|
||||
}
|
||||
|
||||
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));
|
||||
return $profile->setOriginal($filename);
|
||||
}
|
||||
|
||||
function access_token($omb)
|
||||
{
|
||||
|
||||
common_debug('starting request for access token', __FILE__);
|
||||
|
||||
$con = omb_oauth_consumer();
|
||||
$tok = new OAuthToken($omb['token'], $omb['secret']);
|
||||
|
||||
common_debug('using request token "'.$tok.'"', __FILE__);
|
||||
|
||||
$url = $omb['access_token_url'];
|
||||
|
||||
common_debug('using access token url "'.$url.'"', __FILE__);
|
||||
|
||||
# XXX: Is this the right thing to do? Strip off GET params and make them
|
||||
# POST params? Seems wrong to me.
|
||||
|
||||
$parsed = parse_url($url);
|
||||
$params = array();
|
||||
parse_str($parsed['query'], $params);
|
||||
|
||||
$req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params);
|
||||
|
||||
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||
|
||||
# XXX: test to see if endpoint accepts this signature method
|
||||
|
||||
$req->sign_request(omb_hmac_sha1(), $con, $tok);
|
||||
|
||||
# We re-use this tool's fetcher, since it's pretty good
|
||||
|
||||
common_debug('posting to access token url "'.$req->get_normalized_http_url().'"', __FILE__);
|
||||
common_debug('posting request data "'.$req->to_postdata().'"', __FILE__);
|
||||
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
$result = $fetcher->post($req->get_normalized_http_url(),
|
||||
$req->to_postdata(),
|
||||
array('User-Agent: Laconica/' . LACONICA_VERSION));
|
||||
|
||||
common_debug('got result: "'.print_r($result,true).'"', __FILE__);
|
||||
|
||||
if ($result->status != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
parse_str($result->body, $return);
|
||||
|
||||
return array($return['oauth_token'], $return['oauth_token_secret']);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
/**
|
||||
* Handle postnotice action
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*
|
||||
* Laconica - a distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 2009, Control Yourself, Inc.
|
||||
*
|
||||
@ -17,75 +28,49 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once(INSTALLDIR.'/lib/omb.php');
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
|
||||
|
||||
/**
|
||||
* Handler for postnotice action
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*/
|
||||
class PostnoticeAction extends Action
|
||||
{
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
if (!$this->checkNotice()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
common_remove_magic_from_request();
|
||||
$req = OAuthRequest::from_request('POST', common_local_url('postnotice'));
|
||||
# Note: server-to-server function!
|
||||
$server = omb_oauth_server();
|
||||
list($consumer, $token) = $server->verify_request($req);
|
||||
if ($this->save_notice($req, $consumer, $token)) {
|
||||
print "omb_version=".OMB_VERSION_01;
|
||||
}
|
||||
} catch (OAuthException $e) {
|
||||
$srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
|
||||
omb_oauth_server());
|
||||
$srv->handlePostNotice();
|
||||
} catch (Exception $e) {
|
||||
$this->serverError($e->getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function save_notice(&$req, &$consumer, &$token)
|
||||
function checkNotice()
|
||||
{
|
||||
$version = $req->get_parameter('omb_version');
|
||||
if ($version != OMB_VERSION_01) {
|
||||
$this->clientError(_('Unsupported OMB version'), 400);
|
||||
return false;
|
||||
}
|
||||
# First, check to see
|
||||
$listenee = $req->get_parameter('omb_listenee');
|
||||
$remote_profile = Remote_profile::staticGet('uri', $listenee);
|
||||
if (!$remote_profile) {
|
||||
$this->clientError(_('Profile unknown'), 403);
|
||||
return false;
|
||||
}
|
||||
$sub = Subscription::staticGet('token', $token->key);
|
||||
if (!$sub) {
|
||||
$this->clientError(_('No such subscription'), 403);
|
||||
return false;
|
||||
}
|
||||
$content = $req->get_parameter('omb_notice_content');
|
||||
$content_shortened = common_shorten_links($content);
|
||||
if (mb_strlen($content_shortened) > 140) {
|
||||
$content = common_shorten_links($_POST['omb_notice_content']);
|
||||
if (mb_strlen($content) > 140) {
|
||||
$this->clientError(_('Invalid notice content'), 400);
|
||||
return false;
|
||||
}
|
||||
$notice_uri = $req->get_parameter('omb_notice');
|
||||
if (!Validate::uri($notice_uri) &&
|
||||
!common_valid_tag($notice_uri)) {
|
||||
$this->clientError(_('Invalid notice uri'), 400);
|
||||
return false;
|
||||
}
|
||||
$notice_url = $req->get_parameter('omb_notice_url');
|
||||
if ($notice_url && !common_valid_http_url($notice_url)) {
|
||||
$this->clientError(_('Invalid notice url'), 400);
|
||||
return false;
|
||||
}
|
||||
$notice = Notice::staticGet('uri', $notice_uri);
|
||||
if (!$notice) {
|
||||
$notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, null, $notice_uri);
|
||||
if (is_string($notice)) {
|
||||
common_server_serror($notice, 500);
|
||||
return false;
|
||||
}
|
||||
common_broadcast_notice($notice, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,5 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
/**
|
||||
* Handler for remote subscription
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*
|
||||
* Laconica - a distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 2009, Control Yourself, Inc.
|
||||
*
|
||||
@ -15,12 +26,27 @@
|
||||
*
|
||||
* 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('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_consumer.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/profile.php';
|
||||
|
||||
/**
|
||||
* Handler for remote subscription
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
|
||||
require_once(INSTALLDIR.'/lib/omb.php');
|
||||
|
||||
class RemotesubscribeAction extends Action
|
||||
{
|
||||
var $nickname;
|
||||
@ -36,7 +62,7 @@ class RemotesubscribeAction extends Action
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->nickname = $this->trimmed('nickname');
|
||||
$this->nickname = $this->trimmed('nickname');
|
||||
$this->profile_url = $this->trimmed('profile_url');
|
||||
|
||||
return true;
|
||||
@ -47,7 +73,7 @@ class RemotesubscribeAction extends Action
|
||||
parent::handle($args);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
# CSRF protection
|
||||
/* Use a session token for CSRF protection. */
|
||||
$token = $this->trimmed('token');
|
||||
if (!$token || $token != common_session_token()) {
|
||||
$this->showForm(_('There was a problem with your session token. '.
|
||||
@ -90,8 +116,8 @@ class RemotesubscribeAction extends Action
|
||||
|
||||
function showContent()
|
||||
{
|
||||
# id = remotesubscribe conflicts with the
|
||||
# button on profile page
|
||||
/* The id 'remotesubscribe' conflicts with the
|
||||
button on profile page. */
|
||||
$this->elementStart('form', array('id' => 'form_remote_subscribe',
|
||||
'method' => 'post',
|
||||
'class' => 'form_settings',
|
||||
@ -117,13 +143,13 @@ class RemotesubscribeAction extends Action
|
||||
|
||||
function remoteSubscription()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
if (!$user) {
|
||||
if (!$this->nickname) {
|
||||
$this->showForm(_('No such user.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::staticGet('nickname', $this->nickname);
|
||||
|
||||
$this->profile_url = $this->trimmed('profile_url');
|
||||
|
||||
if (!$this->profile_url) {
|
||||
@ -131,233 +157,37 @@ class RemotesubscribeAction extends Action
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Validate::uri($this->profile_url, array('allowed_schemes' => array('http', 'https')))) {
|
||||
if (!Validate::uri($this->profile_url,
|
||||
array('allowed_schemes' => array('http', 'https')))) {
|
||||
$this->showForm(_('Invalid profile URL (bad format)'));
|
||||
return;
|
||||
}
|
||||
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
$yadis = Auth_Yadis_Yadis::discover($this->profile_url, $fetcher);
|
||||
|
||||
if (!$yadis || $yadis->failed) {
|
||||
$this->showForm(_('Not a valid profile URL (no YADIS document).'));
|
||||
try {
|
||||
$service = new OMB_Service_Consumer($this->profile_url,
|
||||
common_root_url(),
|
||||
omb_oauth_datastore());
|
||||
} catch (OMB_InvalidYadisException $e) {
|
||||
$this->showForm(_('Not a valid profile URL (no YADIS document or ' .
|
||||
'no or invalid XRDS defined).'));
|
||||
return;
|
||||
}
|
||||
|
||||
# XXX: a little liberal for sites that accidentally put whitespace before the xml declaration
|
||||
|
||||
$xrds =& Auth_Yadis_XRDS::parseXRDS(trim($yadis->response_text));
|
||||
|
||||
if (!$xrds) {
|
||||
$this->showForm(_('Not a valid profile URL (no XRDS defined).'));
|
||||
return;
|
||||
}
|
||||
|
||||
$omb = $this->getOmb($xrds);
|
||||
|
||||
if (!$omb) {
|
||||
$this->showForm(_('Not a valid profile URL (incorrect services).'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]) ==
|
||||
common_local_url('requesttoken'))
|
||||
{
|
||||
if ($service->getServiceURI(OAUTH_ENDPOINT_REQUEST) ==
|
||||
common_local_url('requesttoken') ||
|
||||
User::staticGet('uri', $service->getRemoteUserURI())) {
|
||||
$this->showForm(_('That\'s a local profile! Login to subscribe.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (User::staticGet('uri', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]))) {
|
||||
$this->showForm(_('That\'s a local profile! Login to subscribe.'));
|
||||
return;
|
||||
}
|
||||
|
||||
list($token, $secret) = $this->requestToken($omb);
|
||||
|
||||
if (!$token || !$secret) {
|
||||
try {
|
||||
$service->requestToken();
|
||||
} catch (OMB_RemoteServiceException $e) {
|
||||
$this->showForm(_('Couldn\'t get a request token.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->requestAuthorization($user, $omb, $token, $secret);
|
||||
}
|
||||
|
||||
function getUser()
|
||||
{
|
||||
$user = null;
|
||||
if ($this->nickname) {
|
||||
$user = User::staticGet('nickname', $this->nickname);
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
function getOmb($xrds)
|
||||
{
|
||||
static $omb_endpoints = array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE);
|
||||
static $oauth_endpoints = array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE,
|
||||
OAUTH_ENDPOINT_ACCESS);
|
||||
$omb = array();
|
||||
|
||||
# XXX: the following code could probably be refactored to eliminate dupes
|
||||
|
||||
$oauth_services = omb_get_services($xrds, OAUTH_DISCOVERY);
|
||||
|
||||
if (!$oauth_services) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$oauth_service = $oauth_services[0];
|
||||
|
||||
$oauth_xrd = $this->getXRD($oauth_service, $xrds);
|
||||
|
||||
if (!$oauth_xrd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->addServices($oauth_xrd, $oauth_endpoints, $omb)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$omb_services = omb_get_services($xrds, OMB_NAMESPACE);
|
||||
|
||||
if (!$omb_services) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$omb_service = $omb_services[0];
|
||||
|
||||
$omb_xrd = $this->getXRD($omb_service, $xrds);
|
||||
|
||||
if (!$omb_xrd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->addServices($omb_xrd, $omb_endpoints, $omb)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
# XXX: check that we got all the services we needed
|
||||
|
||||
foreach (array_merge($omb_endpoints, $oauth_endpoints) as $type) {
|
||||
if (!array_key_exists($type, $omb) || !$omb[$type]) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!omb_local_id($omb[OAUTH_ENDPOINT_REQUEST])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $omb;
|
||||
}
|
||||
|
||||
function getXRD($main_service, $main_xrds)
|
||||
{
|
||||
$uri = omb_service_uri($main_service);
|
||||
if (strpos($uri, "#") !== 0) {
|
||||
# FIXME: more rigorous handling of external service definitions
|
||||
return null;
|
||||
}
|
||||
$id = substr($uri, 1);
|
||||
$nodes = $main_xrds->allXrdNodes;
|
||||
$parser = $main_xrds->parser;
|
||||
foreach ($nodes as $node) {
|
||||
$attrs = $parser->attributes($node);
|
||||
if (array_key_exists('xml:id', $attrs) &&
|
||||
$attrs['xml:id'] == $id) {
|
||||
# XXX: trick the constructor into thinking this is the only node
|
||||
$bogus_nodes = array($node);
|
||||
return new Auth_Yadis_XRDS($parser, $bogus_nodes);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function addServices($xrd, $types, &$omb)
|
||||
{
|
||||
foreach ($types as $type) {
|
||||
$matches = omb_get_services($xrd, $type);
|
||||
if ($matches) {
|
||||
$omb[$type] = $matches[0];
|
||||
} else {
|
||||
# no match for type
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function requestToken($omb)
|
||||
{
|
||||
$con = omb_oauth_consumer();
|
||||
|
||||
$url = omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]);
|
||||
|
||||
# XXX: Is this the right thing to do? Strip off GET params and make them
|
||||
# POST params? Seems wrong to me.
|
||||
|
||||
$parsed = parse_url($url);
|
||||
$params = array();
|
||||
parse_str($parsed['query'], $params);
|
||||
|
||||
$req = OAuthRequest::from_consumer_and_token($con, null, "POST", $url, $params);
|
||||
|
||||
$listener = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]);
|
||||
|
||||
if (!$listener) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$req->set_parameter('omb_listener', $listener);
|
||||
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||
|
||||
# XXX: test to see if endpoint accepts this signature method
|
||||
|
||||
$req->sign_request(omb_hmac_sha1(), $con, null);
|
||||
|
||||
# We re-use this tool's fetcher, since it's pretty good
|
||||
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
|
||||
$result = $fetcher->post($req->get_normalized_http_url(),
|
||||
$req->to_postdata(),
|
||||
array('User-Agent: Laconica/' . LACONICA_VERSION));
|
||||
if ($result->status != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
parse_str($result->body, $return);
|
||||
|
||||
return array($return['oauth_token'], $return['oauth_token_secret']);
|
||||
}
|
||||
|
||||
function requestAuthorization($user, $omb, $token, $secret)
|
||||
{
|
||||
$con = omb_oauth_consumer();
|
||||
$tok = new OAuthToken($token, $secret);
|
||||
|
||||
$url = omb_service_uri($omb[OAUTH_ENDPOINT_AUTHORIZE]);
|
||||
|
||||
# XXX: Is this the right thing to do? Strip off GET params and make them
|
||||
# POST params? Seems wrong to me.
|
||||
|
||||
$parsed = parse_url($url);
|
||||
$params = array();
|
||||
parse_str($parsed['query'], $params);
|
||||
|
||||
$req = OAuthRequest::from_consumer_and_token($con, $tok, 'GET', $url, $params);
|
||||
|
||||
# We send over a ton of information. This lets the other
|
||||
# server store info about our user, and it lets the current
|
||||
# user decide if they really want to authorize the subscription.
|
||||
|
||||
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||
$req->set_parameter('omb_listener', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]));
|
||||
$req->set_parameter('omb_listenee', $user->uri);
|
||||
$req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
|
||||
$req->set_parameter('omb_listenee_nickname', $user->nickname);
|
||||
$req->set_parameter('omb_listenee_license', common_config('license', 'url'));
|
||||
|
||||
/* Create an OMB_Profile from $user. */
|
||||
$profile = $user->getProfile();
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
@ -365,49 +195,16 @@ class RemotesubscribeAction extends Action
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_null($profile->fullname)) {
|
||||
$req->set_parameter('omb_listenee_fullname', $profile->fullname);
|
||||
}
|
||||
if (!is_null($profile->homepage)) {
|
||||
$req->set_parameter('omb_listenee_homepage', $profile->homepage);
|
||||
}
|
||||
if (!is_null($profile->bio)) {
|
||||
$req->set_parameter('omb_listenee_bio', $profile->bio);
|
||||
}
|
||||
if (!is_null($profile->location)) {
|
||||
$req->set_parameter('omb_listenee_location', $profile->location);
|
||||
}
|
||||
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||
if ($avatar) {
|
||||
$req->set_parameter('omb_listenee_avatar', $avatar->url);
|
||||
}
|
||||
|
||||
# XXX: add a nonce to prevent replay attacks
|
||||
|
||||
$req->set_parameter('oauth_callback', common_local_url('finishremotesubscribe'));
|
||||
|
||||
# XXX: test to see if endpoint accepts this signature method
|
||||
|
||||
$req->sign_request(omb_hmac_sha1(), $con, $tok);
|
||||
|
||||
# store all our info here
|
||||
|
||||
$omb['listenee'] = $user->nickname;
|
||||
$omb['listener'] = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]);
|
||||
$omb['token'] = $token;
|
||||
$omb['secret'] = $secret;
|
||||
# call doesn't work after bounce back so we cache; maybe serialization issue...?
|
||||
$omb['access_token_url'] = omb_service_uri($omb[OAUTH_ENDPOINT_ACCESS]);
|
||||
$omb['post_notice_url'] = omb_service_uri($omb[OMB_ENDPOINT_POSTNOTICE]);
|
||||
$omb['update_profile_url'] = omb_service_uri($omb[OMB_ENDPOINT_UPDATEPROFILE]);
|
||||
$target_url = $service->requestAuthorization(
|
||||
profile_to_omb_profile($user->uri, $profile),
|
||||
common_local_url('finishremotesubscribe'));
|
||||
|
||||
common_ensure_session();
|
||||
|
||||
$_SESSION['oauth_authorization_request'] = $omb;
|
||||
$_SESSION['oauth_authorization_request'] = serialize($service);
|
||||
|
||||
# Redirect to authorization service
|
||||
|
||||
common_redirect($req->to_url(), 303);
|
||||
return;
|
||||
/* Redirect to the remote service for authorization. */
|
||||
common_redirect($target_url, 303);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -34,6 +34,7 @@ if (!defined('LACONICA')) {
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
|
||||
|
||||
/**
|
||||
* Request token action class.
|
||||
@ -49,17 +50,17 @@ class RequesttokenAction extends Action
|
||||
{
|
||||
/**
|
||||
* Is read only?
|
||||
*
|
||||
*
|
||||
* @return boolean false
|
||||
*/
|
||||
function isReadOnly($args)
|
||||
function isReadOnly()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class handler.
|
||||
*
|
||||
*
|
||||
* @param array $args array of arguments
|
||||
*
|
||||
* @return void
|
||||
@ -68,14 +69,12 @@ class RequesttokenAction extends Action
|
||||
{
|
||||
parent::handle($args);
|
||||
try {
|
||||
common_remove_magic_from_request();
|
||||
$req = OAuthRequest::from_request('POST', common_local_url('requesttoken'));
|
||||
$server = omb_oauth_server();
|
||||
$token = $server->fetch_request_token($req);
|
||||
print $token;
|
||||
} catch (OAuthException $e) {
|
||||
$srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
|
||||
omb_oauth_server());
|
||||
$srv->writeRequestToken();
|
||||
} catch (Exception $e) {
|
||||
$this->serverError($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -1,5 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
/**
|
||||
* Handle an updateprofile action
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*
|
||||
* Laconica - a distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 2009, Control Yourself, Inc.
|
||||
*
|
||||
@ -17,167 +28,37 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once(INSTALLDIR.'/lib/omb.php');
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
|
||||
|
||||
/**
|
||||
* Handle an updateprofile action
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*/
|
||||
class UpdateprofileAction extends Action
|
||||
{
|
||||
|
||||
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
try {
|
||||
common_remove_magic_from_request();
|
||||
$req = OAuthRequest::from_request('POST', common_local_url('updateprofile'));
|
||||
# Note: server-to-server function!
|
||||
$server = omb_oauth_server();
|
||||
list($consumer, $token) = $server->verify_request($req);
|
||||
if ($this->update_profile($req, $consumer, $token)) {
|
||||
header('HTTP/1.1 200 OK');
|
||||
header('Content-type: text/plain');
|
||||
print "omb_version=".OMB_VERSION_01;
|
||||
}
|
||||
} catch (OAuthException $e) {
|
||||
$srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
|
||||
omb_oauth_server());
|
||||
$srv->handleUpdateProfile();
|
||||
} catch (Exception $e) {
|
||||
$this->serverError($e->getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function update_profile($req, $consumer, $token)
|
||||
{
|
||||
$version = $req->get_parameter('omb_version');
|
||||
if ($version != OMB_VERSION_01) {
|
||||
$this->clientError(_('Unsupported OMB version'), 400);
|
||||
return false;
|
||||
}
|
||||
# First, check to see if listenee exists
|
||||
$listenee = $req->get_parameter('omb_listenee');
|
||||
$remote = Remote_profile::staticGet('uri', $listenee);
|
||||
if (!$remote) {
|
||||
$this->clientError(_('Profile unknown'), 404);
|
||||
return false;
|
||||
}
|
||||
# Second, check to see if they should be able to post updates!
|
||||
# We see if there are any subscriptions to that remote user with
|
||||
# the given token.
|
||||
|
||||
$sub = new Subscription();
|
||||
$sub->subscribed = $remote->id;
|
||||
$sub->token = $token->key;
|
||||
if (!$sub->find(true)) {
|
||||
$this->clientError(_('You did not send us that profile'), 403);
|
||||
return false;
|
||||
}
|
||||
|
||||
$profile = Profile::staticGet('id', $remote->id);
|
||||
if (!$profile) {
|
||||
# This one is our fault
|
||||
$this->serverError(_('Remote profile with no matching profile'), 500);
|
||||
return false;
|
||||
}
|
||||
$nickname = $req->get_parameter('omb_listenee_nickname');
|
||||
if ($nickname && !Validate::string($nickname, array('min_length' => 1,
|
||||
'max_length' => 64,
|
||||
'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) {
|
||||
$this->clientError(_('Nickname must have only lowercase letters and numbers and no spaces.'));
|
||||
return false;
|
||||
}
|
||||
$license = $req->get_parameter('omb_listenee_license');
|
||||
if ($license && !common_valid_http_url($license)) {
|
||||
$this->clientError(sprintf(_("Invalid license URL '%s'"), $license));
|
||||
return false;
|
||||
}
|
||||
$profile_url = $req->get_parameter('omb_listenee_profile');
|
||||
if ($profile_url && !common_valid_http_url($profile_url)) {
|
||||
$this->clientError(sprintf(_("Invalid profile URL '%s'."), $profile_url));
|
||||
return false;
|
||||
}
|
||||
# optional stuff
|
||||
$fullname = $req->get_parameter('omb_listenee_fullname');
|
||||
if ($fullname && mb_strlen($fullname) > 255) {
|
||||
$this->clientError(_("Full name is too long (max 255 chars)."));
|
||||
return false;
|
||||
}
|
||||
$homepage = $req->get_parameter('omb_listenee_homepage');
|
||||
if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
|
||||
$this->clientError(sprintf(_("Invalid homepage '%s'"), $homepage));
|
||||
return false;
|
||||
}
|
||||
$bio = $req->get_parameter('omb_listenee_bio');
|
||||
if ($bio && mb_strlen($bio) > 140) {
|
||||
$this->clientError(_("Bio is too long (max 140 chars)."));
|
||||
return false;
|
||||
}
|
||||
$location = $req->get_parameter('omb_listenee_location');
|
||||
if ($location && mb_strlen($location) > 255) {
|
||||
$this->clientError(_("Location is too long (max 255 chars)."));
|
||||
return false;
|
||||
}
|
||||
$avatar = $req->get_parameter('omb_listenee_avatar');
|
||||
if ($avatar) {
|
||||
if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
|
||||
$this->clientError(sprintf(_("Invalid avatar URL '%s'"), $avatar));
|
||||
return false;
|
||||
}
|
||||
$size = @getimagesize($avatar);
|
||||
if (!$size) {
|
||||
$this->clientError(sprintf(_("Can't read avatar URL '%s'"), $avatar));
|
||||
return false;
|
||||
}
|
||||
if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) {
|
||||
$this->clientError(sprintf(_("Wrong size image at '%s'"), $avatar));
|
||||
return false;
|
||||
}
|
||||
if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG,
|
||||
IMAGETYPE_PNG))) {
|
||||
$this->clientError(sprintf(_("Wrong image type for '%s'"), $avatar));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$orig_profile = clone($profile);
|
||||
|
||||
/* Use values even if they are an empty string. Parsing an empty string in
|
||||
updateProfile is the specified way of clearing a parameter in OMB. */
|
||||
if (!is_null($nickname)) {
|
||||
$profile->nickname = $nickname;
|
||||
}
|
||||
if (!is_null($profile_url)) {
|
||||
$profile->profileurl = $profile_url;
|
||||
}
|
||||
if (!is_null($fullname)) {
|
||||
$profile->fullname = $fullname;
|
||||
}
|
||||
if (!is_null($homepage)) {
|
||||
$profile->homepage = $homepage;
|
||||
}
|
||||
if (!is_null($bio)) {
|
||||
$profile->bio = $bio;
|
||||
}
|
||||
if (!is_null($location)) {
|
||||
$profile->location = $location;
|
||||
}
|
||||
|
||||
if (!$profile->update($orig_profile)) {
|
||||
$this->serverError(_('Could not save new profile info'), 500);
|
||||
return false;
|
||||
} else {
|
||||
if ($avatar) {
|
||||
$temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
|
||||
copy($avatar, $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));
|
||||
if (!$profile->setOriginal($filename)) {
|
||||
$this->serverError(_('Could not save avatar info'), 500);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,5 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
/**
|
||||
* Let the user authorize a remote subscription request
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://laconi.ca/
|
||||
*
|
||||
* Laconica - a distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 2009, Control Yourself, Inc.
|
||||
*
|
||||
@ -17,9 +28,13 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once(INSTALLDIR.'/lib/omb.php');
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/profile.php';
|
||||
define('TIMESTAMP_THRESHOLD', 300);
|
||||
|
||||
class UserauthorizationAction extends Action
|
||||
@ -32,42 +47,58 @@ class UserauthorizationAction extends Action
|
||||
parent::handle($args);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
# CSRF protection
|
||||
/* Use a session token for CSRF protection. */
|
||||
$token = $this->trimmed('token');
|
||||
if (!$token || $token != common_session_token()) {
|
||||
$params = $this->getStoredParams();
|
||||
$this->showForm($params, _('There was a problem with your session token. '.
|
||||
'Try again, please.'));
|
||||
$srv = $this->getStoredParams();
|
||||
$this->showForm($srv->getRemoteUser(), _('There was a problem ' .
|
||||
'with your session token. Try again, ' .
|
||||
'please.'));
|
||||
return;
|
||||
}
|
||||
# We've shown the form, now post user's choice
|
||||
/* We've shown the form, now post user's choice. */
|
||||
$this->sendAuthorization();
|
||||
} else {
|
||||
if (!common_logged_in()) {
|
||||
# Go log in, and then come back
|
||||
/* Go log in, and then come back. */
|
||||
common_set_returnto($_SERVER['REQUEST_URI']);
|
||||
|
||||
common_redirect(common_local_url('login'));
|
||||
return;
|
||||
}
|
||||
|
||||
$user = common_current_user();
|
||||
$profile = $user->getProfile();
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
$this->serverError(_('User without matching profile'));
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: If no token is passed the user should get a prompt to enter
|
||||
it according to OAuth Core 1.0. */
|
||||
try {
|
||||
$this->validateRequest();
|
||||
$this->storeParams($_GET);
|
||||
$this->showForm($_GET);
|
||||
} catch (OAuthException $e) {
|
||||
$this->validateOmb();
|
||||
$srv = new OMB_Service_Provider(
|
||||
profile_to_omb_profile($_GET['omb_listener'], $profile),
|
||||
omb_oauth_datastore());
|
||||
|
||||
$remote_user = $srv->handleUserAuth();
|
||||
} catch (Exception $e) {
|
||||
$this->clearParams();
|
||||
$this->clientError($e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->storeParams($srv);
|
||||
$this->showForm($remote_user);
|
||||
}
|
||||
}
|
||||
|
||||
function showForm($params, $error=null)
|
||||
{
|
||||
$this->params = $params;
|
||||
$this->error = $error;
|
||||
$this->error = $error;
|
||||
$this->showPage();
|
||||
}
|
||||
|
||||
@ -79,23 +110,24 @@ class UserauthorizationAction extends Action
|
||||
function showPageNotice()
|
||||
{
|
||||
$this->element('p', null, _('Please check these details to make sure '.
|
||||
'that you want to subscribe to this user\'s notices. '.
|
||||
'If you didn\'t just ask to subscribe to someone\'s notices, '.
|
||||
'click "Reject".'));
|
||||
'that you want to subscribe to this ' .
|
||||
'user\'s notices. If you didn\'t just ask ' .
|
||||
'to subscribe to someone\'s notices, '.
|
||||
'click “Reject”.'));
|
||||
}
|
||||
|
||||
function showContent()
|
||||
{
|
||||
$params = $this->params;
|
||||
|
||||
$nickname = $params['omb_listenee_nickname'];
|
||||
$profile = $params['omb_listenee_profile'];
|
||||
$license = $params['omb_listenee_license'];
|
||||
$fullname = $params['omb_listenee_fullname'];
|
||||
$homepage = $params['omb_listenee_homepage'];
|
||||
$bio = $params['omb_listenee_bio'];
|
||||
$location = $params['omb_listenee_location'];
|
||||
$avatar = $params['omb_listenee_avatar'];
|
||||
$nickname = $params->getNickname();
|
||||
$profile = $params->getProfileURL();
|
||||
$license = $params->getLicenseURL();
|
||||
$fullname = $params->getFullname();
|
||||
$homepage = $params->getHomepage();
|
||||
$bio = $params->getBio();
|
||||
$location = $params->getLocation();
|
||||
$avatar = $params->getAvatarURL();
|
||||
|
||||
$this->elementStart('div', array('class' => 'profile'));
|
||||
$this->elementStart('div', 'entity_profile vcard');
|
||||
@ -172,11 +204,14 @@ class UserauthorizationAction extends Action
|
||||
'id' => 'userauthorization',
|
||||
'class' => 'form_user_authorization',
|
||||
'name' => 'userauthorization',
|
||||
'action' => common_local_url('userauthorization')));
|
||||
'action' => common_local_url(
|
||||
'userauthorization')));
|
||||
$this->hidden('token', common_session_token());
|
||||
|
||||
$this->submit('accept', _('Accept'), 'submit accept', null, _('Subscribe to this user'));
|
||||
$this->submit('reject', _('Reject'), 'submit reject', null, _('Reject this subscription'));
|
||||
$this->submit('accept', _('Accept'), 'submit accept', null,
|
||||
_('Subscribe to this user'));
|
||||
$this->submit('reject', _('Reject'), 'submit reject', null,
|
||||
_('Reject this subscription'));
|
||||
$this->elementEnd('form');
|
||||
$this->elementEnd('li');
|
||||
$this->elementEnd('ul');
|
||||
@ -186,218 +221,56 @@ class UserauthorizationAction extends Action
|
||||
|
||||
function sendAuthorization()
|
||||
{
|
||||
$params = $this->getStoredParams();
|
||||
$srv = $this->getStoredParams();
|
||||
|
||||
if (!$params) {
|
||||
if (is_null($srv)) {
|
||||
$this->clientError(_('No authorization request!'));
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = $params['oauth_callback'];
|
||||
|
||||
if ($this->arg('accept')) {
|
||||
if (!$this->authorizeToken($params)) {
|
||||
$this->clientError(_('Error authorizing token'));
|
||||
}
|
||||
if (!$this->saveRemoteProfile($params)) {
|
||||
$this->clientError(_('Error saving remote profile'));
|
||||
}
|
||||
if (!$callback) {
|
||||
$this->showAcceptMessage($params['oauth_token']);
|
||||
} else {
|
||||
$newparams = array();
|
||||
$newparams['oauth_token'] = $params['oauth_token'];
|
||||
$newparams['omb_version'] = OMB_VERSION_01;
|
||||
$user = User::staticGet('uri', $params['omb_listener']);
|
||||
$profile = $user->getProfile();
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
$this->serverError(_('User without matching profile'));
|
||||
return;
|
||||
}
|
||||
$newparams['omb_listener_nickname'] = $user->nickname;
|
||||
$newparams['omb_listener_profile'] = common_local_url('showstream',
|
||||
array('nickname' => $user->nickname));
|
||||
if (!is_null($profile->fullname)) {
|
||||
$newparams['omb_listener_fullname'] = $profile->fullname;
|
||||
}
|
||||
if (!is_null($profile->homepage)) {
|
||||
$newparams['omb_listener_homepage'] = $profile->homepage;
|
||||
}
|
||||
if (!is_null($profile->bio)) {
|
||||
$newparams['omb_listener_bio'] = $profile->bio;
|
||||
}
|
||||
if (!is_null($profile->location)) {
|
||||
$newparams['omb_listener_location'] = $profile->location;
|
||||
}
|
||||
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||
if ($avatar) {
|
||||
$newparams['omb_listener_avatar'] = $avatar->url;
|
||||
}
|
||||
$parts = array();
|
||||
foreach ($newparams as $k => $v) {
|
||||
$parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
|
||||
}
|
||||
$query_string = implode('&', $parts);
|
||||
$parsed = parse_url($callback);
|
||||
$url = $callback . (($parsed['query']) ? '&' : '?') . $query_string;
|
||||
common_redirect($url, 303);
|
||||
}
|
||||
$accepted = $this->arg('accept');
|
||||
try {
|
||||
list($val, $token) = $srv->continueUserAuth($accepted);
|
||||
} catch (Exception $e) {
|
||||
$this->clientError($e->getMessage());
|
||||
return;
|
||||
}
|
||||
if ($val !== false) {
|
||||
common_redirect($val, 303);
|
||||
} elseif ($accepted) {
|
||||
$this->showAcceptMessage($token);
|
||||
} else {
|
||||
if (!$callback) {
|
||||
$this->showRejectMessage();
|
||||
} else {
|
||||
# XXX: not 100% sure how to signal failure... just redirect without token?
|
||||
common_redirect($callback, 303);
|
||||
}
|
||||
$this->showRejectMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function authorizeToken(&$params)
|
||||
{
|
||||
$token_field = $params['oauth_token'];
|
||||
$rt = new Token();
|
||||
$rt->tok = $token_field;
|
||||
$rt->type = 0;
|
||||
$rt->state = 0;
|
||||
if ($rt->find(true)) {
|
||||
$orig_rt = clone($rt);
|
||||
$rt->state = 1; # Authorized but not used
|
||||
if ($rt->update($orig_rt)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
# XXX: refactor with similar code in finishremotesubscribe.php
|
||||
|
||||
function saveRemoteProfile(&$params)
|
||||
{
|
||||
# FIXME: we should really do this when the consumer comes
|
||||
# back for an access token. If they never do, we've got stuff in a
|
||||
# weird state.
|
||||
|
||||
$nickname = $params['omb_listenee_nickname'];
|
||||
$fullname = $params['omb_listenee_fullname'];
|
||||
$profile_url = $params['omb_listenee_profile'];
|
||||
$homepage = $params['omb_listenee_homepage'];
|
||||
$bio = $params['omb_listenee_bio'];
|
||||
$location = $params['omb_listenee_location'];
|
||||
$avatar_url = $params['omb_listenee_avatar'];
|
||||
|
||||
$listenee = $params['omb_listenee'];
|
||||
$remote = Remote_profile::staticGet('uri', $listenee);
|
||||
|
||||
if ($remote) {
|
||||
$exists = true;
|
||||
$profile = Profile::staticGet($remote->id);
|
||||
$orig_remote = clone($remote);
|
||||
$orig_profile = clone($profile);
|
||||
} else {
|
||||
$exists = false;
|
||||
$remote = new Remote_profile();
|
||||
$remote->uri = $listenee;
|
||||
$profile = new Profile();
|
||||
}
|
||||
|
||||
$profile->nickname = $nickname;
|
||||
$profile->profileurl = $profile_url;
|
||||
|
||||
if (!is_null($fullname)) {
|
||||
$profile->fullname = $fullname;
|
||||
}
|
||||
if (!is_null($homepage)) {
|
||||
$profile->homepage = $homepage;
|
||||
}
|
||||
if (!is_null($bio)) {
|
||||
$profile->bio = $bio;
|
||||
}
|
||||
if (!is_null($location)) {
|
||||
$profile->location = $location;
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
$profile->update($orig_profile);
|
||||
} else {
|
||||
$profile->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
$id = $profile->insert();
|
||||
if (!$id) {
|
||||
return false;
|
||||
}
|
||||
$remote->id = $id;
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
if (!$remote->update($orig_remote)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$remote->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
if (!$remote->insert()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($avatar_url) {
|
||||
if (!$this->addAvatar($profile, $avatar_url)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$user = common_current_user();
|
||||
|
||||
$sub = new Subscription();
|
||||
$sub->subscriber = $user->id;
|
||||
$sub->subscribed = $remote->id;
|
||||
$sub->token = $params['oauth_token']; # NOTE: request token, not valid for use!
|
||||
$sub->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
|
||||
if (!$sub->insert()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function addAvatar($profile, $url)
|
||||
{
|
||||
$temp_filename = tempnam(sys_get_temp_dir(), 'listenee_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));
|
||||
return $profile->setOriginal($filename);
|
||||
}
|
||||
|
||||
function showAcceptMessage($tok)
|
||||
{
|
||||
common_show_header(_('Subscription authorized'));
|
||||
$this->element('p', null,
|
||||
_('The subscription has been authorized, but no '.
|
||||
'callback URL was passed. Check with the site\'s instructions for '.
|
||||
'details on how to authorize the subscription. Your subscription token is:'));
|
||||
'callback URL was passed. Check with the site\'s ' .
|
||||
'instructions for details on how to authorize the ' .
|
||||
'subscription. Your subscription token is:'));
|
||||
$this->element('blockquote', 'token', $tok);
|
||||
common_show_footer();
|
||||
}
|
||||
|
||||
function showRejectMessage($tok)
|
||||
function showRejectMessage()
|
||||
{
|
||||
common_show_header(_('Subscription rejected'));
|
||||
$this->element('p', null,
|
||||
_('The subscription has been rejected, but no '.
|
||||
'callback URL was passed. Check with the site\'s instructions for '.
|
||||
'details on how to fully reject the subscription.'));
|
||||
'callback URL was passed. Check with the site\'s ' .
|
||||
'instructions for details on how to fully reject ' .
|
||||
'the subscription.'));
|
||||
common_show_footer();
|
||||
}
|
||||
|
||||
function storeParams($params)
|
||||
{
|
||||
common_ensure_session();
|
||||
$_SESSION['userauthorizationparams'] = $params;
|
||||
$_SESSION['userauthorizationparams'] = serialize($params);
|
||||
}
|
||||
|
||||
function clearParams()
|
||||
@ -409,138 +282,65 @@ class UserauthorizationAction extends Action
|
||||
function getStoredParams()
|
||||
{
|
||||
common_ensure_session();
|
||||
$params = $_SESSION['userauthorizationparams'];
|
||||
$params = unserialize($_SESSION['userauthorizationparams']);
|
||||
return $params;
|
||||
}
|
||||
|
||||
# Throws an OAuthException if anything goes wrong
|
||||
|
||||
function validateRequest()
|
||||
{
|
||||
/* Find token.
|
||||
TODO: If no token is passed the user should get a prompt to enter it
|
||||
according to OAuth Core 1.0 */
|
||||
$t = new Token();
|
||||
$t->tok = $_GET['oauth_token'];
|
||||
$t->type = 0;
|
||||
if (!$t->find(true)) {
|
||||
throw new OAuthException("Invalid request token: " . $_GET['oauth_token']);
|
||||
}
|
||||
|
||||
$this->validateOmb();
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateOmb()
|
||||
{
|
||||
foreach (array('omb_version', 'omb_listener', 'omb_listenee',
|
||||
'omb_listenee_profile', 'omb_listenee_nickname',
|
||||
'omb_listenee_license') as $param)
|
||||
{
|
||||
if (!isset($_GET[$param]) || is_null($_GET[$param])) {
|
||||
throw new OAuthException("Required parameter '$param' not found");
|
||||
}
|
||||
}
|
||||
# Now, OMB stuff
|
||||
$version = $_GET['omb_version'];
|
||||
if ($version != OMB_VERSION_01) {
|
||||
throw new OAuthException("OpenMicroBlogging version '$version' not supported");
|
||||
}
|
||||
$listener = $_GET['omb_listener'];
|
||||
$listenee = $_GET['omb_listenee'];
|
||||
$nickname = $_GET['omb_listenee_nickname'];
|
||||
$profile = $_GET['omb_listenee_profile'];
|
||||
|
||||
$user = User::staticGet('uri', $listener);
|
||||
if (!$user) {
|
||||
throw new OAuthException("Listener URI '$listener' not found here");
|
||||
throw new Exception("Listener URI '$listener' not found here");
|
||||
}
|
||||
$cur = common_current_user();
|
||||
if ($cur->id != $user->id) {
|
||||
throw new OAuthException("Can't add for another user!");
|
||||
}
|
||||
$listenee = $_GET['omb_listenee'];
|
||||
if (!Validate::uri($listenee) &&
|
||||
!common_valid_tag($listenee)) {
|
||||
throw new OAuthException("Listenee URI '$listenee' not a recognizable URI");
|
||||
}
|
||||
if (strlen($listenee) > 255) {
|
||||
throw new OAuthException("Listenee URI '$listenee' too long");
|
||||
throw new Exception('Can\'t subscribe for another user!');
|
||||
}
|
||||
|
||||
$other = User::staticGet('uri', $listenee);
|
||||
if ($other) {
|
||||
throw new OAuthException("Listenee URI '$listenee' is local user");
|
||||
throw new Exception("Listenee URI '$listenee' is local user");
|
||||
}
|
||||
|
||||
$remote = Remote_profile::staticGet('uri', $listenee);
|
||||
if ($remote) {
|
||||
$sub = new Subscription();
|
||||
$sub = new Subscription();
|
||||
$sub->subscriber = $user->id;
|
||||
$sub->subscribed = $remote->id;
|
||||
if ($sub->find(true)) {
|
||||
throw new OAuthException("Already subscribed to user!");
|
||||
throw new Exception('You are already subscribed to this user.');
|
||||
}
|
||||
}
|
||||
$nickname = $_GET['omb_listenee_nickname'];
|
||||
if (!Validate::string($nickname, array('min_length' => 1,
|
||||
'max_length' => 64,
|
||||
'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) {
|
||||
throw new OAuthException('Nickname must have only letters and numbers and no spaces.');
|
||||
}
|
||||
$profile = $_GET['omb_listenee_profile'];
|
||||
if (!common_valid_http_url($profile)) {
|
||||
throw new OAuthException("Invalid profile URL '$profile'.");
|
||||
|
||||
if ($profile == common_profile_url($nickname)) {
|
||||
throw new Exception("Profile URL '$profile' is for a local user.");
|
||||
}
|
||||
|
||||
if ($profile == common_local_url('showstream', array('nickname' => $nickname))) {
|
||||
throw new OAuthException("Profile URL '$profile' is for a local user.");
|
||||
}
|
||||
|
||||
$license = $_GET['omb_listenee_license'];
|
||||
if (!common_valid_http_url($license)) {
|
||||
throw new OAuthException("Invalid license URL '$license'.");
|
||||
}
|
||||
$license = $_GET['omb_listenee_license'];
|
||||
$site_license = common_config('license', 'url');
|
||||
if (!common_compatible_license($license, $site_license)) {
|
||||
throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'.");
|
||||
}
|
||||
# optional stuff
|
||||
$fullname = $_GET['omb_listenee_fullname'];
|
||||
if ($fullname && mb_strlen($fullname) > 255) {
|
||||
throw new OAuthException("Full name '$fullname' too long.");
|
||||
}
|
||||
$homepage = $_GET['omb_listenee_homepage'];
|
||||
if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
|
||||
throw new OAuthException("Invalid homepage '$homepage'");
|
||||
}
|
||||
$bio = $_GET['omb_listenee_bio'];
|
||||
if ($bio && mb_strlen($bio) > 140) {
|
||||
throw new OAuthException("Bio too long '$bio'");
|
||||
}
|
||||
$location = $_GET['omb_listenee_location'];
|
||||
if ($location && mb_strlen($location) > 255) {
|
||||
throw new OAuthException("Location too long '$location'");
|
||||
throw new Exception("Listenee stream license '$license' is not " .
|
||||
"compatible with site license '$site_license'.");
|
||||
}
|
||||
$avatar = $_GET['omb_listenee_avatar'];
|
||||
if ($avatar) {
|
||||
if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
|
||||
throw new OAuthException("Invalid avatar URL '$avatar'");
|
||||
throw new Exception("Invalid avatar URL '$avatar'");
|
||||
}
|
||||
$size = @getimagesize($avatar);
|
||||
if (!$size) {
|
||||
throw new OAuthException("Can't read avatar URL '$avatar'");
|
||||
}
|
||||
if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) {
|
||||
throw new OAuthException("Wrong size image at '$avatar'");
|
||||
throw new Exception("Can't read avatar URL '$avatar'.");
|
||||
}
|
||||
if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG,
|
||||
IMAGETYPE_PNG))) {
|
||||
throw new OAuthException("Wrong image type for '$avatar'");
|
||||
throw new Exception("Wrong image type for '$avatar'");
|
||||
}
|
||||
}
|
||||
$callback = $_GET['oauth_callback'];
|
||||
if ($callback && !common_valid_http_url($callback)) {
|
||||
throw new OAuthException("Invalid callback URL '$callback'");
|
||||
}
|
||||
if ($callback && $callback == common_local_url('finishremotesubscribe')) {
|
||||
throw new OAuthException("Callback URL '$callback' is for local site.");
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
112
actions/xrds.php
112
actions/xrds.php
@ -34,6 +34,8 @@ if (!defined('LACONICA')) {
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
|
||||
require_once INSTALLDIR.'/extlib/libomb/xrds_mapper.php';
|
||||
|
||||
/**
|
||||
* XRDS for OpenID
|
||||
@ -52,7 +54,7 @@ class XrdsAction extends Action
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
function isReadOnly($args)
|
||||
function isReadOnly()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -85,89 +87,31 @@ class XrdsAction extends Action
|
||||
*/
|
||||
function showXrds($user)
|
||||
{
|
||||
header('Content-Type: application/xrds+xml');
|
||||
$this->startXML();
|
||||
$this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
|
||||
|
||||
$this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'xml:id' => 'oauth',
|
||||
'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
|
||||
'version' => '2.0'));
|
||||
$this->element('Type', null, 'xri://$xrds*simple');
|
||||
$this->showService(OAUTH_ENDPOINT_REQUEST,
|
||||
common_local_url('requesttoken'),
|
||||
array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
|
||||
array(OAUTH_HMAC_SHA1),
|
||||
$user->uri);
|
||||
$this->showService(OAUTH_ENDPOINT_AUTHORIZE,
|
||||
common_local_url('userauthorization'),
|
||||
array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
|
||||
array(OAUTH_HMAC_SHA1));
|
||||
$this->showService(OAUTH_ENDPOINT_ACCESS,
|
||||
common_local_url('accesstoken'),
|
||||
array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
|
||||
array(OAUTH_HMAC_SHA1));
|
||||
$this->showService(OAUTH_ENDPOINT_RESOURCE,
|
||||
null,
|
||||
array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
|
||||
array(OAUTH_HMAC_SHA1));
|
||||
$this->elementEnd('XRD');
|
||||
|
||||
// XXX: decide whether to include user's ID/nickname in postNotice URL
|
||||
$this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'xml:id' => 'omb',
|
||||
'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
|
||||
'version' => '2.0'));
|
||||
$this->element('Type', null, 'xri://$xrds*simple');
|
||||
$this->showService(OMB_ENDPOINT_POSTNOTICE,
|
||||
common_local_url('postnotice'));
|
||||
$this->showService(OMB_ENDPOINT_UPDATEPROFILE,
|
||||
common_local_url('updateprofile'));
|
||||
$this->elementEnd('XRD');
|
||||
$this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'version' => '2.0'));
|
||||
$this->element('Type', null, 'xri://$xrds*simple');
|
||||
$this->showService(OAUTH_DISCOVERY,
|
||||
'#oauth');
|
||||
$this->showService(OMB_NAMESPACE,
|
||||
'#omb');
|
||||
$this->elementEnd('XRD');
|
||||
$this->elementEnd('XRDS');
|
||||
$this->endXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show service.
|
||||
*
|
||||
* @param string $type XRDS type
|
||||
* @param string $uri URI
|
||||
* @param array $params type parameters, null by default
|
||||
* @param array $sigs type signatures, null by default
|
||||
* @param string $localId local ID, null by default
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showService($type, $uri, $params=null, $sigs=null, $localId=null)
|
||||
{
|
||||
$this->elementStart('Service');
|
||||
if ($uri) {
|
||||
$this->element('URI', null, $uri);
|
||||
}
|
||||
$this->element('Type', null, $type);
|
||||
if ($params) {
|
||||
foreach ($params as $param) {
|
||||
$this->element('Type', null, $param);
|
||||
}
|
||||
}
|
||||
if ($sigs) {
|
||||
foreach ($sigs as $sig) {
|
||||
$this->element('Type', null, $sig);
|
||||
}
|
||||
}
|
||||
if ($localId) {
|
||||
$this->element('LocalID', null, $localId);
|
||||
}
|
||||
$this->elementEnd('Service');
|
||||
$srv = new OMB_Service_Provider(profile_to_omb_profile($user->uri,
|
||||
$user->getProfile()));
|
||||
/* Use libomb’s default XRDS Writer. */
|
||||
$xrds_writer = null;
|
||||
$srv->writeXRDS(new Laconica_XRDS_Mapper(), $xrds_writer);
|
||||
}
|
||||
}
|
||||
|
||||
class Laconica_XRDS_Mapper implements OMB_XRDS_Mapper
|
||||
{
|
||||
protected $urls;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->urls = array(
|
||||
OAUTH_ENDPOINT_REQUEST => 'requesttoken',
|
||||
OAUTH_ENDPOINT_AUTHORIZE => 'userauthorization',
|
||||
OAUTH_ENDPOINT_ACCESS => 'accesstoken',
|
||||
OMB_ENDPOINT_POSTNOTICE => 'postnotice',
|
||||
OMB_ENDPOINT_UPDATEPROFILE => 'updateprofile');
|
||||
}
|
||||
|
||||
public function getURL($action)
|
||||
{
|
||||
return common_local_url($this->urls[$action]);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
51
extlib/libomb/base_url_xrds_mapper.php
Executable file
51
extlib/libomb/base_url_xrds_mapper.php
Executable file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
require_once 'xrds_mapper.php';
|
||||
require_once 'constants.php';
|
||||
|
||||
/**
|
||||
* Map XRDS actions to URLs using base URLs.
|
||||
*
|
||||
* This interface specifies classes which write the XRDS file announcing
|
||||
* the OMB server. An instance of an implementing class should be passed to
|
||||
* OMB_Service_Provider->writeXRDS.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Base_URL_XRDS_Mapper implements OMB_XRDS_Mapper {
|
||||
|
||||
protected $urls;
|
||||
|
||||
public function __construct($oauth_base, $omb_base) {
|
||||
$this->urls = array(
|
||||
OAUTH_ENDPOINT_REQUEST => $oauth_base . 'requesttoken',
|
||||
OAUTH_ENDPOINT_AUTHORIZE => $oauth_base . 'userauthorization',
|
||||
OAUTH_ENDPOINT_ACCESS => $oauth_base . 'accesstoken',
|
||||
OMB_ENDPOINT_POSTNOTICE => $omb_base . 'postnotice',
|
||||
OMB_ENDPOINT_UPDATEPROFILE => $omb_base . 'updateprofile');
|
||||
}
|
||||
|
||||
public function getURL($action) {
|
||||
return $this->urls[$action];
|
||||
}
|
||||
}
|
||||
?>
|
58
extlib/libomb/constants.php
Normal file
58
extlib/libomb/constants.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Constants for libomb
|
||||
*
|
||||
* This file contains constant definitions for libomb. The defined constants
|
||||
* are service and namespace URIs for OAuth and OMB as used in XRDS.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
/**
|
||||
* The OMB constants.
|
||||
**/
|
||||
|
||||
define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
|
||||
|
||||
/* The OMB version supported by this libomb version. */
|
||||
define('OMB_VERSION', OMB_VERSION_01);
|
||||
|
||||
define('OMB_ENDPOINT_UPDATEPROFILE', OMB_VERSION . '/updateProfile');
|
||||
define('OMB_ENDPOINT_POSTNOTICE', OMB_VERSION . '/postNotice');
|
||||
|
||||
/**
|
||||
* The OAuth constants.
|
||||
**/
|
||||
|
||||
define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
|
||||
|
||||
define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
|
||||
define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
|
||||
define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
|
||||
define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource');
|
||||
|
||||
define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
|
||||
define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
|
||||
|
||||
define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
|
||||
|
||||
define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
|
||||
?>
|
198
extlib/libomb/datastore.php
Executable file
198
extlib/libomb/datastore.php
Executable file
@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
require_once 'OAuth.php';
|
||||
|
||||
/**
|
||||
* Data access interface
|
||||
*
|
||||
* This interface specifies data access methods libomb needs. It
|
||||
* should be implemented by libomb users.
|
||||
* OMB_Datastore is libomb’s main interface to the application’s data.
|
||||
*
|
||||
* It is the user’s duty to signal and handle errors. libomb does not check
|
||||
* return values nor handle exceptions. It is suggested to use exceptions.
|
||||
* Note that lookup_token and getProfile return null if the requested object
|
||||
* is not available. This is NOT an error and should not raise an exception.
|
||||
* Same applies for lookup_nonce which returns a boolean value. These methods
|
||||
* may nevertheless throw an exception, for example in case of a storage error.
|
||||
*
|
||||
* Objects corresponding to this interface are used in OMB_Service_Provider and
|
||||
* OMB_Service_Consumer.
|
||||
*
|
||||
* OMB_Datastore extends OAuthDataStore with two OAuth-related methods for token
|
||||
* revoking and authorizing and all OMB-related methods.
|
||||
* Refer to OAuth.php for a complete specification of OAuth-related methods.
|
||||
*
|
||||
* Note that it’s implemented as a class since OAuthDataStore is as well a
|
||||
* class, though only declaring methods.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Datastore extends OAuthDataStore {
|
||||
|
||||
/*********
|
||||
* OAUTH *
|
||||
*********/
|
||||
|
||||
/**
|
||||
* Revoke specified OAuth token
|
||||
*
|
||||
* Revokes the authorization token specified by $token_key.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $token_key The token to be revoked
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function revoke_token($token_key) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize specified OAuth token
|
||||
*
|
||||
* Authorizes the authorization token specified by $token_key.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $token_key The token to be authorized
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function authorize_token($token_key) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/*********
|
||||
* OMB *
|
||||
*********/
|
||||
|
||||
/**
|
||||
* Get profile by identifying URI
|
||||
*
|
||||
* Returns an OMB_Profile object representing the OMB profile identified by
|
||||
* $identifier_uri.
|
||||
* Returns null if there is no such OMB profile.
|
||||
* Throws exceptions in case of other error.
|
||||
*
|
||||
* @param string $identifier_uri The OMB identifier URI specifying the
|
||||
* requested profile
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OMB_Profile The corresponding profile
|
||||
**/
|
||||
public function getProfile($identifier_uri) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save passed profile
|
||||
*
|
||||
* Stores the OMB profile $profile. Overwrites an existing entry.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param OMB_Profile $profile The OMB profile which should be saved
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function saveProfile($profile) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save passed notice
|
||||
*
|
||||
* Stores the OMB notice $notice. The datastore may change the passed notice.
|
||||
* This might by neccessary for URIs depending on a database key. Note that
|
||||
* it is the user’s duty to present a mechanism for his OMB_Datastore to
|
||||
* appropriately change his OMB_Notice. TODO: Ugly.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param OMB_Notice $notice The OMB notice which should be saved
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function saveNotice(&$notice) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscriptions of a given profile
|
||||
*
|
||||
* Returns an array containing subscription informations for the specified
|
||||
* profile. Every array entry should in turn be an array with keys
|
||||
* 'uri´: The identifier URI of the subscriber
|
||||
* 'token´: The subscribe token
|
||||
* 'secret´: The secret token
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $subscribed_user_uri The OMB identifier URI specifying the
|
||||
* subscribed profile
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return mixed An array containing the subscriptions or 0 if no
|
||||
* subscription has been found.
|
||||
**/
|
||||
public function getSubscriptions($subscribed_user_uri) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a subscription
|
||||
*
|
||||
* Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $subscriber_uri The OMB identifier URI specifying the
|
||||
* subscribing profile
|
||||
*
|
||||
* @param string $subscribed_user_uri The OMB identifier URI specifying the
|
||||
* subscribed profile
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function deleteSubscription($subscriber_uri, $subscribed_user_uri) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a subscription
|
||||
*
|
||||
* Saves the subscription from $subscriber_uri to $subscribed_user_uri.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $subscriber_uri The OMB identifier URI specifying
|
||||
* the subscribing profile
|
||||
*
|
||||
* @param string $subscribed_user_uri The OMB identifier URI specifying
|
||||
* the subscribed profile
|
||||
* @param OAuthToken $token The access token
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function saveSubscription($subscriber_uri, $subscribed_user_uri,
|
||||
$token) {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
?>
|
99
extlib/libomb/helper.php
Normal file
99
extlib/libomb/helper.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
require_once 'Validate.php';
|
||||
|
||||
/**
|
||||
* Helper functions for libomb
|
||||
*
|
||||
* This file contains helper functions for libomb.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Helper {
|
||||
|
||||
/**
|
||||
* Non-scalar constants
|
||||
*
|
||||
* The set of OMB and OAuth Services an OMB Server has to implement.
|
||||
*/
|
||||
|
||||
public static $OMB_SERVICES =
|
||||
array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE);
|
||||
public static $OAUTH_SERVICES =
|
||||
array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE, OAUTH_ENDPOINT_ACCESS);
|
||||
|
||||
/**
|
||||
* Validate URL
|
||||
*
|
||||
* Basic URL validation. Currently http, https, ftp and gopher are supported
|
||||
* schemes.
|
||||
*
|
||||
* @param string $url The URL which is to be validated.
|
||||
*
|
||||
* @return bool Whether URL is valid.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function validateURL($url) {
|
||||
return Validate::uri($url, array('allowed_schemes' => array('http', 'https',
|
||||
'gopher', 'ftp')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Media type
|
||||
*
|
||||
* Basic Media type validation. Checks for valid maintype and correct format.
|
||||
*
|
||||
* @param string $mediatype The Media type which is to be validated.
|
||||
*
|
||||
* @return bool Whether media type is valid.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function validateMediaType($mediatype) {
|
||||
if (0 === preg_match('/^(\w+)\/([\w\d-+.]+)$/', $mediatype, $subtypes)) {
|
||||
return false;
|
||||
}
|
||||
if (!in_array(strtolower($subtypes[1]), array('application', 'audio', 'image',
|
||||
'message', 'model', 'multipart', 'text', 'video'))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove escaping from request parameters
|
||||
*
|
||||
* Neutralise the evil effects of magic_quotes_gpc in the current request.
|
||||
* This is used before handing a request off to OAuthRequest::from_request.
|
||||
* Many thanks to Ciaran Gultnieks for this fix.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function removeMagicQuotesFromRequest() {
|
||||
if(get_magic_quotes_gpc() == 1) {
|
||||
$_POST = array_map('stripslashes', $_POST);
|
||||
$_GET = array_map('stripslashes', $_GET);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
32
extlib/libomb/invalidparameterexception.php
Executable file
32
extlib/libomb/invalidparameterexception.php
Executable file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Exception stating that a passed parameter is invalid
|
||||
*
|
||||
* This exception is raised when a parameter does not obey the OMB standard.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
class OMB_InvalidParameterException extends Exception {
|
||||
public function __construct($value, $type, $parameter) {
|
||||
parent::__construct("Invalid value $value for parameter $parameter in $type");
|
||||
}
|
||||
}
|
||||
?>
|
31
extlib/libomb/invalidyadisexception.php
Executable file
31
extlib/libomb/invalidyadisexception.php
Executable file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Exception stating that a requested url does not resolve to a valid yadis
|
||||
*
|
||||
* This exception is raised when OMB_Service is not able to discover a valid
|
||||
* yadis location with XRDS.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
class OMB_InvalidYadisException extends Exception {
|
||||
|
||||
}
|
||||
?>
|
272
extlib/libomb/notice.php
Executable file
272
extlib/libomb/notice.php
Executable file
@ -0,0 +1,272 @@
|
||||
<?php
|
||||
require_once 'invalidparameterexception.php';
|
||||
require_once 'Validate.php';
|
||||
require_once 'helper.php';
|
||||
|
||||
/**
|
||||
* OMB Notice representation
|
||||
*
|
||||
* This class represents an OMB notice.
|
||||
*
|
||||
* Do not call the setters with null values. Instead, if you want to delete a
|
||||
* field, pass an empty string. The getters will return null for empty fields.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Notice {
|
||||
protected $author;
|
||||
protected $uri;
|
||||
protected $content;
|
||||
protected $url;
|
||||
protected $license_url; /* url is an own addition for clarification. */
|
||||
protected $seealso_url; /* url is an own addition for clarification. */
|
||||
protected $seealso_disposition;
|
||||
protected $seealso_mediatype;
|
||||
protected $seealso_license_url; /* url is an addition for clarification. */
|
||||
|
||||
/* The notice as OMB param array. Cached and rebuild on usage.
|
||||
false while outdated. */
|
||||
protected $param_array;
|
||||
|
||||
/**
|
||||
* Constructor for OMB_Notice
|
||||
*
|
||||
* Initializes the OMB_Notice object with author, uri and content.
|
||||
* These parameters are mandatory for postNotice.
|
||||
*
|
||||
* @param object $author An OMB_Profile object representing the author of the
|
||||
* notice.
|
||||
* @param string $uri The notice URI as defined by the OMB. A unique and
|
||||
* unchanging identifier for a notice.
|
||||
* @param string $content The content of the notice. 140 chars recommended,
|
||||
* but there is no limit.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($author, $uri, $content) {
|
||||
$this->content = $content;
|
||||
if (is_null($author)) {
|
||||
throw new OMB_InvalidParameterException('', 'notice', 'omb_listenee');
|
||||
}
|
||||
$this->author = $author;
|
||||
|
||||
if (!Validate::uri($uri)) {
|
||||
throw new OMB_InvalidParameterException($uri, 'notice', 'omb_notice');
|
||||
}
|
||||
$this->uri = $uri;
|
||||
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notice as array
|
||||
*
|
||||
* The method returns an array which contains the whole notice as array. The
|
||||
* array is cached and only rebuilt on changes of the notice.
|
||||
* Empty optional values are not passed.
|
||||
*
|
||||
* @access public
|
||||
* @returns array The notice as parameter array
|
||||
*/
|
||||
public function asParameters() {
|
||||
if ($this->param_array !== false) {
|
||||
return $this->param_array;
|
||||
}
|
||||
|
||||
$this->param_array = array(
|
||||
'omb_notice' => $this->uri,
|
||||
'omb_notice_content' => $this->content);
|
||||
|
||||
if (!is_null($this->url))
|
||||
$this->param_array['omb_notice_url'] = $this->url;
|
||||
|
||||
if (!is_null($this->license_url))
|
||||
$this->param_array['omb_notice_license'] = $this->license_url;
|
||||
|
||||
if (!is_null($this->seealso_url)) {
|
||||
$this->param_array['omb_seealso'] = $this->seealso_url;
|
||||
|
||||
/* This is actually a free interpretation of the OMB standard. We assume
|
||||
that additional seealso parameters are not of any use if seealso itself
|
||||
is not set. */
|
||||
if (!is_null($this->seealso_disposition))
|
||||
$this->param_array['omb_seealso_disposition'] =
|
||||
$this->seealso_disposition;
|
||||
|
||||
if (!is_null($this->seealso_mediatype))
|
||||
$this->param_array['omb_seealso_mediatype'] = $this->seealso_mediatype;
|
||||
|
||||
if (!is_null($this->seealso_license_url))
|
||||
$this->param_array['omb_seealso_license'] = $this->seealso_license_url;
|
||||
}
|
||||
return $this->param_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an OMB_Notice object from array
|
||||
*
|
||||
* The method builds an OMB_Notice object from the passed parameters array.
|
||||
* The array MUST provide a notice URI and content. The array fields HAVE TO
|
||||
* be named according to the OMB standard, i. e. omb_notice_* and
|
||||
* omb_seealso_*. Values are handled as not passed if the corresponding array
|
||||
* fields are not set or the empty string.
|
||||
*
|
||||
* @param object $author An OMB_Profile object representing the author of
|
||||
* the notice.
|
||||
* @param string $parameters An array containing the notice parameters.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @returns OMB_Notice The built OMB_Notice.
|
||||
*/
|
||||
public static function fromParameters($author, $parameters) {
|
||||
$notice = new OMB_Notice($author, $parameters['omb_notice'],
|
||||
$parameters['omb_notice_content']);
|
||||
|
||||
if (isset($parameters['omb_notice_url'])) {
|
||||
$notice->setURL($parameters['omb_notice_url']);
|
||||
}
|
||||
|
||||
if (isset($parameters['omb_notice_license'])) {
|
||||
$notice->setLicenseURL($parameters['omb_notice_license']);
|
||||
}
|
||||
|
||||
if (isset($parameters['omb_seealso'])) {
|
||||
$notice->setSeealsoURL($parameters['omb_seealso']);
|
||||
}
|
||||
|
||||
if (isset($parameters['omb_seealso_disposition'])) {
|
||||
$notice->setSeealsoDisposition($parameters['omb_seealso_disposition']);
|
||||
}
|
||||
|
||||
if (isset($parameters['omb_seealso_mediatype'])) {
|
||||
$notice->setSeealsoMediatype($parameters['omb_seealso_mediatype']);
|
||||
}
|
||||
|
||||
if (isset($parameters['omb_seealso_license'])) {
|
||||
$notice->setSeealsoLicenseURL($parameters['omb_seealso_license']);
|
||||
}
|
||||
return $notice;
|
||||
}
|
||||
|
||||
public function getAuthor() {
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function getIdentifierURI() {
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function getContent() {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function getURL() {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function getLicenseURL() {
|
||||
return $this->license_url;
|
||||
}
|
||||
|
||||
public function getSeealsoURL() {
|
||||
return $this->seealso_url;
|
||||
}
|
||||
|
||||
public function getSeealsoDisposition() {
|
||||
return $this->seealso_disposition;
|
||||
}
|
||||
|
||||
public function getSeealsoMediatype() {
|
||||
return $this->seealso_mediatype;
|
||||
}
|
||||
|
||||
public function getSeealsoLicenseURL() {
|
||||
return $this->seealso_license_url;
|
||||
}
|
||||
|
||||
public function setURL($url) {
|
||||
if ($url === '') {
|
||||
$url = null;
|
||||
} elseif (!OMB_Helper::validateURL($url)) {
|
||||
throw new OMB_InvalidParameterException($url, 'notice', 'omb_notice_url');
|
||||
}
|
||||
$this->url = $url;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setLicenseURL($license_url) {
|
||||
if ($license_url === '') {
|
||||
$license_url = null;
|
||||
} elseif (!OMB_Helper::validateURL($license_url)) {
|
||||
throw new OMB_InvalidParameterException($license_url, 'notice',
|
||||
'omb_notice_license');
|
||||
}
|
||||
$this->license_url = $license_url;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setSeealsoURL($seealso_url) {
|
||||
if ($seealso_url === '') {
|
||||
$seealso_url = null;
|
||||
} elseif (!OMB_Helper::validateURL($seealso_url)) {
|
||||
throw new OMB_InvalidParameterException($seealso_url, 'notice',
|
||||
'omb_seealso');
|
||||
}
|
||||
$this->seealso_url = $seealso_url;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setSeealsoDisposition($seealso_disposition) {
|
||||
if ($seealso_disposition === '') {
|
||||
$seealso_disposition = null;
|
||||
} elseif ($seealso_disposition !== 'link' && $seealso_disposition !== 'inline') {
|
||||
throw new OMB_InvalidParameterException($seealso_disposition, 'notice',
|
||||
'omb_seealso_disposition');
|
||||
}
|
||||
$this->seealso_disposition = $seealso_disposition;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setSeealsoMediatype($seealso_mediatype) {
|
||||
if ($seealso_mediatype === '') {
|
||||
$seealso_mediatype = null;
|
||||
} elseif (!OMB_Helper::validateMediaType($seealso_mediatype)) {
|
||||
throw new OMB_InvalidParameterException($seealso_mediatype, 'notice',
|
||||
'omb_seealso_mediatype');
|
||||
}
|
||||
$this->seealso_mediatype = $seealso_mediatype;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setSeealsoLicenseURL($seealso_license_url) {
|
||||
if ($seealso_license_url === '') {
|
||||
$seealso_license_url = null;
|
||||
} elseif (!OMB_Helper::validateURL($seealso_license_url)) {
|
||||
throw new OMB_InvalidParameterException($seealso_license_url, 'notice',
|
||||
'omb_seealso_license');
|
||||
}
|
||||
$this->seealso_license_url = $seealso_license_url;
|
||||
$this->param_array = false;
|
||||
}
|
||||
}
|
||||
?>
|
196
extlib/libomb/omb_yadis_xrds.php
Executable file
196
extlib/libomb/omb_yadis_xrds.php
Executable file
@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
require_once 'Auth/Yadis/Yadis.php';
|
||||
require_once 'unsupportedserviceexception.php';
|
||||
require_once 'invalidyadisexception.php';
|
||||
|
||||
/**
|
||||
* OMB XRDS representation
|
||||
*
|
||||
* This class represents a Yadis XRDS file for OMB. It adds some useful methods to
|
||||
* Auth_Yadis_XRDS.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Yadis_XRDS extends Auth_Yadis_XRDS {
|
||||
|
||||
protected $fetcher;
|
||||
|
||||
/**
|
||||
* Create an instance from URL
|
||||
*
|
||||
* Constructs an OMB_Yadis_XRDS object from a given URL. A full Yadis
|
||||
* discovery is performed on the URL and the XRDS is parsed.
|
||||
* Throws an OMB_InvalidYadisException when no Yadis is discovered or the
|
||||
* detected XRDS file is broken.
|
||||
*
|
||||
* @param string $url The URL on which Yadis discovery
|
||||
* should be performed on
|
||||
* @param Auth_Yadis_HTTPFetcher $fetcher A fetcher used to get HTTP
|
||||
* resources
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OMB_Yadis_XRDS The initialized object representing the given
|
||||
* resource
|
||||
**/
|
||||
public static function fromYadisURL($url, $fetcher) {
|
||||
/* Perform a Yadis discovery. */
|
||||
$yadis = Auth_Yadis_Yadis::discover($url, $fetcher);
|
||||
if ($yadis->failed) {
|
||||
throw new OMB_InvalidYadisException($url);
|
||||
}
|
||||
|
||||
/* Parse the XRDS file. */
|
||||
$xrds = OMB_Yadis_XRDS::parseXRDS($yadis->response_text);
|
||||
if ($xrds === null) {
|
||||
throw new OMB_InvalidYadisException($url);
|
||||
}
|
||||
$xrds->fetcher = $fetcher;
|
||||
return $xrds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific service
|
||||
*
|
||||
* Returns the Auth_Yadis_Service object corresponding to the given service
|
||||
* URI.
|
||||
* Throws an OMB_UnsupportedServiceException if the service is not available.
|
||||
*
|
||||
* @param string $service URI specifier of the requested service
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return Auth_Yadis_Service The object representing the requested service
|
||||
**/
|
||||
public function getService($service) {
|
||||
$match = $this->services(array( create_function('$s',
|
||||
"return in_array('$service', \$s->getTypes());")));
|
||||
if ($match === array()) {
|
||||
throw new OMB_UnsupportedServiceException($service);
|
||||
}
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific XRD
|
||||
*
|
||||
* Returns the OMB_Yadis_XRDS object corresponding to the given URI.
|
||||
* Throws an OMB_UnsupportedServiceException if the XRD is not available.
|
||||
* Note that getXRD tries to resolve external XRD parts as well.
|
||||
*
|
||||
* @param string $uri URI specifier of the requested XRD
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OMB_Yadis_XRDS The object representing the requested XRD
|
||||
**/
|
||||
public function getXRD($uri) {
|
||||
$nexthash = strpos($uri, '#');
|
||||
if ($nexthash !== 0) {
|
||||
if ($nexthash !== false) {
|
||||
$cururi = substr($uri, 0, $nexthash);
|
||||
$nexturi = substr($uri, $nexthash);
|
||||
}
|
||||
return
|
||||
OMB_Yadis_XRDS::fromYadisURL($cururi, $this->fetcher)->getXRD($nexturi);
|
||||
}
|
||||
|
||||
$id = substr($uri, 1);
|
||||
foreach ($this->allXrdNodes as $node) {
|
||||
$attrs = $this->parser->attributes($node);
|
||||
if (array_key_exists('xml:id', $attrs) && $attrs['xml:id'] == $id) {
|
||||
/* Trick the constructor into thinking this is the only node. */
|
||||
$bogus_nodes = array($node);
|
||||
return new OMB_Yadis_XRDS($this->parser, $bogus_nodes);
|
||||
}
|
||||
}
|
||||
throw new OMB_UnsupportedServiceException($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an XML string containing a XRDS document
|
||||
*
|
||||
* Parse an XML string (XRDS document) and return either a
|
||||
* Auth_Yadis_XRDS object or null, depending on whether the
|
||||
* XRDS XML is valid.
|
||||
* Copy and paste from parent to select correct constructor.
|
||||
*
|
||||
* @param string $xml_string An XRDS XML string.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return mixed An instance of OMB_Yadis_XRDS or null,
|
||||
* depending on the validity of $xml_string
|
||||
**/
|
||||
|
||||
public function &parseXRDS($xml_string, $extra_ns_map = null) {
|
||||
$_null = null;
|
||||
|
||||
if (!$xml_string) {
|
||||
return $_null;
|
||||
}
|
||||
|
||||
$parser = Auth_Yadis_getXMLParser();
|
||||
|
||||
$ns_map = Auth_Yadis_getNSMap();
|
||||
|
||||
if ($extra_ns_map && is_array($extra_ns_map)) {
|
||||
$ns_map = array_merge($ns_map, $extra_ns_map);
|
||||
}
|
||||
|
||||
if (!($parser && $parser->init($xml_string, $ns_map))) {
|
||||
return $_null;
|
||||
}
|
||||
|
||||
// Try to get root element.
|
||||
$root = $parser->evalXPath('/xrds:XRDS[1]');
|
||||
if (!$root) {
|
||||
return $_null;
|
||||
}
|
||||
|
||||
if (is_array($root)) {
|
||||
$root = $root[0];
|
||||
}
|
||||
|
||||
$attrs = $parser->attributes($root);
|
||||
|
||||
if (array_key_exists('xmlns:xrd', $attrs) &&
|
||||
$attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) {
|
||||
return $_null;
|
||||
} else if (array_key_exists('xmlns', $attrs) &&
|
||||
preg_match('/xri/', $attrs['xmlns']) &&
|
||||
$attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) {
|
||||
return $_null;
|
||||
}
|
||||
|
||||
// Get the last XRD node.
|
||||
$xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD');
|
||||
|
||||
if (!$xrd_nodes) {
|
||||
return $_null;
|
||||
}
|
||||
|
||||
$xrds = new OMB_Yadis_XRDS($parser, $xrd_nodes);
|
||||
return $xrds;
|
||||
}
|
||||
}
|
124
extlib/libomb/plain_xrds_writer.php
Executable file
124
extlib/libomb/plain_xrds_writer.php
Executable file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
require_once 'xrds_writer.php';
|
||||
|
||||
/**
|
||||
* Write OMB-specific XRDS using XMLWriter.
|
||||
*
|
||||
* This class writes the XRDS file announcing the OMB server. It uses
|
||||
* OMB_XMLWriter, which is a subclass of XMLWriter. An instance of
|
||||
* OMB_Plain_XRDS_Writer should be passed to OMB_Service_Provider->writeXRDS.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Plain_XRDS_Writer implements OMB_XRDS_Writer {
|
||||
public function writeXRDS($user, $mapper) {
|
||||
header('Content-Type: application/xrds+xml');
|
||||
$xw = new XMLWriter();
|
||||
$xw->openURI('php://output');
|
||||
$xw->setIndent(true);
|
||||
|
||||
$xw->startDocument('1.0', 'UTF-8');
|
||||
$this->writeFullElement($xw, 'XRDS', array('xmlns' => 'xri://$xrds'), array(
|
||||
array('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'xml:id' => 'oauth',
|
||||
'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
|
||||
'version' => '2.0'), array(
|
||||
array('Type', null, 'xri://$xrds*simple'),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OAUTH_ENDPOINT_REQUEST),
|
||||
array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_REQUEST)),
|
||||
array('Type', null, OAUTH_AUTH_HEADER),
|
||||
array('Type', null, OAUTH_POST_BODY),
|
||||
array('Type', null, OAUTH_HMAC_SHA1),
|
||||
array('LocalID', null, $user->getIdentifierURI())
|
||||
)),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OAUTH_ENDPOINT_AUTHORIZE),
|
||||
array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_AUTHORIZE)),
|
||||
array('Type', null, OAUTH_AUTH_HEADER),
|
||||
array('Type', null, OAUTH_POST_BODY),
|
||||
array('Type', null, OAUTH_HMAC_SHA1)
|
||||
)),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OAUTH_ENDPOINT_ACCESS),
|
||||
array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_ACCESS)),
|
||||
array('Type', null, OAUTH_AUTH_HEADER),
|
||||
array('Type', null, OAUTH_POST_BODY),
|
||||
array('Type', null, OAUTH_HMAC_SHA1)
|
||||
)),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OAUTH_ENDPOINT_RESOURCE),
|
||||
array('Type', null, OAUTH_AUTH_HEADER),
|
||||
array('Type', null, OAUTH_POST_BODY),
|
||||
array('Type', null, OAUTH_HMAC_SHA1)
|
||||
))
|
||||
)),
|
||||
array('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'xml:id' => 'omb',
|
||||
'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
|
||||
'version' => '2.0'), array(
|
||||
array('Type', null, 'xri://$xrds*simple'),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OMB_ENDPOINT_POSTNOTICE),
|
||||
array('URI', null, $mapper->getURL(OMB_ENDPOINT_POSTNOTICE))
|
||||
)),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OMB_ENDPOINT_UPDATEPROFILE),
|
||||
array('URI', null, $mapper->getURL(OMB_ENDPOINT_UPDATEPROFILE))
|
||||
))
|
||||
)),
|
||||
array('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'version' => '2.0'), array(
|
||||
array('Type', null, 'xri://$xrds*simple'),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OAUTH_DISCOVERY),
|
||||
array('URI', null, '#oauth')
|
||||
)),
|
||||
array('Service', null, array(
|
||||
array('Type', null, OMB_VERSION),
|
||||
array('URI', null, '#omb')
|
||||
))
|
||||
))
|
||||
));
|
||||
$xw->endDocument();
|
||||
$xw->flush();
|
||||
}
|
||||
|
||||
public static function writeFullElement($xw, $tag, $attributes, $content) {
|
||||
$xw->startElement($tag);
|
||||
if (!is_null($attributes)) {
|
||||
foreach ($attributes as $name => $value) {
|
||||
$xw->writeAttribute($name, $value);
|
||||
}
|
||||
}
|
||||
if (is_array($content)) {
|
||||
foreach ($content as $values) {
|
||||
OMB_Plain_XRDS_Writer::writeFullElement($xw, $values[0], $values[1], $values[2]);
|
||||
}
|
||||
} else {
|
||||
$xw->text($content);
|
||||
}
|
||||
$xw->fullEndElement();
|
||||
}
|
||||
}
|
||||
?>
|
317
extlib/libomb/profile.php
Executable file
317
extlib/libomb/profile.php
Executable file
@ -0,0 +1,317 @@
|
||||
<?php
|
||||
require_once 'invalidparameterexception.php';
|
||||
require_once 'Validate.php';
|
||||
require_once 'helper.php';
|
||||
|
||||
/**
|
||||
* OMB profile representation
|
||||
*
|
||||
* This class represents an OMB profile.
|
||||
*
|
||||
* Do not call the setters with null values. Instead, if you want to delete a
|
||||
* field, pass an empty string. The getters will return null for empty fields.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Profile {
|
||||
protected $identifier_uri;
|
||||
protected $profile_url;
|
||||
protected $nickname;
|
||||
protected $license_url;
|
||||
protected $fullname;
|
||||
protected $homepage;
|
||||
protected $bio;
|
||||
protected $location;
|
||||
protected $avatar_url;
|
||||
|
||||
/* The profile as OMB param array. Cached and rebuild on usage.
|
||||
false while outdated. */
|
||||
protected $param_array;
|
||||
|
||||
/**
|
||||
* Constructor for OMB_Profile
|
||||
*
|
||||
* Initializes the OMB_Profile object with an identifier uri.
|
||||
*
|
||||
* @param string $identifier_uri The profile URI as defined by the OMB. A unique
|
||||
* and unchanging identifier for a profile.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($identifier_uri) {
|
||||
if (!Validate::uri($identifier_uri)) {
|
||||
throw new OMB_InvalidParameterException($identifier_uri, 'profile',
|
||||
'omb_listenee or omb_listener');
|
||||
}
|
||||
$this->identifier_uri = $identifier_uri;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the profile as array
|
||||
*
|
||||
* The method returns an array which contains the whole profile as array. The
|
||||
* array is cached and only rebuilt on changes of the profile.
|
||||
*
|
||||
* @param bool $force_all Specifies whether empty fields should be added to
|
||||
* the array as well. This is neccessary to clear
|
||||
* fields via updateProfile.
|
||||
*
|
||||
* @param string $prefix The common prefix to the key for all parameters.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return array The profile as parameter array
|
||||
*/
|
||||
public function asParameters($prefix, $force_all = false) {
|
||||
if ($this->param_array === false) {
|
||||
$this->param_array = array('' => $this->identifier_uri);
|
||||
|
||||
if ($force_all || !is_null($this->profile_url)) {
|
||||
$this->param_array['_profile'] = $this->profile_url;
|
||||
}
|
||||
|
||||
if ($force_all || !is_null($this->homepage)) {
|
||||
$this->param_array['_homepage'] = $this->homepage;
|
||||
}
|
||||
|
||||
if ($force_all || !is_null($this->nickname)) {
|
||||
$this->param_array['_nickname'] = $this->nickname;
|
||||
}
|
||||
|
||||
if ($force_all || !is_null($this->license_url)) {
|
||||
$this->param_array['_license'] = $this->license_url;
|
||||
}
|
||||
|
||||
if ($force_all || !is_null($this->fullname)) {
|
||||
$this->param_array['_fullname'] = $this->fullname;
|
||||
}
|
||||
|
||||
if ($force_all || !is_null($this->bio)) {
|
||||
$this->param_array['_bio'] = $this->bio;
|
||||
}
|
||||
|
||||
if ($force_all || !is_null($this->location)) {
|
||||
$this->param_array['_location'] = $this->location;
|
||||
}
|
||||
|
||||
if ($force_all || !is_null($this->avatar_url)) {
|
||||
$this->param_array['_avatar'] = $this->avatar_url;
|
||||
}
|
||||
|
||||
}
|
||||
$ret = array();
|
||||
foreach ($this->param_array as $k => $v) {
|
||||
$ret[$prefix . $k] = $v;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an OMB_Profile object from array
|
||||
*
|
||||
* The method builds an OMB_Profile object from the passed parameters array. The
|
||||
* array MUST provide a profile URI. The array fields HAVE TO be named according
|
||||
* to the OMB standard. The prefix (omb_listener or omb_listenee) is passed as a
|
||||
* parameter.
|
||||
*
|
||||
* @param string $parameters An array containing the profile parameters.
|
||||
* @param string $prefix The common prefix of the profile parameter keys.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @returns OMB_Profile The built OMB_Profile.
|
||||
*/
|
||||
public static function fromParameters($parameters, $prefix) {
|
||||
if (!isset($parameters[$prefix])) {
|
||||
throw new OMB_InvalidParameterException('', 'profile', $prefix);
|
||||
}
|
||||
|
||||
$profile = new OMB_Profile($parameters[$prefix]);
|
||||
$profile->updateFromParameters($parameters, $prefix);
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update from array
|
||||
*
|
||||
* Updates from the passed parameters array. The array does not have to
|
||||
* provide a profile URI. The array fields HAVE TO be named according to the
|
||||
* OMB standard. The prefix (omb_listener or omb_listenee) is passed as a
|
||||
* parameter.
|
||||
*
|
||||
* @param string $parameters An array containing the profile parameters.
|
||||
* @param string $prefix The common prefix of the profile parameter keys.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function updateFromParameters($parameters, $prefix) {
|
||||
if (isset($parameters[$prefix.'_profile'])) {
|
||||
$this->setProfileURL($parameters[$prefix.'_profile']);
|
||||
}
|
||||
|
||||
if (isset($parameters[$prefix.'_license'])) {
|
||||
$this->setLicenseURL($parameters[$prefix.'_license']);
|
||||
}
|
||||
|
||||
if (isset($parameters[$prefix.'_nickname'])) {
|
||||
$this->setNickname($parameters[$prefix.'_nickname']);
|
||||
}
|
||||
|
||||
if (isset($parameters[$prefix.'_fullname'])) {
|
||||
$this->setFullname($parameters[$prefix.'_fullname']);
|
||||
}
|
||||
|
||||
if (isset($parameters[$prefix.'_homepage'])) {
|
||||
$this->setHomepage($parameters[$prefix.'_homepage']);
|
||||
}
|
||||
|
||||
if (isset($parameters[$prefix.'_bio'])) {
|
||||
$this->setBio($parameters[$prefix.'_bio']);
|
||||
}
|
||||
|
||||
if (isset($parameters[$prefix.'_location'])) {
|
||||
$this->setLocation($parameters[$prefix.'_location']);
|
||||
}
|
||||
|
||||
if (isset($parameters[$prefix.'_avatar'])) {
|
||||
$this->setAvatarURL($parameters[$prefix.'_avatar']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getIdentifierURI() {
|
||||
return $this->identifier_uri;
|
||||
}
|
||||
|
||||
public function getProfileURL() {
|
||||
return $this->profile_url;
|
||||
}
|
||||
|
||||
public function getHomepage() {
|
||||
return $this->homepage;
|
||||
}
|
||||
|
||||
public function getNickname() {
|
||||
return $this->nickname;
|
||||
}
|
||||
|
||||
public function getLicenseURL() {
|
||||
return $this->license_url;
|
||||
}
|
||||
|
||||
public function getFullname() {
|
||||
return $this->fullname;
|
||||
}
|
||||
|
||||
public function getBio() {
|
||||
return $this->bio;
|
||||
}
|
||||
|
||||
public function getLocation() {
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
public function getAvatarURL() {
|
||||
return $this->avatar_url;
|
||||
}
|
||||
|
||||
public function setProfileURL($profile_url) {
|
||||
if (!OMB_Helper::validateURL($profile_url)) {
|
||||
throw new OMB_InvalidParameterException($profile_url, 'profile',
|
||||
'omb_listenee_profile or omb_listener_profile');
|
||||
}
|
||||
$this->profile_url = $profile_url;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setNickname($nickname) {
|
||||
if (!Validate::string($nickname,
|
||||
array('min_length' => 1,
|
||||
'max_length' => 64,
|
||||
'format' => VALIDATE_NUM . VALIDATE_ALPHA))) {
|
||||
throw new OMB_InvalidParameterException($nickname, 'profile', 'nickname');
|
||||
}
|
||||
|
||||
$this->nickname = $nickname;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setLicenseURL($license_url) {
|
||||
if (!OMB_Helper::validateURL($license_url)) {
|
||||
throw new OMB_InvalidParameterException($license_url, 'profile',
|
||||
'omb_listenee_license or omb_listener_license');
|
||||
}
|
||||
$this->license_url = $license_url;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setFullname($fullname) {
|
||||
if ($fullname === '') {
|
||||
$fullname = null;
|
||||
} elseif (!Validate::string($fullname, array('max_length' => 255))) {
|
||||
throw new OMB_InvalidParameterException($fullname, 'profile', 'fullname');
|
||||
}
|
||||
$this->fullname = $fullname;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setHomepage($homepage) {
|
||||
if ($homepage === '') {
|
||||
$homepage = null;
|
||||
}
|
||||
$this->homepage = $homepage;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setBio($bio) {
|
||||
if ($bio === '') {
|
||||
$bio = null;
|
||||
} elseif (!Validate::string($bio, array('max_length' => 140))) {
|
||||
throw new OMB_InvalidParameterException($bio, 'profile', 'fullname');
|
||||
}
|
||||
$this->bio = $bio;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setLocation($location) {
|
||||
if ($location === '') {
|
||||
$location = null;
|
||||
} elseif (!Validate::string($location, array('max_length' => 255))) {
|
||||
throw new OMB_InvalidParameterException($location, 'profile', 'fullname');
|
||||
}
|
||||
$this->location = $location;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
public function setAvatarURL($avatar_url) {
|
||||
if ($avatar_url === '') {
|
||||
$avatar_url = null;
|
||||
} elseif (!OMB_Helper::validateURL($avatar_url)) {
|
||||
throw new OMB_InvalidParameterException($avatar_url, 'profile',
|
||||
'omb_listenee_avatar or omb_listener_avatar');
|
||||
}
|
||||
$this->avatar_url = $avatar_url;
|
||||
$this->param_array = false;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
42
extlib/libomb/remoteserviceexception.php
Executable file
42
extlib/libomb/remoteserviceexception.php
Executable file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Exception stating that the remote service had a failure
|
||||
*
|
||||
* This exception is raised when a remote service failed to return a valid
|
||||
* response to a request or send a valid request.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
class OMB_RemoteServiceException extends Exception {
|
||||
public static function fromYadis($request_uri, $result) {
|
||||
if ($result->status == 200) {
|
||||
$err = 'Got wrong response ' . $result->body;
|
||||
} else {
|
||||
$err = 'Got error code ' . $result->status . ' with response ' . $result->body;
|
||||
}
|
||||
return new OMB_RemoteServiceException($request_uri . ': ' . $err);
|
||||
}
|
||||
|
||||
public static function forRequest($action_uri, $failure) {
|
||||
return new OMB_RemoteServiceException("Handler for $action_uri: " . $failure);
|
||||
}
|
||||
}
|
||||
?>
|
430
extlib/libomb/service_consumer.php
Executable file
430
extlib/libomb/service_consumer.php
Executable file
@ -0,0 +1,430 @@
|
||||
<?php
|
||||
|
||||
require_once 'constants.php';
|
||||
require_once 'Validate.php';
|
||||
require_once 'Auth/Yadis/Yadis.php';
|
||||
require_once 'OAuth.php';
|
||||
require_once 'unsupportedserviceexception.php';
|
||||
require_once 'remoteserviceexception.php';
|
||||
require_once 'omb_yadis_xrds.php';
|
||||
require_once 'helper.php';
|
||||
|
||||
/**
|
||||
* OMB service representation
|
||||
*
|
||||
* This class represents a complete remote OMB service. It provides discovery
|
||||
* and execution of the service’s methods.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Service_Consumer {
|
||||
protected $url; /* The service URL */
|
||||
protected $services; /* An array of strings mapping service URI to
|
||||
service URL */
|
||||
|
||||
protected $token; /* An OAuthToken */
|
||||
|
||||
protected $listener_uri; /* The URI identifying the listener, i. e. the
|
||||
remote user. */
|
||||
|
||||
protected $listenee_uri; /* The URI identifying the listenee, i. e. the
|
||||
local user during an auth request. */
|
||||
|
||||
/**
|
||||
* According to OAuth Core 1.0, an user authorization request is no full-blown
|
||||
* OAuth request. nonce, timestamp, consumer_key and signature are not needed
|
||||
* in this step. See http://laconi.ca/trac/ticket/827 for more informations.
|
||||
*
|
||||
* Since Laconica up to version 0.7.2 performs a full OAuth request check, a
|
||||
* correct request would fail.
|
||||
**/
|
||||
public $performLegacyAuthRequest = true;
|
||||
|
||||
/* Helper stuff we are going to need. */
|
||||
protected $fetcher;
|
||||
protected $oauth_consumer;
|
||||
protected $datastore;
|
||||
|
||||
/**
|
||||
* Constructor for OMB_Service_Consumer
|
||||
*
|
||||
* Initializes an OMB_Service_Consumer object representing the OMB service
|
||||
* specified by $service_url. Performs a complete service discovery using
|
||||
* Yadis.
|
||||
* Throws OMB_UnsupportedServiceException if XRDS file does not specify a
|
||||
* complete OMB service.
|
||||
*
|
||||
* @param string $service_url The URL of the service
|
||||
* @param string $consumer_url An URL representing the consumer
|
||||
* @param OMB_Datastore $datastore An instance of a class implementing
|
||||
* OMB_Datastore
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function __construct ($service_url, $consumer_url, $datastore) {
|
||||
$this->url = $service_url;
|
||||
$this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
$this->datastore = $datastore;
|
||||
$this->oauth_consumer = new OAuthConsumer($consumer_url, '');
|
||||
|
||||
$xrds = OMB_Yadis_XRDS::fromYadisURL($service_url, $this->fetcher);
|
||||
|
||||
/* Detect our services. This performs a validation as well, since
|
||||
getService und getXRD throw exceptions on failure. */
|
||||
$this->services = array();
|
||||
|
||||
foreach (array(OAUTH_DISCOVERY => OMB_Helper::$OAUTH_SERVICES,
|
||||
OMB_VERSION => OMB_Helper::$OMB_SERVICES)
|
||||
as $service_root => $targetservices) {
|
||||
$uris = $xrds->getService($service_root)->getURIs();
|
||||
$xrd = $xrds->getXRD($uris[0]);
|
||||
foreach ($targetservices as $targetservice) {
|
||||
$yadis_service = $xrd->getService($targetservice);
|
||||
if ($targetservice == OAUTH_ENDPOINT_REQUEST) {
|
||||
$localid = $yadis_service->getElements('xrd:LocalID');
|
||||
$this->listener_uri = $yadis_service->parser->content($localid[0]);
|
||||
}
|
||||
$uris = $yadis_service->getURIs();
|
||||
$this->services[$targetservice] = $uris[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the handler URI for a service
|
||||
*
|
||||
* Returns the URI the remote web service has specified for the given
|
||||
* service.
|
||||
*
|
||||
* @param string $service The URI identifying the service
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string The service handler URI
|
||||
**/
|
||||
public function getServiceURI($service) {
|
||||
return $this->services[$service];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remote user’s URI
|
||||
*
|
||||
* Returns the URI of the remote user, i. e. the listener.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string The remote user’s URI
|
||||
**/
|
||||
public function getRemoteUserURI() {
|
||||
return $this->listener_uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the listenee’s URI
|
||||
*
|
||||
* Returns the URI of the user being subscribed to, i. e. the local user.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string The local user’s URI
|
||||
**/
|
||||
public function getListeneeURI() {
|
||||
return $this->listenee_uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a request token
|
||||
*
|
||||
* Performs a token request on the service. Returns an OAuthToken on success.
|
||||
* Throws an exception if the request fails.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OAuthToken An unauthorized request token
|
||||
**/
|
||||
public function requestToken() {
|
||||
/* Set the token to null just in case the user called setToken. */
|
||||
$this->token = null;
|
||||
|
||||
$result = $this->performAction(OAUTH_ENDPOINT_REQUEST,
|
||||
array('omb_listener' => $this->listener_uri));
|
||||
if ($result->status != 200) {
|
||||
throw OMB_RemoteServiceException::fromYadis(OAUTH_ENDPOINT_REQUEST,
|
||||
$result);
|
||||
}
|
||||
parse_str($result->body, $return);
|
||||
if (!isset($return['oauth_token']) || !isset($return['oauth_token_secret'])) {
|
||||
throw OMB_RemoteServiceException::fromYadis(OAUTH_ENDPOINT_REQUEST,
|
||||
$result);
|
||||
}
|
||||
$this->setToken($return['oauth_token'], $return['oauth_token_secret']);
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Request authorization
|
||||
*
|
||||
* Returns an URL which equals to an authorization request. The end user
|
||||
* should be redirected to this location to perform authorization.
|
||||
* The $finish_url should be a local resource which invokes
|
||||
* OMB_Consumer::finishAuthorization on request.
|
||||
*
|
||||
* @param OMB_Profile $profile An OMB_Profile object representing the
|
||||
* soon-to-be subscribed (i. e. local) user
|
||||
* @param string $finish_url Target location after successful
|
||||
* authorization
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string An URL representing an authorization request
|
||||
**/
|
||||
public function requestAuthorization($profile, $finish_url) {
|
||||
if ($this->performLegacyAuthRequest) {
|
||||
$params = $profile->asParameters('omb_listenee', false);
|
||||
$params['omb_listener'] = $this->listener_uri;
|
||||
$params['oauth_callback'] = $finish_url;
|
||||
|
||||
$url = $this->prepareAction(OAUTH_ENDPOINT_AUTHORIZE, $params, 'GET')->to_url();
|
||||
} else {
|
||||
|
||||
$params = array(
|
||||
'oauth_callback' => $finish_url,
|
||||
'oauth_token' => $this->token->key,
|
||||
'omb_version' => OMB_VERSION,
|
||||
'omb_listener' => $this->listener_uri);
|
||||
|
||||
$params = array_merge($profile->asParameters('omb_listenee', false). $params);
|
||||
|
||||
/* Build result URL. */
|
||||
$url = $this->services[OAUTH_ENDPOINT_AUTHORIZE];
|
||||
$url .= (strrpos($url, '?') === false ? '?' : '&');
|
||||
foreach ($params as $k => $v) {
|
||||
$url .= OAuthUtil::urlencode_rfc3986($k) . '=' . OAuthUtil::urlencode_rfc3986($v) . '&';
|
||||
}
|
||||
}
|
||||
|
||||
$this->listenee_uri = $profile->getIdentifierURI();
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish authorization
|
||||
*
|
||||
* Finish the subscription process by converting the received and authorized
|
||||
* request token into an access token. After that, the subscriber’s profile
|
||||
* and the subscription are stored in the database.
|
||||
* Expects an OAuthRequest in query parameters.
|
||||
* Throws exceptions on failure.
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function finishAuthorization() {
|
||||
OMB_Helper::removeMagicQuotesFromRequest();
|
||||
$req = OAuthRequest::from_request();
|
||||
if ($req->get_parameter('oauth_token') !=
|
||||
$this->token->key) {
|
||||
/* That’s not the token I wanted to get authorized. */
|
||||
throw new OAuthException('The authorized token does not equal the ' .
|
||||
'submitted token.');
|
||||
}
|
||||
|
||||
if ($req->get_parameter('omb_version') != OMB_VERSION) {
|
||||
throw new OMB_RemoteServiceException('The remote service uses an ' .
|
||||
'unsupported OMB version');
|
||||
}
|
||||
|
||||
/* Construct the profile to validate it. */
|
||||
|
||||
/* Fix OMB bug. Listener URI is not passed. */
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$params = $_POST;
|
||||
} else {
|
||||
$params = $_GET;
|
||||
}
|
||||
$params['omb_listener'] = $this->listener_uri;
|
||||
|
||||
require_once 'profile.php';
|
||||
$listener = OMB_Profile::fromParameters($params, 'omb_listener');
|
||||
|
||||
/* Ask the remote service to convert the authorized request token into an
|
||||
access token. */
|
||||
|
||||
$result = $this->performAction(OAUTH_ENDPOINT_ACCESS, array());
|
||||
if ($result->status != 200) {
|
||||
throw new OAuthException('Could not get access token');
|
||||
}
|
||||
|
||||
parse_str($result->body, $return);
|
||||
if (!isset($return['oauth_token']) || !isset($return['oauth_token_secret'])) {
|
||||
throw new OAuthException('Could not get access token');
|
||||
}
|
||||
$this->setToken($return['oauth_token'], $return['oauth_token_secret']);
|
||||
|
||||
/* Subscription is finished and valid. Now store the new subscriber and the
|
||||
subscription in the database. */
|
||||
|
||||
$this->datastore->saveProfile($listener);
|
||||
$this->datastore->saveSubscription($this->listener_uri,
|
||||
$this->listenee_uri,
|
||||
$this->token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URI identifying the listener
|
||||
*
|
||||
* Returns the URI for the OMB user who tries to subscribe or already has
|
||||
* subscribed our user. This method is a workaround for a serious OMB flaw:
|
||||
* The Listener URI is not passed in the finishauthorization call.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string the listener’s URI
|
||||
**/
|
||||
public function getListenerURI() {
|
||||
return $this->listener_uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform the service about a profile update
|
||||
*
|
||||
* Sends an updated profile to the service.
|
||||
*
|
||||
* @param OMB_Profile $profile The profile that has changed
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function updateProfile($profile) {
|
||||
$params = $profile->asParameters('omb_listenee', true);
|
||||
$this->performOMBAction(OMB_ENDPOINT_UPDATEPROFILE, $params, $profile->getIdentifierURI());
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform the service about a new notice
|
||||
*
|
||||
* Sends a notice to the service.
|
||||
*
|
||||
* @param OMB_Notice $notice The notice
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function postNotice($notice) {
|
||||
$params = $notice->asParameters();
|
||||
$params['omb_listenee'] = $notice->getAuthor()->getIdentifierURI();
|
||||
$this->performOMBAction(OMB_ENDPOINT_POSTNOTICE, $params, $params['omb_listenee']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the token member variable
|
||||
*
|
||||
* Initializes the token based on given token and secret token.
|
||||
*
|
||||
* @param string $token The token
|
||||
* @param string $secret The secret token
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function setToken($token, $secret) {
|
||||
$this->token = new OAuthToken($token, $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an OAuthRequest object
|
||||
*
|
||||
* Creates an OAuthRequest object mapping the request specified by the
|
||||
* parameters.
|
||||
*
|
||||
* @param string $action_uri The URI specifying the target service
|
||||
* @param array $params Additional parameters for the service call
|
||||
* @param string $method The HTTP method used to call the service
|
||||
* ('POST' or 'GET', usually)
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @return OAuthRequest the prepared request
|
||||
**/
|
||||
protected function prepareAction($action_uri, $params, $method) {
|
||||
$url = $this->services[$action_uri];
|
||||
|
||||
$url_params = array();
|
||||
parse_str(parse_url($url, PHP_URL_QUERY), $url_params);
|
||||
|
||||
/* Add OMB version. */
|
||||
$url_params['omb_version'] = OMB_VERSION;
|
||||
|
||||
/* Add user-defined parameters. */
|
||||
$url_params = array_merge($url_params, $params);
|
||||
|
||||
$req = OAuthRequest::from_consumer_and_token($this->oauth_consumer,
|
||||
$this->token, $method, $url, $url_params);
|
||||
|
||||
/* Sign the request. */
|
||||
$req->sign_request(new OAuthSignatureMethod_HMAC_SHA1(),
|
||||
$this->oauth_consumer, $this->token);
|
||||
|
||||
return $req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a service call
|
||||
*
|
||||
* Creates an OAuthRequest object and execute the mapped call as POST request.
|
||||
*
|
||||
* @param string $action_uri The URI specifying the target service
|
||||
* @param array $params Additional parameters for the service call
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @return Auth_Yadis_HTTPResponse The POST request response
|
||||
**/
|
||||
protected function performAction($action_uri, $params) {
|
||||
$req = $this->prepareAction($action_uri, $params, 'POST');
|
||||
|
||||
/* Return result page. */
|
||||
return $this->fetcher->post($req->get_normalized_http_url(), $req->to_postdata(), array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an OMB action
|
||||
*
|
||||
* Executes an OMB action – to date, it’s one of updateProfile or postNotice.
|
||||
*
|
||||
* @param string $action_uri The URI specifying the target service
|
||||
* @param array $params Additional parameters for the service call
|
||||
* @param string $listenee_uri The URI identifying the local user for whom
|
||||
* the action is performed
|
||||
*
|
||||
* @access protected
|
||||
**/
|
||||
protected function performOMBAction($action_uri, $params, $listenee_uri) {
|
||||
$result = $this->performAction($action_uri, $params);
|
||||
if ($result->status == 403) {
|
||||
/* The remote user unsubscribed us. */
|
||||
$this->datastore->deleteSubscription($this->listener_uri, $listenee_uri);
|
||||
} else if ($result->status != 200 ||
|
||||
strpos($result->body, 'omb_version=' . OMB_VERSION) === false) {
|
||||
/* The server signaled an error or sent an incorrect response. */
|
||||
throw OMB_RemoteServiceException::fromYadis($action_uri, $result);
|
||||
}
|
||||
}
|
||||
}
|
411
extlib/libomb/service_provider.php
Executable file
411
extlib/libomb/service_provider.php
Executable file
@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
require_once 'constants.php';
|
||||
require_once 'remoteserviceexception.php';
|
||||
require_once 'helper.php';
|
||||
|
||||
/**
|
||||
* OMB service realization
|
||||
*
|
||||
* This class realizes a complete, simple OMB service.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
class OMB_Service_Provider {
|
||||
protected $user; /* An OMB_Profile representing the user */
|
||||
protected $datastore; /* AN OMB_Datastore */
|
||||
|
||||
protected $remote_user; /* An OMB_Profile representing the remote user during
|
||||
the authorization process */
|
||||
|
||||
protected $oauth_server; /* An OAuthServer; should only be accessed via
|
||||
getOAuthServer. */
|
||||
|
||||
/**
|
||||
* Initialize an OMB_Service_Provider object
|
||||
*
|
||||
* Constructs an OMB_Service_Provider instance that provides OMB services
|
||||
* referring to a particular user.
|
||||
*
|
||||
* @param OMB_Profile $user An OMB_Profile; mandatory for XRDS
|
||||
* output, user auth handling and OMB
|
||||
* action performing
|
||||
* @param OMB_Datastore $datastore An OMB_Datastore; mandatory for
|
||||
* everything but XRDS output
|
||||
* @param OAuthServer $oauth_server An OAuthServer; used for token writing
|
||||
* and OMB action handling; will use
|
||||
* default value if not set
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function __construct ($user = null, $datastore = null, $oauth_server = null) {
|
||||
$this->user = $user;
|
||||
$this->datastore = $datastore;
|
||||
$this->oauth_server = $oauth_server;
|
||||
}
|
||||
|
||||
public function getRemoteUser() {
|
||||
return $this->remote_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a XRDS document
|
||||
*
|
||||
* Writes a XRDS document specifying the OMB service. Optionally uses a
|
||||
* given object of a class implementing OMB_XRDS_Writer for output. Else
|
||||
* OMB_Plain_XRDS_Writer is used.
|
||||
*
|
||||
* @param OMB_XRDS_Mapper $xrds_mapper An object mapping actions to URLs
|
||||
* @param OMB_XRDS_Writer $xrds_writer Optional; The OMB_XRDS_Writer used to
|
||||
* write the XRDS document
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return mixed Depends on the used OMB_XRDS_Writer; OMB_Plain_XRDS_Writer
|
||||
* returns nothing.
|
||||
**/
|
||||
public function writeXRDS($xrds_mapper, $xrds_writer = null) {
|
||||
if ($xrds_writer == null) {
|
||||
require_once 'plain_xrds_writer.php';
|
||||
$xrds_writer = new OMB_Plain_XRDS_Writer();
|
||||
}
|
||||
return $xrds_writer->writeXRDS($this->user, $xrds_mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo a request token
|
||||
*
|
||||
* Outputs an unauthorized request token for the query found in $_GET or
|
||||
* $_POST.
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function writeRequestToken() {
|
||||
OMB_Helper::removeMagicQuotesFromRequest();
|
||||
echo $this->getOAuthServer()->fetch_request_token(OAuthRequest::from_request());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an user authorization request.
|
||||
*
|
||||
* Parses an authorization request. This includes OAuth and OMB verification.
|
||||
* Throws exceptions on failures. Returns an OMB_Profile object representing
|
||||
* the remote user.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OMB_Profile The profile of the soon-to-be subscribed, i. e. remote
|
||||
* user
|
||||
**/
|
||||
public function handleUserAuth() {
|
||||
OMB_Helper::removeMagicQuotesFromRequest();
|
||||
|
||||
/* Verify the request token. */
|
||||
|
||||
$this->token = $this->datastore->lookup_token(null, "request", $_GET['oauth_token']);
|
||||
if (is_null($this->token)) {
|
||||
throw new OAuthException('The given request token has not been issued ' .
|
||||
'by this service.');
|
||||
}
|
||||
|
||||
/* Verify the OMB part. */
|
||||
|
||||
if ($_GET['omb_version'] !== OMB_VERSION) {
|
||||
throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
|
||||
'Wrong OMB version ' . $_GET['omb_version']);
|
||||
}
|
||||
|
||||
if ($_GET['omb_listener'] !== $this->user->getIdentifierURI()) {
|
||||
throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
|
||||
'Wrong OMB listener ' . $_GET['omb_listener']);
|
||||
}
|
||||
|
||||
foreach (array('omb_listenee', 'omb_listenee_profile',
|
||||
'omb_listenee_nickname', 'omb_listenee_license') as $param) {
|
||||
if (!isset($_GET[$param]) || is_null($_GET[$param])) {
|
||||
throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
|
||||
"Required parameter '$param' not found");
|
||||
}
|
||||
}
|
||||
|
||||
/* Store given callback for later use. */
|
||||
if (isset($_GET['oauth_callback']) && $_GET['oauth_callback'] !== '') {
|
||||
$this->callback = $_GET['oauth_callback'];
|
||||
}
|
||||
$this->remote_user = OMB_Profile::fromParameters($_GET, 'omb_listenee');
|
||||
|
||||
return $this->remote_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Continue the OAuth dance after user authorization
|
||||
*
|
||||
* Performs the appropriate actions after user answered the authorization
|
||||
* request.
|
||||
*
|
||||
* @param bool $accepted Whether the user granted authorization
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return array A two-component array with the values:
|
||||
* - callback The callback URL or null if none given
|
||||
* - token The authorized request token or null if not
|
||||
* authorized.
|
||||
**/
|
||||
public function continueUserAuth($accepted) {
|
||||
$callback = $this->callback;
|
||||
if (!$accepted) {
|
||||
$this->datastore->revoke_token($this->token->key);
|
||||
$this->token = null;
|
||||
/* TODO: The handling is probably wrong in terms of OAuth 1.0 but the way
|
||||
laconica works. Moreover I don’t know the right way either. */
|
||||
|
||||
} else {
|
||||
$this->datastore->authorize_token($this->token->key);
|
||||
$this->datastore->saveProfile($this->remote_user);
|
||||
$this->datastore->saveSubscription($this->user->getIdentifierURI(),
|
||||
$this->remote_user->getIdentifierURI(), $this->token);
|
||||
|
||||
if (!is_null($this->callback)) {
|
||||
/* Callback wants to get some informations as well. */
|
||||
$params = $this->user->asParameters('omb_listener', false);
|
||||
|
||||
$params['oauth_token'] = $this->token->key;
|
||||
$params['omb_version'] = OMB_VERSION;
|
||||
|
||||
$callback .= (parse_url($this->callback, PHP_URL_QUERY) ? '&' : '?');
|
||||
foreach ($params as $k => $v) {
|
||||
$callback .= OAuthUtil::urlencode_rfc3986($k) . '=' .
|
||||
OAuthUtil::urlencode_rfc3986($v) . '&';
|
||||
}
|
||||
}
|
||||
}
|
||||
return array($callback, $this->token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo an access token
|
||||
*
|
||||
* Outputs an access token for the query found in $_GET or $_POST.
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function writeAccessToken() {
|
||||
OMB_Helper::removeMagicQuotesFromRequest();
|
||||
echo $this->getOAuthServer()->fetch_access_token(OAuthRequest::from_request());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an updateprofile request
|
||||
*
|
||||
* Handles an updateprofile request posted to this service. Updates the
|
||||
* profile through the OMB_Datastore.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OMB_Profile The updated profile
|
||||
**/
|
||||
public function handleUpdateProfile() {
|
||||
list($req, $profile) = $this->handleOMBRequest(OMB_ENDPOINT_UPDATEPROFILE);
|
||||
$profile->updateFromParameters($req->get_parameters(), 'omb_listenee');
|
||||
$this->datastore->saveProfile($profile);
|
||||
$this->finishOMBRequest();
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a postnotice request
|
||||
*
|
||||
* Handles a postnotice request posted to this service.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OMB_Notice The received notice
|
||||
**/
|
||||
public function handlePostNotice() {
|
||||
list($req, $profile) = $this->handleOMBRequest(OMB_ENDPOINT_POSTNOTICE);
|
||||
require_once 'notice.php';
|
||||
$notice = OMB_Notice::fromParameters($profile, $req->get_parameters());
|
||||
$this->datastore->saveNotice($notice);
|
||||
$this->finishOMBRequest();
|
||||
return $notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an OMB request
|
||||
*
|
||||
* Performs common OMB request handling.
|
||||
*
|
||||
* @param string $uri The URI defining the OMB endpoint being served
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @return array(OAuthRequest, OMB_Profile)
|
||||
**/
|
||||
protected function handleOMBRequest($uri) {
|
||||
|
||||
OMB_Helper::removeMagicQuotesFromRequest();
|
||||
$req = OAuthRequest::from_request();
|
||||
$listenee = $req->get_parameter('omb_listenee');
|
||||
|
||||
try {
|
||||
list($consumer, $token) = $this->getOAuthServer()->verify_request($req);
|
||||
} catch (OAuthException $e) {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
throw OMB_RemoteServiceException::forRequest($uri,
|
||||
'Revoked accesstoken for ' . $listenee);
|
||||
}
|
||||
|
||||
$version = $req->get_parameter('omb_version');
|
||||
if ($version !== OMB_VERSION) {
|
||||
header('HTTP/1.1 400 Bad Request');
|
||||
throw OMB_RemoteServiceException::forRequest($uri,
|
||||
'Wrong OMB version ' . $version);
|
||||
}
|
||||
|
||||
$profile = $this->datastore->getProfile($listenee);
|
||||
if (is_null($profile)) {
|
||||
header('HTTP/1.1 400 Bad Request');
|
||||
throw OMB_RemoteServiceException::forRequest($uri,
|
||||
'Unknown remote profile ' . $listenee);
|
||||
}
|
||||
|
||||
$subscribers = $this->datastore->getSubscriptions($listenee);
|
||||
if (count($subscribers) === 0) {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
throw OMB_RemoteServiceException::forRequest($uri,
|
||||
'No subscriber for ' . $listenee);
|
||||
}
|
||||
|
||||
return array($req, $profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes an OMB request handling
|
||||
*
|
||||
* Performs common OMB request handling finishing.
|
||||
*
|
||||
* @access protected
|
||||
**/
|
||||
protected function finishOMBRequest() {
|
||||
header('HTTP/1.1 200 OK');
|
||||
header('Content-type: text/plain');
|
||||
/* There should be no clutter but the version. */
|
||||
echo "omb_version=" . OMB_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an OAuthServer
|
||||
*
|
||||
* Checks whether the OAuthServer is null. If so, initializes it with a
|
||||
* default value. Returns the OAuth server.
|
||||
*
|
||||
* @access protected
|
||||
**/
|
||||
protected function getOAuthServer() {
|
||||
if (is_null($this->oauth_server)) {
|
||||
$this->oauth_server = new OAuthServer($this->datastore);
|
||||
$this->oauth_server->add_signature_method(
|
||||
new OAuthSignatureMethod_HMAC_SHA1());
|
||||
}
|
||||
return $this->oauth_server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a notice
|
||||
*
|
||||
* Posts an OMB notice. This includes storing the notice and posting it to
|
||||
* subscribed users.
|
||||
*
|
||||
* @param OMB_Notice $notice The new notice
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return array An array mapping subscriber URIs to the exception posting to
|
||||
* them has raised; Empty array if no exception occured
|
||||
**/
|
||||
public function postNotice($notice) {
|
||||
$uri = $this->user->getIdentifierURI();
|
||||
|
||||
/* $notice is passed by reference and may change. */
|
||||
$this->datastore->saveNotice($notice);
|
||||
$subscribers = $this->datastore->getSubscriptions($uri);
|
||||
|
||||
/* No one to post to. */
|
||||
if (is_null($subscribers)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
require_once 'service_consumer.php';
|
||||
|
||||
$err = array();
|
||||
foreach($subscribers as $subscriber) {
|
||||
try {
|
||||
$service = new OMB_Service_Consumer($subscriber['uri'], $uri, $this->datastore);
|
||||
$service->setToken($subscriber['token'], $subscriber['secret']);
|
||||
$service->postNotice($notice);
|
||||
} catch (Exception $e) {
|
||||
$err[$subscriber['uri']] = $e;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return $err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a profile update
|
||||
*
|
||||
* Posts the current profile as an OMB profile update. This includes updating
|
||||
* the stored profile and posting it to subscribed users.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return array An array mapping subscriber URIs to the exception posting to
|
||||
* them has raised; Empty array if no exception occured
|
||||
**/
|
||||
public function updateProfile() {
|
||||
$uri = $this->user->getIdentifierURI();
|
||||
|
||||
$this->datastore->saveProfile($this->user);
|
||||
$subscribers = $this->datastore->getSubscriptions($uri);
|
||||
|
||||
/* No one to post to. */
|
||||
if (is_null($subscribers)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
require_once 'service_consumer.php';
|
||||
|
||||
$err = array();
|
||||
foreach($subscribers as $subscriber) {
|
||||
try {
|
||||
$service = new OMB_Service_Consumer($subscriber['uri'], $uri, $this->datastore);
|
||||
$service->setToken($subscriber['token'], $subscriber['secret']);
|
||||
$service->updateProfile($this->user);
|
||||
} catch (Exception $e) {
|
||||
$err[$subscriber['uri']] = $e;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return $err;
|
||||
}
|
||||
}
|
31
extlib/libomb/unsupportedserviceexception.php
Executable file
31
extlib/libomb/unsupportedserviceexception.php
Executable file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Exception stating that a requested service is not available
|
||||
*
|
||||
* This exception is raised when OMB_Service is asked to call a service the remote
|
||||
* server does not provide.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
class OMB_UnsupportedServiceException extends Exception {
|
||||
|
||||
}
|
||||
?>
|
33
extlib/libomb/xrds_mapper.php
Executable file
33
extlib/libomb/xrds_mapper.php
Executable file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Map XRDS actions to URLs
|
||||
*
|
||||
* This interface specifies classes which write the XRDS file announcing
|
||||
* the OMB server. An instance of an implementing class should be passed to
|
||||
* OMB_Service_Provider->writeXRDS.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
interface OMB_XRDS_Mapper {
|
||||
public function getURL($action);
|
||||
}
|
||||
?>
|
33
extlib/libomb/xrds_writer.php
Executable file
33
extlib/libomb/xrds_writer.php
Executable file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Write OMB-specific XRDS
|
||||
*
|
||||
* This interface specifies classes which write the XRDS file announcing
|
||||
* the OMB server. An instance of an implementing class should be passed to
|
||||
* OMB_Service_Provider->writeXRDS.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: 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/>.
|
||||
*
|
||||
* @package OMB
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @copyright 2009 Adrian Lang
|
||||
* @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
|
||||
**/
|
||||
|
||||
interface OMB_XRDS_Writer {
|
||||
public function writeXRDS($user, $mapper);
|
||||
}
|
||||
?>
|
@ -17,15 +17,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once(INSTALLDIR.'/lib/omb.php');
|
||||
require_once 'libomb/datastore.php';
|
||||
|
||||
class LaconicaOAuthDataStore extends OAuthDataStore
|
||||
class LaconicaDataStore extends OMB_Datastore
|
||||
{
|
||||
|
||||
// We keep a record of who's contacted us
|
||||
|
||||
function lookup_consumer($consumer_key)
|
||||
{
|
||||
$con = Consumer::staticGet('consumer_key', $consumer_key);
|
||||
@ -44,7 +45,9 @@ class LaconicaOAuthDataStore extends OAuthDataStore
|
||||
function lookup_token($consumer, $token_type, $token_key)
|
||||
{
|
||||
$t = new Token();
|
||||
$t->consumer_key = $consumer->key;
|
||||
if (!is_null($consumer)) {
|
||||
$t->consumer_key = $consumer->key;
|
||||
}
|
||||
$t->tok = $token_key;
|
||||
$t->type = ($token_type == 'access') ? 1 : 0;
|
||||
if ($t->find(true)) {
|
||||
@ -154,4 +157,348 @@ class LaconicaOAuthDataStore extends OAuthDataStore
|
||||
{
|
||||
return $this->new_access_token($consumer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Revoke specified OAuth token
|
||||
*
|
||||
* Revokes the authorization token specified by $token_key.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $token_key The token to be revoked
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function revoke_token($token_key) {
|
||||
$rt = new Token();
|
||||
$rt->tok = $token_key;
|
||||
$rt->type = 0;
|
||||
$rt->state = 0;
|
||||
if (!$rt->find(true)) {
|
||||
throw new Exception('Tried to revoke unknown token');
|
||||
}
|
||||
if (!$rt->delete()) {
|
||||
throw new Exception('Failed to delete revoked token');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize specified OAuth token
|
||||
*
|
||||
* Authorizes the authorization token specified by $token_key.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $token_key The token to be authorized
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function authorize_token($token_key) {
|
||||
$rt = new Token();
|
||||
$rt->tok = $token_key;
|
||||
$rt->type = 0;
|
||||
$rt->state = 0;
|
||||
if (!$rt->find(true)) {
|
||||
throw new Exception('Tried to authorize unknown token');
|
||||
}
|
||||
$orig_rt = clone($rt);
|
||||
$rt->state = 1; # Authorized but not used
|
||||
if (!$rt->update($orig_rt)) {
|
||||
throw new Exception('Failed to authorize token');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get profile by identifying URI
|
||||
*
|
||||
* Returns an OMB_Profile object representing the OMB profile identified by
|
||||
* $identifier_uri.
|
||||
* Returns null if there is no such OMB profile.
|
||||
* Throws exceptions in case of other error.
|
||||
*
|
||||
* @param string $identifier_uri The OMB identifier URI specifying the
|
||||
* requested profile
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return OMB_Profile The corresponding profile
|
||||
**/
|
||||
public function getProfile($identifier_uri) {
|
||||
/* getProfile is only used for remote profiles by libomb.
|
||||
TODO: Make it work with local ones anyway. */
|
||||
$remote = Remote_profile::staticGet('uri', $identifier_uri);
|
||||
if (!$remote) throw new Exception('No such remote profile');
|
||||
$profile = Profile::staticGet('id', $remote->id);
|
||||
if (!$profile) throw new Exception('No profile for remote user');
|
||||
|
||||
require_once INSTALLDIR.'/lib/omb.php';
|
||||
return profile_to_omb_profile($identifier_uri, $profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save passed profile
|
||||
*
|
||||
* Stores the OMB profile $profile. Overwrites an existing entry.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param OMB_Profile $profile The OMB profile which should be saved
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function saveProfile($omb_profile) {
|
||||
if (common_profile_url($omb_profile->getNickname()) ==
|
||||
$omb_profile->getProfileURL()) {
|
||||
throw new Exception('Not implemented');
|
||||
} else {
|
||||
$remote = Remote_profile::staticGet('uri', $omb_profile->getIdentifierURI());
|
||||
|
||||
if ($remote) {
|
||||
$exists = true;
|
||||
$profile = Profile::staticGet($remote->id);
|
||||
$orig_remote = clone($remote);
|
||||
$orig_profile = clone($profile);
|
||||
# XXX: compare current postNotice and updateProfile URLs to the ones
|
||||
# stored in the DB to avoid (possibly...) above attack
|
||||
} else {
|
||||
$exists = false;
|
||||
$remote = new Remote_profile();
|
||||
$remote->uri = $omb_profile->getIdentifierURI();
|
||||
$profile = new Profile();
|
||||
}
|
||||
|
||||
$profile->nickname = $omb_profile->getNickname();
|
||||
$profile->profileurl = $omb_profile->getProfileURL();
|
||||
|
||||
$fullname = $omb_profile->getFullname();
|
||||
$profile->fullname = is_null($fullname) ? '' : $fullname;
|
||||
$homepage = $omb_profile->getHomepage();
|
||||
$profile->homepage = is_null($homepage) ? '' : $homepage;
|
||||
$bio = $omb_profile->getBio();
|
||||
$profile->bio = is_null($bio) ? '' : $bio;
|
||||
$location = $omb_profile->getLocation();
|
||||
$profile->location = is_null($location) ? '' : $location;
|
||||
|
||||
if ($exists) {
|
||||
$profile->update($orig_profile);
|
||||
} else {
|
||||
$profile->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
$id = $profile->insert();
|
||||
if (!$id) {
|
||||
throw new Exception(_('Error inserting new profile'));
|
||||
}
|
||||
$remote->id = $id;
|
||||
}
|
||||
|
||||
$avatar_url = $omb_profile->getAvatarURL();
|
||||
if ($avatar_url) {
|
||||
if (!$this->add_avatar($profile, $avatar_url)) {
|
||||
throw new Exception(_('Error inserting avatar'));
|
||||
}
|
||||
} else {
|
||||
$avatar = $profile->getOriginalAvatar();
|
||||
if($avatar) $avatar->delete();
|
||||
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||
if($avatar) $avatar->delete();
|
||||
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
|
||||
if($avatar) $avatar->delete();
|
||||
$avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
|
||||
if($avatar) $avatar->delete();
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
if (!$remote->update($orig_remote)) {
|
||||
throw new Exception(_('Error updating remote profile'));
|
||||
}
|
||||
} else {
|
||||
$remote->created = DB_DataObject_Cast::dateTime(); # current time
|
||||
if (!$remote->insert()) {
|
||||
throw new Exception(_('Error inserting remote profile'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return $profile->setOriginal($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save passed notice
|
||||
*
|
||||
* Stores the OMB notice $notice. The datastore may change the passed notice.
|
||||
* This might by neccessary for URIs depending on a database key. Note that
|
||||
* it is the user’s duty to present a mechanism for his OMB_Datastore to
|
||||
* appropriately change his OMB_Notice.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param OMB_Notice $notice The OMB notice which should be saved
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function saveNotice(&$omb_notice) {
|
||||
if (Notice::staticGet('uri', $omb_notice->getIdentifierURI())) {
|
||||
throw new Exception(_('Duplicate notice'));
|
||||
}
|
||||
$author_uri = $omb_notice->getAuthor()->getIdentifierURI();
|
||||
common_log(LOG_DEBUG, $author_uri, __FILE__);
|
||||
$author = Remote_profile::staticGet('uri', $author_uri);
|
||||
if (!$author) {
|
||||
$author = User::staticGet('uri', $author_uri);
|
||||
}
|
||||
if (!$author) {
|
||||
throw new Exception('No such user');
|
||||
}
|
||||
|
||||
common_log(LOG_DEBUG, print_r($author, true), __FILE__);
|
||||
|
||||
$notice = Notice::saveNew($author->id,
|
||||
$omb_notice->getContent(),
|
||||
'omb',
|
||||
false,
|
||||
null,
|
||||
$omb_notice->getIdentifierURI());
|
||||
if (is_string($notice)) {
|
||||
throw new Exception($notice);
|
||||
}
|
||||
common_broadcast_notice($notice, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscriptions of a given profile
|
||||
*
|
||||
* Returns an array containing subscription informations for the specified
|
||||
* profile. Every array entry should in turn be an array with keys
|
||||
* 'uri´: The identifier URI of the subscriber
|
||||
* 'token´: The subscribe token
|
||||
* 'secret´: The secret token
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $subscribed_user_uri The OMB identifier URI specifying the
|
||||
* subscribed profile
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return mixed An array containing the subscriptions or 0 if no
|
||||
* subscription has been found.
|
||||
**/
|
||||
public function getSubscriptions($subscribed_user_uri) {
|
||||
$sub = new Subscription();
|
||||
|
||||
$user = $this->_getAnyProfile($subscribed_user_uri);
|
||||
|
||||
$sub->subscribed = $user->id;
|
||||
|
||||
if (!$sub->find(true)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Since we do not use OMB_Service_Provider’s action methods, there
|
||||
is no need to actually return the subscriptions. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function _getAnyProfile($uri)
|
||||
{
|
||||
$user = Remote_profile::staticGet('uri', $uri);
|
||||
if (!$user) {
|
||||
$user = User::staticGet('uri', $uri);
|
||||
}
|
||||
if (!$user) {
|
||||
throw new Exception('No such user');
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a subscription
|
||||
*
|
||||
* Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $subscriber_uri The OMB identifier URI specifying the
|
||||
* subscribing profile
|
||||
*
|
||||
* @param string $subscribed_user_uri The OMB identifier URI specifying the
|
||||
* subscribed profile
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function deleteSubscription($subscriber_uri, $subscribed_user_uri)
|
||||
{
|
||||
$sub = new Subscription();
|
||||
|
||||
$subscribed = $this->_getAnyProfile($subscribed_user_uri);
|
||||
$subscriber = $this->_getAnyProfile($subscriber_uri);
|
||||
|
||||
$sub->subscribed = $subscribed->id;
|
||||
$sub->subscriber = $subscriber->id;
|
||||
|
||||
$sub->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a subscription
|
||||
*
|
||||
* Saves the subscription from $subscriber_uri to $subscribed_user_uri.
|
||||
* Throws exceptions in case of error.
|
||||
*
|
||||
* @param string $subscriber_uri The OMB identifier URI specifying
|
||||
* the subscribing profile
|
||||
*
|
||||
* @param string $subscribed_user_uri The OMB identifier URI specifying
|
||||
* the subscribed profile
|
||||
* @param OAuthToken $token The access token
|
||||
*
|
||||
* @access public
|
||||
**/
|
||||
public function saveSubscription($subscriber_uri, $subscribed_user_uri,
|
||||
$token)
|
||||
{
|
||||
$sub = new Subscription();
|
||||
|
||||
$subscribed = $this->_getAnyProfile($subscribed_user_uri);
|
||||
$subscriber = $this->_getAnyProfile($subscriber_uri);
|
||||
|
||||
$sub->subscribed = $subscribed->id;
|
||||
$sub->subscriber = $subscriber->id;
|
||||
|
||||
$sub_exists = $sub->find(true);
|
||||
|
||||
if ($sub_exists) {
|
||||
$orig_sub = clone($sub);
|
||||
} else {
|
||||
$sub->created = DB_DataObject_Cast::dateTime();
|
||||
}
|
||||
|
||||
$sub->token = $token->key;
|
||||
$sub->secret = $token->secret;
|
||||
|
||||
if ($sub_exists) {
|
||||
$result = $sub->update($orig_sub);
|
||||
} else {
|
||||
$result = $sub->insert();
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
|
||||
throw new Exception(_('Couldn\'t insert new subscription.'));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Notify user, if necessary. */
|
||||
|
||||
if ($subscribed instanceof User) {
|
||||
mail_subscribe_notify_profile($subscribed,
|
||||
Profile::staticGet($subscriber->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
347
lib/omb.php
347
lib/omb.php
@ -17,36 +17,22 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once('OAuth.php');
|
||||
require_once(INSTALLDIR.'/lib/oauthstore.php');
|
||||
|
||||
require_once(INSTALLDIR.'/classes/Consumer.php');
|
||||
require_once(INSTALLDIR.'/classes/Nonce.php');
|
||||
require_once(INSTALLDIR.'/classes/Token.php');
|
||||
|
||||
require_once('Auth/Yadis/Yadis.php');
|
||||
|
||||
define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
|
||||
define('OMB_NAMESPACE', 'http://openmicroblogging.org/protocol/0.1');
|
||||
define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
|
||||
define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
|
||||
|
||||
define('OMB_ENDPOINT_UPDATEPROFILE', OMB_NAMESPACE.'/updateProfile');
|
||||
define('OMB_ENDPOINT_POSTNOTICE', OMB_NAMESPACE.'/postNotice');
|
||||
define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
|
||||
define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
|
||||
define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
|
||||
define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource');
|
||||
define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
|
||||
define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
|
||||
define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
|
||||
require_once INSTALLDIR.'/lib/oauthstore.php';
|
||||
require_once 'OAuth.php';
|
||||
require_once 'libomb/constants.php';
|
||||
require_once 'libomb/service_consumer.php';
|
||||
require_once 'libomb/notice.php';
|
||||
require_once 'libomb/profile.php';
|
||||
require_once 'Auth/Yadis/Yadis.php';
|
||||
|
||||
function omb_oauth_consumer()
|
||||
{
|
||||
static $con = null;
|
||||
if (!$con) {
|
||||
if (is_null($con)) {
|
||||
$con = new OAuthConsumer(common_root_url(), '');
|
||||
}
|
||||
return $con;
|
||||
@ -55,7 +41,7 @@ function omb_oauth_consumer()
|
||||
function omb_oauth_server()
|
||||
{
|
||||
static $server = null;
|
||||
if (!$server) {
|
||||
if (is_null($server)) {
|
||||
$server = new OAuthServer(omb_oauth_datastore());
|
||||
$server->add_signature_method(omb_hmac_sha1());
|
||||
}
|
||||
@ -65,8 +51,8 @@ function omb_oauth_server()
|
||||
function omb_oauth_datastore()
|
||||
{
|
||||
static $store = null;
|
||||
if (!$store) {
|
||||
$store = new LaconicaOAuthDataStore();
|
||||
if (is_null($store)) {
|
||||
$store = new LaconicaDataStore();
|
||||
}
|
||||
return $store;
|
||||
}
|
||||
@ -74,57 +60,18 @@ function omb_oauth_datastore()
|
||||
function omb_hmac_sha1()
|
||||
{
|
||||
static $hmac_method = null;
|
||||
if (!$hmac_method) {
|
||||
if (is_null($hmac_method)) {
|
||||
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
|
||||
}
|
||||
return $hmac_method;
|
||||
}
|
||||
|
||||
function omb_get_services($xrd, $type)
|
||||
{
|
||||
return $xrd->services(array(omb_service_filter($type)));
|
||||
}
|
||||
|
||||
function omb_service_filter($type)
|
||||
{
|
||||
return create_function('$s',
|
||||
'return omb_match_service($s, \''.$type.'\');');
|
||||
}
|
||||
|
||||
function omb_match_service($service, $type)
|
||||
{
|
||||
return in_array($type, $service->getTypes());
|
||||
}
|
||||
|
||||
function omb_service_uri($service)
|
||||
{
|
||||
if (!$service) {
|
||||
return null;
|
||||
}
|
||||
$uris = $service->getURIs();
|
||||
if (!$uris) {
|
||||
return null;
|
||||
}
|
||||
return $uris[0];
|
||||
}
|
||||
|
||||
function omb_local_id($service)
|
||||
{
|
||||
if (!$service) {
|
||||
return null;
|
||||
}
|
||||
$els = $service->getElements('xrd:LocalID');
|
||||
if (!$els) {
|
||||
return null;
|
||||
}
|
||||
$el = $els[0];
|
||||
return $service->parser->content($el);
|
||||
}
|
||||
|
||||
function omb_broadcast_remote_subscribers($notice)
|
||||
function omb_broadcast_notice($notice)
|
||||
{
|
||||
|
||||
# First, get remote users subscribed to this profile
|
||||
$omb_notice = notice_to_omb_notice($notice);
|
||||
|
||||
/* Get remote users subscribed to this profile. */
|
||||
$rp = new Remote_profile();
|
||||
|
||||
$rp->query('SELECT postnoticeurl, token, secret ' .
|
||||
@ -135,170 +82,148 @@ function omb_broadcast_remote_subscribers($notice)
|
||||
$posted = array();
|
||||
|
||||
while ($rp->fetch()) {
|
||||
if (!$posted[$rp->postnoticeurl]) {
|
||||
common_log(LOG_DEBUG, 'Posting to ' . $rp->postnoticeurl);
|
||||
if (omb_post_notice_keys($notice, $rp->postnoticeurl, $rp->token, $rp->secret)) {
|
||||
common_log(LOG_DEBUG, 'Finished to ' . $rp->postnoticeurl);
|
||||
$posted[$rp->postnoticeurl] = true;
|
||||
} else {
|
||||
common_log(LOG_DEBUG, 'Failed posting to ' . $rp->postnoticeurl);
|
||||
}
|
||||
if (isset($posted[$rp->postnoticeurl])) {
|
||||
/* We already posted to this url. */
|
||||
continue;
|
||||
}
|
||||
common_debug('Posting to ' . $rp->postnoticeurl, __FILE__);
|
||||
|
||||
/* Post notice. */
|
||||
$service = new Laconica_OMB_Service_Consumer(
|
||||
array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl));
|
||||
try {
|
||||
$service->setToken($rp->token, $rp->secret);
|
||||
$service->postNotice($omb_notice);
|
||||
} catch (Exception $e) {
|
||||
common_log(LOG_ERR, 'Failed posting to ' . $rp->postnoticeurl);
|
||||
common_log(LOG_ERR, 'Error status '.$e);
|
||||
continue;
|
||||
}
|
||||
$posted[$rp->postnoticeurl] = true;
|
||||
|
||||
common_debug('Finished to ' . $rp->postnoticeurl, __FILE__);
|
||||
}
|
||||
|
||||
$rp->free();
|
||||
unset($rp);
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
function omb_post_notice($notice, $remote_profile, $subscription)
|
||||
function omb_broadcast_profile($profile)
|
||||
{
|
||||
return omb_post_notice_keys($notice, $remote_profile->postnoticeurl, $subscription->token, $subscription->secret);
|
||||
}
|
||||
|
||||
function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
|
||||
{
|
||||
$user = User::staticGet('id', $notice->profile_id);
|
||||
$user = User::staticGet('id', $profile->id);
|
||||
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$con = omb_oauth_consumer();
|
||||
$profile = $user->getProfile();
|
||||
|
||||
$token = new OAuthToken($tk, $secret);
|
||||
$omb_profile = profile_to_omb_profile($user->uri, $profile, true);
|
||||
|
||||
$url = $postnoticeurl;
|
||||
$parsed = parse_url($url);
|
||||
$params = array();
|
||||
parse_str($parsed['query'], $params);
|
||||
/* Get remote users subscribed to this profile. */
|
||||
$rp = new Remote_profile();
|
||||
|
||||
$req = OAuthRequest::from_consumer_and_token($con, $token,
|
||||
'POST', $url, $params);
|
||||
$rp->query('SELECT updateprofileurl, token, secret ' .
|
||||
'FROM subscription JOIN remote_profile ' .
|
||||
'ON subscription.subscriber = remote_profile.id ' .
|
||||
'WHERE subscription.subscribed = ' . $profile->id . ' ');
|
||||
|
||||
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||
$req->set_parameter('omb_listenee', $user->uri);
|
||||
$req->set_parameter('omb_notice', $notice->uri);
|
||||
$req->set_parameter('omb_notice_content', $notice->content);
|
||||
$req->set_parameter('omb_notice_url', common_local_url('shownotice',
|
||||
array('notice' =>
|
||||
$notice->id)));
|
||||
$req->set_parameter('omb_notice_license', common_config('license', 'url'));
|
||||
$posted = array();
|
||||
|
||||
$user->free();
|
||||
unset($user);
|
||||
|
||||
$req->sign_request(omb_hmac_sha1(), $con, $token);
|
||||
|
||||
# We re-use this tool's fetcher, since it's pretty good
|
||||
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
|
||||
if (!$fetcher) {
|
||||
common_log(LOG_WARNING, 'Failed to initialize Yadis fetcher.', __FILE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $fetcher->post($req->get_normalized_http_url(),
|
||||
$req->to_postdata(),
|
||||
array('User-Agent: Laconica/' . LACONICA_VERSION));
|
||||
|
||||
if ($result->status == 403) { # not authorized, don't send again
|
||||
common_debug('403 result, deleting subscription', __FILE__);
|
||||
# FIXME: figure out how to delete this
|
||||
# $subscription->delete();
|
||||
return false;
|
||||
} else if ($result->status != 200) {
|
||||
common_debug('Error status '.$result->status, __FILE__);
|
||||
return false;
|
||||
} else { # success!
|
||||
parse_str($result->body, $return);
|
||||
if ($return['omb_version'] == OMB_VERSION_01) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
while ($rp->fetch()) {
|
||||
if (isset($posted[$rp->updateprofileurl])) {
|
||||
/* We already posted to this url. */
|
||||
continue;
|
||||
}
|
||||
common_debug('Posting to ' . $rp->updateprofileurl, __FILE__);
|
||||
|
||||
/* Update profile. */
|
||||
$service = new Laconica_OMB_Service_Consumer(
|
||||
array(OMB_ENDPOINT_UPDATEPROFILE => $rp->updateprofileurl));
|
||||
try {
|
||||
$service->setToken($rp->token, $rp->secret);
|
||||
$service->updateProfile($omb_profile);
|
||||
} catch (Exception $e) {
|
||||
common_log(LOG_ERR, 'Failed posting to ' . $rp->updateprofileurl);
|
||||
common_log(LOG_ERR, 'Error status '.$e);
|
||||
continue;
|
||||
}
|
||||
$posted[$rp->updateprofileurl] = true;
|
||||
|
||||
common_debug('Finished to ' . $rp->updateprofileurl, __FILE__);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function omb_broadcast_profile($profile)
|
||||
{
|
||||
# First, get remote users subscribed to this profile
|
||||
# XXX: use a join here rather than looping through results
|
||||
$sub = new Subscription();
|
||||
$sub->subscribed = $profile->id;
|
||||
if ($sub->find()) {
|
||||
$updated = array();
|
||||
while ($sub->fetch()) {
|
||||
$rp = Remote_profile::staticGet('id', $sub->subscriber);
|
||||
if ($rp) {
|
||||
if (!array_key_exists($rp->updateprofileurl, $updated)) {
|
||||
if (omb_update_profile($profile, $rp, $sub)) {
|
||||
$updated[$rp->updateprofileurl] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class Laconica_OMB_Service_Consumer extends OMB_Service_Consumer {
|
||||
public function __construct($urls)
|
||||
{
|
||||
$this->services = $urls;
|
||||
$this->datastore = omb_oauth_datastore();
|
||||
$this->oauth_consumer = omb_oauth_consumer();
|
||||
$this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function omb_update_profile($profile, $remote_profile, $subscription)
|
||||
function profile_to_omb_profile($uri, $profile, $force = false)
|
||||
{
|
||||
$user = User::staticGet($profile->id);
|
||||
$con = omb_oauth_consumer();
|
||||
$token = new OAuthToken($subscription->token, $subscription->secret);
|
||||
$url = $remote_profile->updateprofileurl;
|
||||
$parsed = parse_url($url);
|
||||
$params = array();
|
||||
parse_str($parsed['query'], $params);
|
||||
$req = OAuthRequest::from_consumer_and_token($con, $token,
|
||||
"POST", $url, $params);
|
||||
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||
$req->set_parameter('omb_listenee', $user->uri);
|
||||
$req->set_parameter('omb_listenee_profile', common_profile_url($profile->nickname));
|
||||
$req->set_parameter('omb_listenee_nickname', $profile->nickname);
|
||||
|
||||
# We use blanks to force emptying any existing values in these optional fields
|
||||
|
||||
$req->set_parameter('omb_listenee_fullname',
|
||||
($profile->fullname) ? $profile->fullname : '');
|
||||
$req->set_parameter('omb_listenee_homepage',
|
||||
($profile->homepage) ? $profile->homepage : '');
|
||||
$req->set_parameter('omb_listenee_bio',
|
||||
($profile->bio) ? $profile->bio : '');
|
||||
$req->set_parameter('omb_listenee_location',
|
||||
($profile->location) ? $profile->location : '');
|
||||
$omb_profile = new OMB_Profile($uri);
|
||||
$omb_profile->setNickname($profile->nickname);
|
||||
$omb_profile->setLicenseURL(common_config('license', 'url'));
|
||||
if (!is_null($profile->fullname)) {
|
||||
$omb_profile->setFullname($profile->fullname);
|
||||
} elseif ($force) {
|
||||
$omb_profile->setFullname('');
|
||||
}
|
||||
if (!is_null($profile->homepage)) {
|
||||
$omb_profile->setHomepage($profile->homepage);
|
||||
} elseif ($force) {
|
||||
$omb_profile->setHomepage('');
|
||||
}
|
||||
if (!is_null($profile->bio)) {
|
||||
$omb_profile->setBio($profile->bio);
|
||||
} elseif ($force) {
|
||||
$omb_profile->setBio('');
|
||||
}
|
||||
if (!is_null($profile->location)) {
|
||||
$omb_profile->setLocation($profile->location);
|
||||
} elseif ($force) {
|
||||
$omb_profile->setLocation('');
|
||||
}
|
||||
if (!is_null($profile->profileurl)) {
|
||||
$omb_profile->setProfileURL($profile->profileurl);
|
||||
} elseif ($force) {
|
||||
$omb_profile->setProfileURL('');
|
||||
}
|
||||
|
||||
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||
$req->set_parameter('omb_listenee_avatar',
|
||||
($avatar) ? $avatar->url : '');
|
||||
|
||||
$req->sign_request(omb_hmac_sha1(), $con, $token);
|
||||
|
||||
# We re-use this tool's fetcher, since it's pretty good
|
||||
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
|
||||
$result = $fetcher->post($req->get_normalized_http_url(),
|
||||
$req->to_postdata(),
|
||||
array('User-Agent: Laconica/' . LACONICA_VERSION));
|
||||
|
||||
if (empty($result) || !$result) {
|
||||
common_debug("Unable to contact " . $req->get_normalized_http_url());
|
||||
} else if ($result->status == 403) { # not authorized, don't send again
|
||||
common_debug('403 result, deleting subscription', __FILE__);
|
||||
$subscription->delete();
|
||||
return false;
|
||||
} else if ($result->status != 200) {
|
||||
common_debug('Error status '.$result->status, __FILE__);
|
||||
return false;
|
||||
} else { # success!
|
||||
parse_str($result->body, $return);
|
||||
if (isset($return['omb_version']) && $return['omb_version'] === OMB_VERSION_01) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if ($avatar) {
|
||||
$omb_profile->setAvatarURL($avatar->url);
|
||||
} elseif ($force) {
|
||||
$omb_profile->setAvatarURL('');
|
||||
}
|
||||
return $omb_profile;
|
||||
}
|
||||
|
||||
function notice_to_omb_notice($notice)
|
||||
{
|
||||
/* Create an OMB_Notice for $notice. */
|
||||
$user = User::staticGet('id', $notice->profile_id);
|
||||
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$profile = $user->getProfile();
|
||||
|
||||
$omb_notice = new OMB_Notice(profile_to_omb_profile($user->uri, $profile),
|
||||
$notice->uri,
|
||||
$notice->content);
|
||||
$omb_notice->setURL(common_local_url('shownotice', array('notice' =>
|
||||
$notice->id)));
|
||||
$omb_notice->setLicenseURL(common_config('license', 'url'));
|
||||
|
||||
return $omb_notice;
|
||||
}
|
||||
?>
|
||||
|
@ -39,7 +39,7 @@ class UnQueueManager
|
||||
case 'omb':
|
||||
if ($this->_isLocal($notice)) {
|
||||
require_once(INSTALLDIR.'/lib/omb.php');
|
||||
omb_broadcast_remote_subscribers($notice);
|
||||
omb_broadcast_notice($notice);
|
||||
}
|
||||
break;
|
||||
case 'public':
|
||||
|
@ -57,7 +57,7 @@ class OmbQueueHandler extends QueueHandler
|
||||
$this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id);
|
||||
return true;
|
||||
} else {
|
||||
return omb_broadcast_remote_subscribers($notice);
|
||||
return omb_broadcast_notice($notice);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user