Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly

This commit is contained in:
abjectio 2015-06-06 18:15:17 +02:00
commit 28eb441812
10 changed files with 174 additions and 38 deletions

View File

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

View 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;
}
}

View 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

View File

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

View File

@ -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();

View File

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

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

View File

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

View File

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

View File

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