OStatus support removed
This commit is contained in:
parent
eb066b258d
commit
e8a3be6820
87 changed files with 773 additions and 4383 deletions
|
|
@ -1,148 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace Friendica\Module\OStatus;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GServer;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class PubSub extends \Friendica\BaseModule
|
||||
{
|
||||
/** @var Database */
|
||||
private $database;
|
||||
/** @var App\Request */
|
||||
private $request;
|
||||
|
||||
public function __construct(App\Request $request, Database $database, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->database = $database;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
{
|
||||
$xml = Network::postdata();
|
||||
|
||||
$this->logger->info('Feed arrived.', ['from' => $this->request->getRemoteAddress(), 'for' => $this->args->getCommand(), 'user-agent' => $this->server['HTTP_USER_AGENT']]);
|
||||
$this->logger->debug('Data stream.', ['xml' => $xml]);
|
||||
$this->logger->debug('Got request data.', ['request' => $request]);
|
||||
|
||||
$nickname = $this->parameters['nickname'] ?? '';
|
||||
$contact_id = $this->parameters['cid'] ?? 0;
|
||||
|
||||
$importer = $this->database->selectFirst('user', [], ['nickname' => $nickname, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]);
|
||||
if (!$importer) {
|
||||
throw new HTTPException\OKException();
|
||||
}
|
||||
|
||||
$condition = ['id' => $contact_id, 'uid' => $importer['uid'], 'subhub' => true, 'blocked' => false];
|
||||
$contact = $this->database->selectFirst('contact', [], $condition);
|
||||
if (!$contact) {
|
||||
$author = OStatus::salmonAuthor($xml, $importer);
|
||||
if (!empty($author['contact-id'])) {
|
||||
$condition = ['id' => $author['contact-id'], 'uid' => $importer['uid'], 'subhub' => true, 'blocked' => false];
|
||||
$contact = $this->database->selectFirst('contact', [], $condition);
|
||||
$this->logger->notice('No record found for nickname, using author entry instead.', ['nickname' => $nickname, 'contact-id' => $contact_id, 'author-contact-id' => $author['contact-id']]);
|
||||
}
|
||||
|
||||
if (!$contact) {
|
||||
$this->logger->notice("Contact wasn't found - ignored.", ['author-link' => $author['author-link'], 'contact-id' => $contact_id, 'nickname' => $nickname, 'xml' => $xml]);
|
||||
throw new HTTPException\OKException();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($contact['gsid'])) {
|
||||
GServer::setProtocol($contact['gsid'], Post\DeliveryData::OSTATUS);
|
||||
}
|
||||
|
||||
if (!in_array($contact['rel'], [Contact::SHARING, Contact::FRIEND]) && ($contact['network'] != Protocol::FEED)) {
|
||||
$this->logger->notice('Contact is not expected to share with us - ignored.', ['contact-id' => $contact['id']]);
|
||||
throw new HTTPException\OKException();
|
||||
}
|
||||
|
||||
// We only import feeds from OStatus here
|
||||
if (!in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::OSTATUS])) {
|
||||
$this->logger->warning('Unexpected network', ['contact' => $contact, 'network' => $contact['network']]);
|
||||
throw new HTTPException\OKException();
|
||||
}
|
||||
|
||||
$this->logger->info('Import item from Contact.', ['nickname' => $nickname, 'contact-nickname' => $contact['nick'], 'contact-id' => $contact['id']]);
|
||||
$feedhub = '';
|
||||
Item::incrementOutbound(Protocol::OSTATUS);
|
||||
OStatus::import($xml, $importer, $contact, $feedhub);
|
||||
|
||||
throw new HTTPException\OKException();
|
||||
}
|
||||
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
$nickname = $this->parameters['nickname'] ?? '';
|
||||
$contact_id = $this->parameters['cid'] ?? 0;
|
||||
|
||||
$hub_mode = trim($request['hub_mode'] ?? '');
|
||||
$hub_topic = trim($request['hub_topic'] ?? '');
|
||||
$hub_challenge = trim($request['hub_challenge'] ?? '');
|
||||
$hub_verify = trim($request['hub_verify_token'] ?? '');
|
||||
|
||||
$this->logger->notice('Subscription start.', ['from' => $this->request->getRemoteAddress(), 'mode' => $hub_mode, 'nickname' => $nickname]);
|
||||
$this->logger->debug('Data: ', ['get' => $request]);
|
||||
|
||||
$owner = $this->database->selectFirst('user', ['uid'], ['nickname' => $nickname, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]);
|
||||
if (!$owner) {
|
||||
$this->logger->notice('Local account not found.', ['nickname' => $nickname]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
$condition = ['uid' => $owner['uid'], 'id' => $contact_id, 'blocked' => false, 'pending' => false];
|
||||
|
||||
if (!empty($hub_verify)) {
|
||||
$condition['hub-verify'] = $hub_verify;
|
||||
}
|
||||
|
||||
$contact = $this->database->selectFirst('contact', ['id', 'poll'], $condition);
|
||||
if (!$contact) {
|
||||
$this->logger->notice('Contact not found.', ['contact' => $contact_id]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
if (!empty($hub_topic) && !Strings::compareLink($hub_topic, $contact['poll'])) {
|
||||
$this->logger->notice("Hub topic isn't valid for Contact.", ['hub_topic' => $hub_topic, 'contact_poll' => $contact['poll']]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
// We must initiate an unsubscribe request with a verify_token.
|
||||
// Don't allow outsiders to unsubscribe us.
|
||||
|
||||
if (($hub_mode === 'unsubscribe') && empty($hub_verify)) {
|
||||
$this->logger->notice('Bogus unsubscribe');
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
if (!empty($hub_mode)) {
|
||||
Contact::update(['subhub' => $hub_mode === 'subscribe'], ['id' => $contact['id']]);
|
||||
$this->logger->notice('Success for contact.', ['mode' => $hub_mode, 'contact' => $contact_id]);
|
||||
}
|
||||
|
||||
$this->httpExit($hub_challenge);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace Friendica\Module\OStatus;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Model\PushSubscriber;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* An open, simple, web-scale and decentralized pubsub protocol.
|
||||
*
|
||||
* Part of the OStatus stack.
|
||||
*
|
||||
* See https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html
|
||||
*
|
||||
* @version 0.4
|
||||
*/
|
||||
class PubSubHubBub extends \Friendica\BaseModule
|
||||
{
|
||||
/** @var IManageConfigValues */
|
||||
private $config;
|
||||
/** @var Database */
|
||||
private $database;
|
||||
/** @var ICanSendHttpRequests */
|
||||
private $httpClient;
|
||||
/** @var App\Request */
|
||||
private $request;
|
||||
|
||||
public function __construct(App\Request $request, ICanSendHttpRequests $httpClient, Database $database, IManageConfigValues $config, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->config = $config;
|
||||
$this->database = $database;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
{
|
||||
// PuSH subscription must be considered "public" so just block it
|
||||
// if public access isn't enabled.
|
||||
if ($this->config->get('system', 'block_public')) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
$this->logger->debug('Got request data.', ['request' => $request]);
|
||||
|
||||
// Subscription request from subscriber
|
||||
// https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.5.1
|
||||
// Example from GNU Social:
|
||||
// [hub_mode] => subscribe
|
||||
// [hub_callback] => http://status.local/main/push/callback/1
|
||||
// [hub_verify] => sync
|
||||
// [hub_verify_token] => af11...
|
||||
// [hub_secret] => af11...
|
||||
// [hub_topic] => http://friendica.local/dfrn_poll/sazius
|
||||
|
||||
$hub_mode = $request['hub_mode'] ?? '';
|
||||
$hub_callback = $request['hub_callback'] ?? '';
|
||||
$hub_verify_token = $request['hub_verify_token'] ?? '';
|
||||
$hub_secret = $request['hub_secret'] ?? '';
|
||||
$hub_topic = $request['hub_topic'] ?? '';
|
||||
|
||||
// check for valid hub_mode
|
||||
if ($hub_mode === 'subscribe') {
|
||||
$subscribe = 1;
|
||||
} elseif ($hub_mode === 'unsubscribe') {
|
||||
$subscribe = 0;
|
||||
} else {
|
||||
$this->logger->notice('Invalid hub_mod - ignored.', ['mode' => $hub_mode]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
$this->logger->info('hub_mode request details.', ['from' => $this->request->getRemoteAddress(), 'mode' => $hub_mode]);
|
||||
|
||||
$nickname = $this->parameters['nickname'] ?? $hub_topic;
|
||||
|
||||
// Extract nickname and strip any .atom extension
|
||||
$nickname = basename($nickname, '.atom');
|
||||
if (!$nickname) {
|
||||
$this->logger->notice('Empty nick, ignoring.');
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
// fetch user from database given the nickname
|
||||
$condition = ['nickname' => $nickname, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false];
|
||||
$owner = $this->database->selectFirst('user', ['uid', 'nickname'], $condition);
|
||||
if (!$owner) {
|
||||
$this->logger->notice('Local account not found', ['nickname' => $nickname, 'topic' => $hub_topic, 'callback' => $hub_callback]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
// get corresponding row from contact table
|
||||
$condition = ['uid' => $owner['uid'], 'blocked' => false, 'pending' => false, 'self' => true];
|
||||
$contact = $this->database->selectFirst('contact', ['poll'], $condition);
|
||||
if (!$contact) {
|
||||
$this->logger->notice('Self contact for user not found.', ['uid' => $owner['uid']]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
// sanity check that topic URLs are the same
|
||||
$hub_topic2 = str_replace('/feed/', '/dfrn_poll/', $hub_topic);
|
||||
$self = $this->baseUrl . '/api/statuses/user_timeline/' . $owner['nickname'] . '.atom';
|
||||
|
||||
if (!Strings::compareLink($hub_topic, $contact['poll']) && !Strings::compareLink($hub_topic2, $contact['poll']) && !Strings::compareLink($hub_topic, $self)) {
|
||||
$this->logger->notice('Hub topic invalid', ['hub_topic' => $hub_topic, 'poll' => $contact['poll']]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
// do subscriber verification according to the PuSH protocol
|
||||
$hub_challenge = Strings::getRandomHex(40);
|
||||
|
||||
$params = http_build_query([
|
||||
'hub.mode' => $subscribe == 1 ? 'subscribe' : 'unsubscribe',
|
||||
'hub.topic' => $hub_topic,
|
||||
'hub.challenge' => $hub_challenge,
|
||||
'hub.verify_token' => $hub_verify_token,
|
||||
|
||||
// lease time is hard coded to one week (in seconds)
|
||||
// we don't actually enforce the lease time because GNU
|
||||
// Social/StatusNet doesn't honour it (yet)
|
||||
'hub.lease_seconds' => 604800,
|
||||
]);
|
||||
|
||||
$hub_callback = rtrim($hub_callback, ' ?&#');
|
||||
$separator = parse_url($hub_callback, PHP_URL_QUERY) === null ? '?' : '&';
|
||||
|
||||
$fetchResult = $this->httpClient->get($hub_callback . $separator . $params, HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::PUBSUB]);
|
||||
$body = $fetchResult->getBodyString();
|
||||
$returnCode = $fetchResult->getReturnCode();
|
||||
|
||||
// give up if the HTTP return code wasn't a success (2xx)
|
||||
if ($returnCode < 200 || $returnCode > 299) {
|
||||
$this->logger->notice('Subscriber verification ignored', ['hub_topic' => $hub_topic, 'callback' => $hub_callback, 'returnCode' => $returnCode]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
// check that the correct hub_challenge code was echoed back
|
||||
if (trim($body) !== $hub_challenge) {
|
||||
$this->logger->notice('Subscriber did not echo back hub.challenge, ignoring.', ['hub_challenge' => $hub_challenge, 'body' => trim($body)]);
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
PushSubscriber::renew($owner['uid'], $nickname, $subscribe, $hub_callback, $hub_topic, $hub_secret);
|
||||
|
||||
throw new HTTPException\AcceptedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace Friendica\Module\OStatus;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session\Capability\IHandleUserSessions;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Navigation\SystemMessages;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Repair extends \Friendica\BaseModule
|
||||
{
|
||||
/** @var IHandleUserSessions */
|
||||
private $session;
|
||||
/** @var SystemMessages */
|
||||
private $systemMessages;
|
||||
/** @var Database */
|
||||
private $database;
|
||||
/** @var App\Page */
|
||||
private $page;
|
||||
|
||||
public function __construct(App\Page $page, Database $database, SystemMessages $systemMessages, IHandleUserSessions $session, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->session = $session;
|
||||
$this->systemMessages = $systemMessages;
|
||||
$this->database = $database;
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
protected function content(array $request = []): string
|
||||
{
|
||||
if (!$this->session->getLocalUserId()) {
|
||||
$this->systemMessages->addNotice($this->t('Permission denied.'));
|
||||
$this->baseUrl->redirect('login');
|
||||
}
|
||||
|
||||
$uid = $this->session->getLocalUserId();
|
||||
|
||||
$counter = intval($request['counter'] ?? 0);
|
||||
|
||||
$condition = ['uid' => $uid, 'network' => Protocol::OSTATUS, 'rel' => [Contact::FRIEND, Contact::SHARING]];
|
||||
$total = $this->database->count('contact', $condition);
|
||||
if ($total) {
|
||||
$contacts = Contact::selectToArray(['url'], $condition, ['order' => ['url'], 'limit' => [$counter++, 1]]);
|
||||
if ($contacts) {
|
||||
Contact::createFromProbeForUser($this->session->getLocalUserId(), $contacts[0]['url']);
|
||||
|
||||
$this->page['htmlhead'] .= '<meta http-equiv="refresh" content="5; url=ostatus/repair?counter=' . $counter . '">';
|
||||
}
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('ostatus/repair.tpl');
|
||||
|
||||
return Renderer::replaceMacros($tpl, [
|
||||
'$l10n' => [
|
||||
'title' => $this->t('Resubscribing to OStatus contacts'),
|
||||
'keep' => $this->t('Keep this window open until done.'),
|
||||
'done' => $this->t('✔ Done'),
|
||||
'nocontacts' => $this->t('No OStatus contacts to resubscribe to.'),
|
||||
],
|
||||
'$total' => $total,
|
||||
'$counter' => $counter,
|
||||
'$contact' => $contacts[0] ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace Friendica\Module\OStatus;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Model\GServer;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Protocol\ActivityNamespace;
|
||||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Util\Crypto;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Salmon as SalmonProtocol;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Technical endpoint for the Salmon protocol
|
||||
*/
|
||||
class Salmon extends \Friendica\BaseModule
|
||||
{
|
||||
/** @var Database */
|
||||
private $database;
|
||||
|
||||
public function __construct(Database $database, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $request
|
||||
* @return void
|
||||
* @throws HTTPException\AcceptedException
|
||||
* @throws HTTPException\BadRequestException
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws HTTPException\OKException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
protected function post(array $request = [])
|
||||
{
|
||||
$xml = Network::postdata();
|
||||
$this->logger->debug('Got request data.', ['request' => $request]);
|
||||
|
||||
$nickname = $this->parameters['nickname'] ?? '';
|
||||
if (empty($nickname)) {
|
||||
throw new HTTPException\BadRequestException('nickname parameter is mandatory');
|
||||
}
|
||||
|
||||
$this->logger->debug('New Salmon', ['nickname' => $nickname, 'xml' => $xml]);
|
||||
|
||||
$importer = $this->database->selectFirst('user', [], ['nickname' => $nickname, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]);
|
||||
if (!$this->database->isResult($importer)) {
|
||||
throw new HTTPException\InternalServerErrorException();
|
||||
}
|
||||
|
||||
// parse the xml
|
||||
$dom = simplexml_load_string($xml, 'SimpleXMLElement', 0, ActivityNamespace::SALMON_ME);
|
||||
|
||||
$base = null;
|
||||
|
||||
// figure out where in the DOM tree our data is hiding
|
||||
if (!empty($dom->provenance->data)) {
|
||||
$base = $dom->provenance;
|
||||
} elseif (!empty($dom->env->data)) {
|
||||
$base = $dom->env;
|
||||
} elseif (!empty($dom->data)) {
|
||||
$base = $dom;
|
||||
}
|
||||
|
||||
if (empty($base)) {
|
||||
$this->logger->notice('unable to locate salmon data in xml');
|
||||
throw new HTTPException\BadRequestException();
|
||||
}
|
||||
|
||||
// Stash the signature away for now. We have to find their key or it won't be good for anything.
|
||||
$signature = Strings::base64UrlDecode($base->sig);
|
||||
|
||||
// unpack the data
|
||||
|
||||
// strip whitespace so our data element will return to one big base64 blob
|
||||
$data = str_replace([" ", "\t", "\r", "\n"], ["", "", "", ""], $base->data);
|
||||
|
||||
// stash away some other stuff for later
|
||||
|
||||
$type = $base->data[0]->attributes()->type[0];
|
||||
$keyhash = $base->sig[0]->attributes()->keyhash[0] ?? '';
|
||||
$encoding = $base->encoding;
|
||||
$alg = $base->alg;
|
||||
|
||||
// Salmon magic signatures have evolved and there is no way of knowing ahead of time which
|
||||
// flavour we have. We'll try and verify it regardless.
|
||||
|
||||
$stnet_signed_data = $data;
|
||||
|
||||
$signed_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg);
|
||||
|
||||
$compliant_format = str_replace('=', '', $signed_data);
|
||||
|
||||
|
||||
// decode the data
|
||||
$data = Strings::base64UrlDecode($data);
|
||||
|
||||
$author = OStatus::salmonAuthor($data, $importer);
|
||||
$author_link = $author["author-link"];
|
||||
if (!$author_link) {
|
||||
$this->logger->notice('Could not retrieve author URI.');
|
||||
throw new HTTPException\BadRequestException();
|
||||
}
|
||||
|
||||
// Once we have the author URI, go to the web and try to find their public key
|
||||
|
||||
$this->logger->notice('Fetching key for ' . $author_link);
|
||||
|
||||
$key = SalmonProtocol::getKey($author_link, $keyhash);
|
||||
|
||||
if (!$key) {
|
||||
$this->logger->notice('Could not retrieve author key.');
|
||||
throw new HTTPException\BadRequestException();
|
||||
}
|
||||
|
||||
$this->logger->info('Key details', ['info' => $key]);
|
||||
|
||||
$pubkey = SalmonProtocol::magicKeyToPem($key);
|
||||
|
||||
// We should have everything we need now. Let's see if it verifies.
|
||||
|
||||
// Try GNU Social format
|
||||
$verify = Crypto::rsaVerify($signed_data, $signature, $pubkey);
|
||||
$mode = 1;
|
||||
|
||||
if (!$verify) {
|
||||
$this->logger->notice('Message did not verify using protocol. Trying compliant format.');
|
||||
$verify = Crypto::rsaVerify($compliant_format, $signature, $pubkey);
|
||||
$mode = 2;
|
||||
}
|
||||
|
||||
if (!$verify) {
|
||||
$this->logger->notice('Message did not verify using padding. Trying old statusnet format.');
|
||||
$verify = Crypto::rsaVerify($stnet_signed_data, $signature, $pubkey);
|
||||
$mode = 3;
|
||||
}
|
||||
|
||||
if (!$verify) {
|
||||
$this->logger->notice('Message did not verify. Discarding.');
|
||||
throw new HTTPException\BadRequestException();
|
||||
}
|
||||
|
||||
$this->logger->notice('Message verified with mode ' . $mode);
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
|
||||
*
|
||||
*/
|
||||
|
||||
$contact = $this->database->selectFirst(
|
||||
'contact',
|
||||
[],
|
||||
[
|
||||
"`network` IN (?, ?)
|
||||
AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)
|
||||
AND `uid` = ?",
|
||||
Protocol::OSTATUS, Protocol::DFRN,
|
||||
Strings::normaliseLink($author_link), $author_link, Strings::normaliseLink($author_link),
|
||||
$importer['uid']
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($contact['gsid'])) {
|
||||
GServer::setProtocol($contact['gsid'], Post\DeliveryData::OSTATUS);
|
||||
}
|
||||
|
||||
// Have we ignored the person?
|
||||
// If so we can not accept this post.
|
||||
|
||||
if (!empty($contact['blocked'])) {
|
||||
$this->logger->notice('Ignoring this author.');
|
||||
throw new HTTPException\AcceptedException();
|
||||
}
|
||||
|
||||
// Placeholder for hub discovery.
|
||||
$hub = '';
|
||||
|
||||
$contact = $contact ?: [];
|
||||
|
||||
Item::incrementOutbound(Protocol::OSTATUS);
|
||||
OStatus::import($data, $importer, $contact, $hub);
|
||||
|
||||
throw new HTTPException\OKException();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue