forked from GNUsocial/gnu-social
Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly
This commit is contained in:
commit
28eb441812
@ -65,6 +65,15 @@ class TagprofileAction extends FormAction
|
||||
return sprintf(_m('ADDTOLIST','List %s'), $this->target->getNickname());
|
||||
}
|
||||
|
||||
function showPage()
|
||||
{
|
||||
// Only serve page content if we aren't POSTing via ajax
|
||||
// otherwise, we serve XML content from doPost()
|
||||
if (!$this->isPost() || !$this->boolean('ajax')) {
|
||||
parent::showPage();
|
||||
}
|
||||
}
|
||||
|
||||
function showContent()
|
||||
{
|
||||
$this->elementStart('div', 'entity_profile h-card');
|
||||
|
65
plugins/Diaspora/DiasporaPlugin.php
Normal file
65
plugins/Diaspora/DiasporaPlugin.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/*
|
||||
* GNU Social - a federating social network
|
||||
* Copyright (C) 2015, Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Diaspora federation protocol plugin for GNU Social
|
||||
*
|
||||
* Depends on:
|
||||
* - OStatus plugin
|
||||
* - WebFinger plugin
|
||||
*
|
||||
* @package ProtocolDiasporaPlugin
|
||||
* @maintainer Mikael Nordfeldth <mmn@hethane.se>
|
||||
*/
|
||||
|
||||
class DiasporaPlugin extends Plugin
|
||||
{
|
||||
const REL_SEED_LOCATION = 'http://joindiaspora.com/seed_location';
|
||||
const REL_GUID = 'http://joindiaspora.com/guid';
|
||||
const REL_PUBLIC_KEY = 'diaspora-public-key';
|
||||
|
||||
public function onEndAttachPubkeyToUserXRD(Magicsig $magicsig, XML_XRD $xrd, Profile $target)
|
||||
{
|
||||
// So far we've only handled RSA keys, but it can change in the future,
|
||||
// so be prepared. And remember to change the statically assigned type attribute below!
|
||||
assert($magicsig->publicKey instanceof Crypt_RSA);
|
||||
$xrd->links[] = new XML_XRD_Element_Link(self::REL_PUBLIC_KEY,
|
||||
base64_encode($magicsig->exportPublicKey()), 'RSA');
|
||||
|
||||
// Instead of choosing a random string, we calculate our GUID from the public key
|
||||
// by fingerprint through a sha256 hash.
|
||||
$xrd->links[] = new XML_XRD_Element_Link(self::REL_GUID,
|
||||
strtolower($magicsig->toFingerprint()));
|
||||
}
|
||||
|
||||
public function onPluginVersion(array &$versions)
|
||||
{
|
||||
$versions[] = array('name' => 'Diaspora',
|
||||
'version' => '0.1',
|
||||
'author' => 'Mikael Nordfeldth',
|
||||
'homepage' => 'https://gnu.io/social',
|
||||
// TRANS: Plugin description.
|
||||
'rawdescription' => _m('Follow people across social networks that implement '.
|
||||
'the <a href="https://diasporafoundation.org/">Diaspora</a> federation protocol.'));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
7
plugins/OStatus/EVENTS.txt
Normal file
7
plugins/OStatus/EVENTS.txt
Normal file
@ -0,0 +1,7 @@
|
||||
StartAttachPubkeyToUserXRD: Runs only for XRD generation where a Magicsig exists for a Profile which is a "person".
|
||||
@param Magicsig $magicsig crypto stuff related to the profile we're representing
|
||||
@param XRD $xrd the XRD object which holds all data for the profile we're representing
|
||||
|
||||
EndAttachPubkeyToUserXRD: Runs only for XRD generation where a Magicsig exists for a Profile which is a "person". And only if StartAttachPubkeyToUserXRD didn't abort.
|
||||
@param Magicsig $magicsig crypto stuff related to the profile we're representing
|
||||
@param XRD $xrd the XRD object which holds all data for the profile we're representing
|
@ -30,19 +30,6 @@ if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/extlib/phpseclib');
|
||||
|
||||
class FeedSubException extends Exception
|
||||
{
|
||||
function __construct($msg=null)
|
||||
{
|
||||
$type = get_class($this);
|
||||
if ($msg) {
|
||||
parent::__construct("$type: $msg");
|
||||
} else {
|
||||
parent::__construct($type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OStatusPlugin extends Plugin
|
||||
{
|
||||
/**
|
||||
@ -1315,11 +1302,14 @@ class OStatusPlugin extends Plugin
|
||||
$magicsig = Magicsig::generate($target->getUser());
|
||||
}
|
||||
|
||||
if ($magicsig instanceof Magicsig) {
|
||||
if (!$magicsig instanceof Magicsig) {
|
||||
return false; // value doesn't mean anything, just figured I'd indicate this function didn't do anything
|
||||
}
|
||||
if (Event::handle('StartAttachPubkeyToUserXRD', array($magicsig, $xrd, $target))) {
|
||||
$xrd->links[] = new XML_XRD_Element_Link(Magicsig::PUBLICKEYREL,
|
||||
'data:application/magic-public-key,'. $magicsig->toString());
|
||||
$xrd->links[] = new XML_XRD_Element_Link(Magicsig::DIASPORA_PUBLICKEYREL,
|
||||
base64_encode($magicsig->exportPublicKey()));
|
||||
// The following event handles plugins like Diaspora which add their own version of the Magicsig pubkey
|
||||
Event::handle('EndAttachPubkeyToUserXRD', array($magicsig, $xrd, $target));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,6 @@ require_once 'Crypt/RSA.php';
|
||||
class Magicsig extends Managed_DataObject
|
||||
{
|
||||
const PUBLICKEYREL = 'magic-public-key';
|
||||
const DIASPORA_PUBLICKEYREL = 'diaspora-public-key';
|
||||
|
||||
const DEFAULT_KEYLEN = 1024;
|
||||
const DEFAULT_SIGALG = 'RSA-SHA256';
|
||||
@ -179,18 +178,31 @@ class Magicsig extends Managed_DataObject
|
||||
* @param boolean $full_pair set to true to include the private key.
|
||||
* @return string
|
||||
*/
|
||||
public function toString($full_pair=false)
|
||||
public function toString($full_pair=false, $base64url=true)
|
||||
{
|
||||
$mod = Magicsig::base64_url_encode($this->publicKey->modulus->toBytes());
|
||||
$exp = Magicsig::base64_url_encode($this->publicKey->exponent->toBytes());
|
||||
$base64_func = $base64url ? 'Magicsig::base64_url_encode' : 'base64_encode';
|
||||
$mod = call_user_func($base64_func, $this->publicKey->modulus->toBytes());
|
||||
$exp = call_user_func($base64_func, $this->publicKey->exponent->toBytes());
|
||||
|
||||
$private_exp = '';
|
||||
if ($full_pair && $this->privateKey instanceof Crypt_RSA && $this->privateKey->exponent->toBytes()) {
|
||||
$private_exp = '.' . Magicsig::base64_url_encode($this->privateKey->exponent->toBytes());
|
||||
$private_exp = '.' . call_user_func($base64_func, $this->privateKey->exponent->toBytes());
|
||||
}
|
||||
|
||||
return 'RSA.' . $mod . '.' . $exp . $private_exp;
|
||||
}
|
||||
|
||||
public function toFingerprint()
|
||||
{
|
||||
// This assumes a specific behaviour from toString, to format as such:
|
||||
// "RSA." + base64(pubkey.modulus_as_bytes) + "." + base64(pubkey.exponent_as_bytes)
|
||||
// We don't want the base64 string to be the "url encoding" version because it is not
|
||||
// as common in programming libraries. And we want it to be base64 encoded since ASCII
|
||||
// representation avoids any problems with NULL etc. in less forgiving languages and also
|
||||
// just easier to debug...
|
||||
return strtolower(hash('sha256', $this->toString(false, false)));
|
||||
}
|
||||
|
||||
public function exportPublicKey($format=CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
|
||||
{
|
||||
$this->publicKey->setPublicKey();
|
||||
|
@ -17,13 +17,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* @package OStatusPlugin
|
||||
* @maintainer Brion Vibber <brion@status.net>
|
||||
* @author Brion Vibber <brion@status.net>
|
||||
* @maintainer Mikael Nordfeldth <mmn@hethane.se>
|
||||
*/
|
||||
class Ostatus_profile extends Managed_DataObject
|
||||
{
|
||||
@ -1746,11 +1745,8 @@ class Ostatus_profile extends Managed_DataObject
|
||||
throw new Exception(_m('Not a valid webfinger address.'));
|
||||
}
|
||||
|
||||
$hints = array('webfinger' => $addr);
|
||||
|
||||
$dhints = DiscoveryHints::fromXRD($xrd);
|
||||
|
||||
$hints = array_merge($hints, $dhints);
|
||||
$hints = array_merge(array('webfinger' => $addr),
|
||||
DiscoveryHints::fromXRD($xrd));
|
||||
|
||||
// If there's an Hcard, let's grab its info
|
||||
if (array_key_exists('hcard', $hints)) {
|
||||
|
13
plugins/OStatus/lib/feedsubexception.php
Normal file
13
plugins/OStatus/lib/feedsubexception.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
class FeedSubException extends Exception
|
||||
{
|
||||
function __construct($msg=null)
|
||||
{
|
||||
$type = get_class($this);
|
||||
if ($msg) {
|
||||
parent::__construct("$type: $msg");
|
||||
} else {
|
||||
parent::__construct($type);
|
||||
}
|
||||
}
|
||||
}
|
@ -71,7 +71,19 @@ class Salmon
|
||||
common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
if ($response->getStatus() != 200) {
|
||||
|
||||
// Diaspora wants a slightly different formatting on the POST (other Content-type, so body needs "xml=")
|
||||
if ($response->getStatus() === 422) {
|
||||
common_debug(sprintf('Salmon (from profile %d) endpoint %s returned status %s. Diaspora? Will try again! Body: %s',
|
||||
$user->id, $endpoint_uri, $response->getStatus(), $response->getBody()));
|
||||
$headers = array('Content-Type: application/x-www-form-urlencoded');
|
||||
$client->setBody('xml=' . Magicsig::base64_url_encode($envxml));
|
||||
$response = $client->post($endpoint_uri, $headers);
|
||||
}
|
||||
|
||||
// 200 OK is the best response
|
||||
// 202 Accepted is what we get from Diaspora for example
|
||||
if (!in_array($response->getStatus(), array(200, 202))) {
|
||||
common_log(LOG_ERR, sprintf('Salmon (from profile %d) endpoint %s returned status %s: %s',
|
||||
$user->id, $endpoint_uri, $response->getStatus(), $response->getBody()));
|
||||
return false;
|
||||
|
@ -41,13 +41,27 @@ class SalmonAction extends Action
|
||||
|
||||
parent::prepare($args);
|
||||
|
||||
if (!isset($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/magic-envelope+xml') {
|
||||
// TRANS: Client error. Do not translate "application/magic-envelope+xml".
|
||||
$this->clientError(_m('Salmon requires "application/magic-envelope+xml".'));
|
||||
if (!isset($_SERVER['CONTENT_TYPE'])) {
|
||||
// TRANS: Client error. Do not translate "Content-type"
|
||||
$this->clientError(_m('Salmon requires a Content-type header.'));
|
||||
}
|
||||
$envxml = null;
|
||||
switch ($_SERVER['CONTENT_TYPE']) {
|
||||
case 'application/magic-envelope+xml':
|
||||
$envxml = file_get_contents('php://input');
|
||||
break;
|
||||
case 'application/x-www-form-urlencoded':
|
||||
$envxml = Magicsig::base64_url_decode($this->trimmed('xml'));
|
||||
break;
|
||||
default:
|
||||
// TRANS: Client error. Do not translate the quoted "application/[type]" strings.
|
||||
$this->clientError(_m('Salmon requires "application/magic-envelope+xml". For Diaspora we also accept "application/x-www-form-urlencoded" with an "xml" parameter.', 415));
|
||||
}
|
||||
|
||||
try {
|
||||
$envxml = file_get_contents('php://input');
|
||||
if (empty($envxml)) {
|
||||
throw new ClientException('No magic envelope supplied in POST.');
|
||||
}
|
||||
$magic_env = new MagicEnvelope($envxml); // parse incoming XML as a MagicEnvelope
|
||||
|
||||
$entry = $magic_env->getPayload(); // Not cryptographically verified yet!
|
||||
|
@ -54,10 +54,28 @@ print "\n";
|
||||
print "Re-running feed discovery for profile URL $oprofile->uri\n";
|
||||
// @fixme will bork where the URI isn't the profile URL for now
|
||||
$discover = new FeedDiscovery();
|
||||
$feedurl = $discover->discoverFromURL($oprofile->uri);
|
||||
try {
|
||||
$feedurl = $discover->discoverFromURL($oprofile->uri);
|
||||
$salmonuri = $discover->getAtomLink(Salmon::REL_SALMON)
|
||||
?: $discover->getAtomLink(Salmon::NS_REPLIES); // NS_REPLIES is deprecated
|
||||
} catch (FeedSubException $e) {
|
||||
$acct = $oprofile->localProfile()->getAcctUri();
|
||||
print "Could not discover feeds HTML response, trying reconstructed acct URI: " . $acct;
|
||||
$disco = new Discovery();
|
||||
$xrd = $disco->lookup($acct);
|
||||
$hints = DiscoveryHints::fromXRD($xrd);
|
||||
|
||||
if (!array_key_exists('feedurl', $hints)) {
|
||||
throw new FeedSubNoFeedException($acct);
|
||||
}
|
||||
$feedurl = $hints['feedurl'];
|
||||
$salmonuri = array_key_exists('salmon', $hints) ? $hints['salmon'] : null;
|
||||
|
||||
// get the hub data too and put it in the FeedDiscovery object
|
||||
$discover->discoverFromFeedUrl($feedurl);
|
||||
}
|
||||
|
||||
$huburi = $discover->getHubLink();
|
||||
$salmonuri = $discover->getAtomLink(Salmon::REL_SALMON)
|
||||
?: $discover->getAtomLink(Salmon::NS_REPLIES);
|
||||
|
||||
print " Feed URL: $feedurl\n";
|
||||
print " Hub URL: $huburi\n";
|
||||
|
Loading…
Reference in New Issue
Block a user