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.
*/
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',
foreach ($xrd->links as $link) {
switch ($link->rel) {
- case WebFinger::PROFILEPAGE:
+ case WebFingerResource::PROFILEPAGE:
$hints['profileurl'] = $link->href;
break;
case Salmon::NS_MENTIONS:
* @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',
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
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) {
*/
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);
// 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;
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);
}
}
+++ /dev/null
-<?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;
- }
-}
--- /dev/null
+<?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');
+ }
+}
--- /dev/null
+<?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
+ }
+}
--- /dev/null
+<?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));
+ }
+ }
+}
// our back-compatibility with StatusNet <=1.1.1
protected $defaultformat = null;
- protected $resource = null;
- protected $target = null;
protected $xrd = null;
public function isReadOnly($args)