git.gnu.io has moved to IP address 209.51.188.249 -- please double check where you are logging in.

Commit e868ebfe authored by mmn's avatar mmn

WebFingerResource introduced, instead of strict Profile object

This is the beginning of getting notice URI info via WebFinger

*XrdActionLinks is renamed *WebFingerProfileLinks, check EVENTS.txt
in WebFinger plugin for new events.
parent d632df32
......@@ -216,6 +216,12 @@ class Notice extends Managed_DataObject
return $this->uri;
}
public function getUrl()
{
// The risk is we start having empty urls and non-http uris...
return $this->url ?: $this->uri;
}
/**
* Extract #hashtags from this notice's content and save them to the database.
*/
......
......@@ -1314,7 +1314,7 @@ class OStatusPlugin extends Plugin
return true;
}
function onEndXrdActionLinks(XML_XRD $xrd, Profile $target)
function onEndWebFingerProfileLinks(XML_XRD $xrd, Profile $target)
{
$xrd->links[] = new XML_XRD_Element_Link(Discovery::UPDATESFROM,
common_local_url('ApiTimelineUser',
......
......@@ -26,7 +26,7 @@ class DiscoveryHints {
foreach ($xrd->links as $link) {
switch ($link->rel) {
case WebFinger::PROFILEPAGE:
case WebFingerResource::PROFILEPAGE:
$hints['profileurl'] = $link->href;
break;
case Salmon::NS_MENTIONS:
......
......@@ -773,7 +773,7 @@ class OpenIDPlugin extends Plugin
* @return boolean hook value (always true)
*/
function onEndXrdActionLinks(XML_XRD $xrd, Profile $target)
function onEndWebFingerProfileLinks(XML_XRD $xrd, Profile $target)
{
$xrd->links[] = new XML_XRD_Element_Link(
'http://specs.openid.net/auth/2.0/provider',
......
......@@ -4,26 +4,28 @@ StartHostMetaLinks: Start /.well-known/host-meta links
EndHostMetaLinks: End /.well-known/host-meta links
- &links: array containing the links elements to be written
StartWebFingerReconstruction:
StartGetWebFingerResource: Get a WebFingerResource extended object by resource string
- $resource String that contains the requested URI
- &$target WebFingerResource extended object goes here
- $args Array which may contains arguments such as 'rel' filtering values
EndGetWebFingerResource: Last attempts getting a WebFingerResource object
- $resource String that contains the requested URI
- &$target WebFingerResource extended object goes here
- $args Array which may contains arguments such as 'rel' filtering values
StartWebFingerReconstruction: Generate an acct: uri from a Profile object
- $profile: Profile object for which we want a WebFinger ID
- &$acct: String reference where reconstructed ID is stored
EndWebFingerReconstruction:
EndWebFingerReconstruction: Last attempts to generate an acct: uri from a Profile object
- $profile: Profile object for which we want a WebFinger ID
- &$acct: String reference where reconstructed ID is stored
StartXrdActionAliases: About to set aliases for the XRD for a user
- $xrd: XML_XRD object being shown
- $target: Profile being shown
EndXrdActionAliases: Done with aliases for the XRD for a user
- $xrd: XML_XRD object being shown
- $target: Profile being shown
StartXrdActionLinks: About to set links for the XRD for a profile
StartWebFingerProfileLinks: About to set links for the resource descriptor of a profile
- $xrd: XML_XRD object being shown
- $target: Profile being shown
EndXrdActionLinks: Done with links for the XRD for a profile
EndWebFingerProfileLinks: Done with links for the resource descriptor of a profile
- $xrd: XML_XRD object being shown
- $target: Profile being shown
......@@ -58,6 +58,48 @@ class WebFingerPlugin extends Plugin
return true;
}
public function onEndGetWebFingerResource($resource, WebFingerResource &$target=null, array $args=array())
{
$profile = null;
if (Discovery::isAcct($resource)) {
$parts = explode('@', substr(urldecode($resource), 5)); // 5 is strlen of 'acct:'
if (count($parts) == 2) {
list($nick, $domain) = $parts;
if ($domain === common_config('site', 'server')) {
$nick = common_canonical_nickname($nick);
$user = User::getKV('nickname', $nick);
if (!($user instanceof User)) {
throw new NoSuchUserException(array('nickname'=>$nick));
}
$profile = $user->getProfile();
} else {
throw new Exception(_('Remote profiles not supported via WebFinger yet.'));
}
}
} else {
$user = User::getKV('uri', $resource);
if ($user instanceof User) {
$profile = $user->getProfile();
} else {
// try and get it by profile url
$profile = Profile::getKV('profileurl', $resource);
}
}
if ($profile instanceof Profile) {
$target = new WebFingerResource_Profile($profile);
return false; // We got our target, stop handler execution
}
$notice = Notice::getKV('uri', $resource);
if ($notice instanceof Notice) {
$target = new WebFingerResource_Notice($notice);
return false;
}
return true;
}
public function onStartHostMetaLinks(array &$links)
{
foreach (Discovery::supportedMimeTypes() as $type) {
......
......@@ -26,6 +26,9 @@ if (!defined('GNUSOCIAL')) { exit(1); }
*/
class WebfingerAction extends XrdAction
{
protected $resource = null; // string with the resource URI
protected $target = null; // object of the WebFingerResource class
protected function prepare(array $args=array())
{
parent::prepare($args);
......@@ -33,34 +36,8 @@ class WebfingerAction extends XrdAction
// throws exception if resource is empty
$this->resource = Discovery::normalize($this->trimmed('resource'));
if (Discovery::isAcct($this->resource)) {
$parts = explode('@', substr(urldecode($this->resource), 5));
if (count($parts) == 2) {
list($nick, $domain) = $parts;
if ($domain === common_config('site', 'server')) {
$nick = common_canonical_nickname($nick);
$user = User::getKV('nickname', $nick);
if (!($user instanceof User)) {
throw new NoSuchUserException(array('nickname'=>$nick));
}
$this->target = $user->getProfile();
} else {
throw new Exception(_('Remote profiles not supported via WebFinger yet.'));
}
}
} else {
$user = User::getKV('uri', $this->resource);
if ($user instanceof User) {
$this->target = $user->getProfile();
} else {
// try and get it by profile url
$this->target = Profile::getKV('profileurl', $this->resource);
}
}
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when user not found for an action.
$this->clientError(_('No such user: ') . var_export($this->resource,true), 404);
if (Event::handle('StartGetWebFingerResource', array($this->resource, &$this->target, $this->args))) {
Event::handle('EndGetWebFingerResource', array($this->resource, &$this->target, $this->args));
}
return true;
......@@ -68,55 +45,18 @@ class WebfingerAction extends XrdAction
protected function setXRD()
{
if (empty($this->target)) {
if (!($this->target instanceof WebFingerResource)) {
throw new Exception(_('Target not set for resource descriptor'));
}
// $this->target set in a _child_ class prepare()
$nick = $this->target->nickname;
$this->xrd->subject = $this->resource;
if (Event::handle('StartXrdActionAliases', array($this->xrd, $this->target))) {
$uris = WebFinger::getIdentities($this->target);
foreach ($uris as $uri) {
if ($uri != $this->xrd->subject && !in_array($uri, $this->xrd->aliases)) {
$this->xrd->aliases[] = $uri;
}
foreach ($this->target->getAliases() as $alias) {
if ($alias != $this->xrd->subject && !in_array($alias, $this->xrd->aliases)) {
$this->xrd->aliases[] = $alias;
}
Event::handle('EndXrdActionAliases', array($this->xrd, $this->target));
}
if (Event::handle('StartXrdActionLinks', array($this->xrd, $this->target))) {
$this->xrd->links[] = new XML_XRD_Element_Link(WebFinger::PROFILEPAGE,
$this->target->getUrl(), 'text/html');
// XFN
$this->xrd->links[] = new XML_XRD_Element_Link('http://gmpg.org/xfn/11',
$this->target->getUrl(), 'text/html');
// FOAF
$this->xrd->links[] = new XML_XRD_Element_Link('describedby',
common_local_url('foaf', array('nickname' => $nick)),
'application/rdf+xml');
$link = new XML_XRD_Element_Link('http://apinamespace.org/atom',
common_local_url('ApiAtomService', array('id' => $nick)),
'application/atomsvc+xml');
// XML_XRD must implement changing properties first $link['http://apinamespace.org/atom/username'] = $nick;
$this->xrd->links[] = clone $link;
if (common_config('site', 'fancy')) {
$apiRoot = common_path('api/', true);
} else {
$apiRoot = common_path('index.php/api/', true);
}
$link = new XML_XRD_Element_Link('http://apinamespace.org/twitter', $apiRoot);
// XML_XRD must implement changing properties first $link['http://apinamespace.org/twitter/username'] = $nick;
$this->xrd->links[] = clone $link;
Event::handle('EndXrdActionLinks', array($this->xrd, $this->target));
}
$this->target->updateXRD($this->xrd);
}
}
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* WebFinger functions
*
* PHP version 5
*
* 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 GNUsocial
* @author Mikael Nordfeldth
* @copyright 2013 Free Software Foundation, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class WebFinger
{
const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
/*
* Reconstructs a WebFinger ID from data we know about the profile.
*
* @param Profile $profile The profile we want a WebFinger ID for
*
* @return string $acct acct:user@example.com URI
*/
public static function reconstruct(Profile $profile)
{
$acct = null;
if (Event::handle('StartWebFingerReconstruction', array($profile, &$acct))) {
// TODO: getUri may not always give us the correct host on remote users?
$host = parse_url($profile->getUri(), PHP_URL_HOST);
if (empty($profile->nickname) || empty($host)) {
throw new WebFingerReconstructionException($profile);
}
$acct = sprintf('acct:%s@%s', $profile->nickname, $host);
Event::handle('EndWebFingerReconstruction', array($profile, &$acct));
}
return $acct;
}
/*
* Gets all URI aliases for a Profile
*
* @param Profile $profile The profile we want aliases for
*
* @return array $aliases All the Profile's alternative URLs
*/
public static function getAliases(Profile $profile)
{
$aliases = array();
$aliases[] = $profile->getUri();
try {
$aliases[] = $profile->getUrl();
} catch (InvalidUrlException $e) {
common_debug('Profile id='.$profile->id.' has invalid profileurl: ' .
var_export($profile->profileurl, true));
}
return $aliases;
}
/*
* Gets all identities for a Profile, includes WebFinger acct: if
* available, as well as alias URLs.
*
* @param Profile $profile The profile we want aliases for
*
* @return array $uris WebFinger acct: URI and alias URLs
*/
public static function getIdentities(Profile $profile)
{
$uris = array();
try {
$uris[] = self::reconstruct($profile);
} catch (WebFingerReconstructionException $e) {
common_debug('WebFinger reconstruction for Profile failed, ' .
' (id='.$profile->id.')');
}
$uris = array_merge($uris, self::getAliases($profile));
return $uris;
}
}
<?php
/**
* WebFinger resource parent class
*
* @package GNUsocial
* @author Mikael Nordfeldth
* @copyright 2013 Free Software Foundation, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
abstract class WebFingerResource
{
const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
protected $identities = array();
protected $object = null;
protected $type = null;
public function __construct(Managed_DataObject $object)
{
$this->object = $object;
}
public function getObject()
{
if ($this->object === null) {
throw new ServerException('Object is not set');
}
return $this->object;
}
public function getAliases()
{
$aliases = array();
// Add the URI as an identity, this is _not_ necessarily an HTTP url
$aliases[] = $this->object->getUri();
try {
$aliases[] = $this->object->getUrl();
} catch (InvalidUrlException $e) {
// getUrl failed because no valid URL could be returned, just ignore it
}
return $aliases;
}
public function updateXRD(XML_XRD $xrd) {
$xrd->links[] = new XML_XRD_Element_Link(WebFingerResource::PROFILEPAGE,
$this->object->getUrl(), 'text/html');
}
}
<?php
/**
* WebFinger resource for Notice objects
*
* @package GNUsocial
* @author Mikael Nordfeldth
* @copyright 2013 Free Software Foundation, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class WebFingerResource_Notice extends WebFingerResource
{
public function __construct(Notice $object)
{
// The type argument above verifies that it's our class
parent::__construct($object);
}
public function updateXRD(XML_XRD $xrd)
{
parent::updateXRD($xrd);
// TODO: Add atom and json representation links here
// TODO: Add Salmon/callback links and stuff here
}
}
<?php
/**
* WebFinger resource for Profile objects
*
* @package GNUsocial
* @author Mikael Nordfeldth
* @copyright 2013 Free Software Foundation, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class WebFingerResource_Profile extends WebFingerResource
{
public function __construct(Profile $object)
{
// The type argument above verifies that it's our class
parent::__construct($object);
}
public function getAliases()
{
$aliases = array();
try {
// Try to create an acct: URI if we're dealing with a profile
$aliases[] = $this->reconstructAcct();
} catch (WebFingerReconstructionException $e) {
common_debug("WebFinger reconstruction for Profile failed (id={$this->object->id})");
}
return array_merge($aliases, parent::getAliases());
}
protected function reconstructAcct()
{
$acct = null;
if (Event::handle('StartWebFingerReconstruction', array($this->object, &$acct))) {
// TODO: getUri may not always give us the correct host on remote users?
$host = parse_url($this->object->getUri(), PHP_URL_HOST);
if (empty($this->object->nickname) || empty($host)) {
throw new WebFingerReconstructionException($this->object);
}
$acct = sprintf('acct:%s@%s', $this->object->nickname, $host);
Event::handle('EndWebFingerReconstruction', array($this->object, &$acct));
}
return $acct;
}
public function updateXRD(XML_XRD $xrd)
{
if (Event::handle('StartWebFingerProfileLinks', array($xrd, $this->object))) {
parent::updateXRD($xrd);
// XFN
$xrd->links[] = new XML_XRD_Element_Link('http://gmpg.org/xfn/11',
$this->object->getUrl(), 'text/html');
// FOAF
$xrd->links[] = new XML_XRD_Element_Link('describedby',
common_local_url('foaf',
array('nickname' => $this->object->nickname)),
'application/rdf+xml');
$link = new XML_XRD_Element_Link('http://apinamespace.org/atom',
common_local_url('ApiAtomService',
array('id' => $this->object->nickname)),
'application/atomsvc+xml');
// XML_XRD must implement changing properties first $link['http://apinamespace.org/atom/username'] = $this->object->nickname;
$xrd->links[] = clone $link;
if (common_config('site', 'fancy')) {
$apiRoot = common_path('api/', true);
} else {
$apiRoot = common_path('index.php/api/', true);
}
$link = new XML_XRD_Element_Link('http://apinamespace.org/twitter', $apiRoot);
// XML_XRD must implement changing properties first $link['http://apinamespace.org/twitter/username'] = $this->object->nickname;
$xrd->links[] = clone $link;
Event::handle('EndWebFingerProfileLinks', array($xrd, $this->object));
}
}
}
......@@ -31,8 +31,6 @@ abstract class XrdAction extends Action
// our back-compatibility with StatusNet <=1.1.1
protected $defaultformat = null;
protected $resource = null;
protected $target = null;
protected $xrd = null;
public function isReadOnly($args)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment