mirror of
https://github.com/friendica/friendica
synced 2025-04-23 08:30:10 +00:00
Legacy DFRN transport layer is removed
This commit is contained in:
parent
fd37a57678
commit
3a5523820c
27 changed files with 199 additions and 3175 deletions
|
@ -23,9 +23,7 @@ namespace Friendica\Protocol;
|
|||
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use Friendica\App\BaseURL;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\DBA;
|
||||
|
@ -39,13 +37,10 @@ use Friendica\Model\Item;
|
|||
use Friendica\Model\ItemURI;
|
||||
use Friendica\Model\Mail;
|
||||
use Friendica\Model\Notification;
|
||||
use Friendica\Model\PermissionSet;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Post\Category;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Util\Crypto;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
|
@ -145,172 +140,6 @@ class DFRN
|
|||
return trim($doc->saveXML());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an atom feed for the given user
|
||||
*
|
||||
* This function is called when another server is pulling data from the user feed.
|
||||
*
|
||||
* @param string $dfrn_id DFRN ID from the requesting party
|
||||
* @param string $owner_nick Owner nick name
|
||||
* @param string $last_update Date of the last update
|
||||
* @param int $direction Can be -1, 0 or 1.
|
||||
* @param boolean $onlyheader Output only the header without content? (Default is "no")
|
||||
*
|
||||
* @return string DFRN feed entries
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function feed($dfrn_id, $owner_nick, $last_update, $direction = 0, $onlyheader = false)
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
$sitefeed = ((strlen($owner_nick)) ? false : true); // not yet implemented, need to rewrite huge chunks of following logic
|
||||
$public_feed = (($dfrn_id) ? false : true);
|
||||
$starred = false; // not yet implemented, possible security issues
|
||||
$converse = false;
|
||||
|
||||
if ($public_feed && $a->argc > 2) {
|
||||
for ($x = 2; $x < $a->argc; $x++) {
|
||||
if ($a->argv[$x] == 'converse') {
|
||||
$converse = true;
|
||||
}
|
||||
if ($a->argv[$x] == 'starred') {
|
||||
$starred = true;
|
||||
}
|
||||
if ($a->argv[$x] == 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) {
|
||||
$category = $a->argv[$x+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default permissions - anonymous user
|
||||
|
||||
$sql_extra = sprintf(" AND `private` != %s ", Item::PRIVATE);
|
||||
|
||||
$owner = DBA::selectFirst('owner-view', [], ['nickname' => $owner_nick]);
|
||||
if (!DBA::isResult($owner)) {
|
||||
Logger::log(sprintf('No contact found for nickname=%d', $owner_nick), Logger::WARNING);
|
||||
exit();
|
||||
}
|
||||
|
||||
$owner_id = $owner['uid'];
|
||||
|
||||
if (!$public_feed) {
|
||||
switch ($direction) {
|
||||
case (-1):
|
||||
$sql_extra = sprintf(" AND `issued-id` = '%s' ", DBA::escape($dfrn_id));
|
||||
break;
|
||||
case 0:
|
||||
$sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
|
||||
break;
|
||||
case 1:
|
||||
$sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break; // NOTREACHED
|
||||
}
|
||||
|
||||
$contact = DBA::selectFirst('contact', [], ["NOT `blocked` AND `contact`.`uid` = ?" . $sql_extra, $owner_id]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
Logger::notice('No contact found', ['uid' => $owner_id]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$set = PermissionSet::get($owner_id, $contact['id']);
|
||||
|
||||
if (!empty($set)) {
|
||||
$sql_extra = " AND `psid` IN (" . implode(',', $set) .")";
|
||||
} else {
|
||||
$sql_extra = sprintf(" AND `private` != %s", Item::PRIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!strlen($last_update)) {
|
||||
$last_update = 'now -30 days';
|
||||
}
|
||||
|
||||
if (isset($category)) {
|
||||
$sql_extra .= sprintf(" AND `uri-id` IN (SELECT `uri-id` FROM `category-view` WHERE `name` = '%s' AND `type` = %d AND `uid` = %d)",
|
||||
DBA::escape(Strings::protectSprintf($category)), intval(Category::CATEGORY), intval($owner_id));
|
||||
}
|
||||
|
||||
if ($public_feed && ! $converse) {
|
||||
$sql_extra .= " AND `self` ";
|
||||
}
|
||||
|
||||
$check_date = DateTimeFormat::utc($last_update);
|
||||
|
||||
$condition = ["`uid` = ? AND `wall` AND `changed` > ? AND `vid` != ? AND `visible`" . $sql_extra,
|
||||
$owner_id, $check_date, Verb::getID(Activity::ANNOUNCE)];
|
||||
|
||||
$params = ['sort' => ['parent' => $public_feed, 'received']];
|
||||
$items = Post::selectToArray(Item::DELIVER_FIELDLIST, $condition, $params, ['limit' => 300]);
|
||||
|
||||
/*
|
||||
* Will check further below if this actually returned results.
|
||||
* We will provide an empty feed if that is the case.
|
||||
*/
|
||||
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$alternatelink = $owner['url'];
|
||||
|
||||
if (isset($category)) {
|
||||
$alternatelink .= "/category/".$category;
|
||||
}
|
||||
|
||||
if ($public_feed) {
|
||||
$author = "dfrn:owner";
|
||||
} else {
|
||||
$author = "author";
|
||||
}
|
||||
|
||||
$root = self::addHeader($doc, $owner, $author, $alternatelink, true);
|
||||
|
||||
/// @TODO This hook can't work anymore
|
||||
// \Friendica\Core\Hook::callAll('atom_feed', $atom);
|
||||
|
||||
if (!DBA::isResult($items) || $onlyheader) {
|
||||
$atom = trim($doc->saveXML());
|
||||
|
||||
Hook::callAll('atom_feed_end', $atom);
|
||||
|
||||
return $atom;
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
// prevent private email from leaking.
|
||||
if ($item['network'] == Protocol::MAIL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// public feeds get html, our own nodes use bbcode
|
||||
|
||||
if ($public_feed) {
|
||||
$type = 'html';
|
||||
// catch any email that's in a public conversation and make sure it doesn't leak
|
||||
if ($item['private'] == Item::PRIVATE) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
$type = 'text';
|
||||
}
|
||||
|
||||
$entry = self::entry($doc, $type, $item, $owner, true);
|
||||
if (isset($entry)) {
|
||||
$root->appendChild($entry);
|
||||
}
|
||||
}
|
||||
|
||||
$atom = trim($doc->saveXML());
|
||||
|
||||
Hook::callAll('atom_feed_end', $atom);
|
||||
|
||||
return $atom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an atom entry for a given uri id and user
|
||||
*
|
||||
|
@ -1089,268 +918,6 @@ class DFRN
|
|||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypts data via AES
|
||||
*
|
||||
* @param string $data The data that is to be encrypted
|
||||
* @param string $key The AES key
|
||||
*
|
||||
* @return string encrypted data
|
||||
*/
|
||||
private static function aesEncrypt($data, $key)
|
||||
{
|
||||
return openssl_encrypt($data, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypts data via AES
|
||||
*
|
||||
* @param string $encrypted The encrypted data
|
||||
* @param string $key The AES key
|
||||
*
|
||||
* @return string decrypted data
|
||||
*/
|
||||
public static function aesDecrypt($encrypted, $key)
|
||||
{
|
||||
return openssl_decrypt($encrypted, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers the atom content to the contacts
|
||||
*
|
||||
* @param array $owner Owner record
|
||||
* @param array $contact Contact record of the receiver
|
||||
* @param string $atom Content that will be transmitted
|
||||
* @param bool $dissolve (to be documented)
|
||||
*
|
||||
* @return int Deliver status. Negative values mean an error.
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
* @todo Add array type-hint for $owner, $contact
|
||||
*/
|
||||
public static function deliver($owner, $contact, $atom, $dissolve = false)
|
||||
{
|
||||
$idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
|
||||
|
||||
if ($contact['duplex'] && $contact['dfrn-id']) {
|
||||
$idtosend = '0:' . $orig_id;
|
||||
}
|
||||
if ($contact['duplex'] && $contact['issued-id']) {
|
||||
$idtosend = '1:' . $orig_id;
|
||||
}
|
||||
|
||||
$rino = DI::config()->get('system', 'rino_encrypt');
|
||||
$rino = intval($rino);
|
||||
|
||||
Logger::log("Local rino version: ". $rino, Logger::DEBUG);
|
||||
|
||||
$ssl_val = intval(DI::config()->get('system', 'ssl_policy'));
|
||||
|
||||
switch ($ssl_val) {
|
||||
case BaseURL::SSL_POLICY_FULL:
|
||||
$ssl_policy = 'full';
|
||||
break;
|
||||
case BaseURL::SSL_POLICY_SELFSIGN:
|
||||
$ssl_policy = 'self';
|
||||
break;
|
||||
case BaseURL::SSL_POLICY_NONE:
|
||||
default:
|
||||
$ssl_policy = 'none';
|
||||
break;
|
||||
}
|
||||
|
||||
$url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino='.$rino : '');
|
||||
|
||||
Logger::log('dfrn_deliver: ' . $url);
|
||||
|
||||
$curlResult = DI::httpRequest()->get($url);
|
||||
|
||||
if ($curlResult->isTimeout()) {
|
||||
return -2; // timed out
|
||||
}
|
||||
|
||||
$xml = $curlResult->getBody();
|
||||
|
||||
$curl_stat = $curlResult->getReturnCode();
|
||||
if (empty($curl_stat)) {
|
||||
return -3; // timed out
|
||||
}
|
||||
|
||||
Logger::log('dfrn_deliver: ' . $xml, Logger::DATA);
|
||||
|
||||
if (empty($xml)) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (strpos($xml, '<?xml') === false) {
|
||||
Logger::log('dfrn_deliver: no valid XML returned');
|
||||
Logger::log('dfrn_deliver: returned XML: ' . $xml, Logger::DATA);
|
||||
return 3;
|
||||
}
|
||||
|
||||
$res = XML::parseString($xml);
|
||||
|
||||
if (!is_object($res) || (intval($res->status) != 0) || !strlen($res->challenge) || !strlen($res->dfrn_id)) {
|
||||
if (empty($res->status)) {
|
||||
$status = 3;
|
||||
} else {
|
||||
$status = $res->status;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
$postvars = [];
|
||||
$sent_dfrn_id = hex2bin((string) $res->dfrn_id);
|
||||
$challenge = hex2bin((string) $res->challenge);
|
||||
$perm = (($res->perm) ? $res->perm : null);
|
||||
$dfrn_version = floatval($res->dfrn_version ?: 2.0);
|
||||
$rino_remote_version = intval($res->rino);
|
||||
$page = (($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY) ? 1 : 0);
|
||||
|
||||
Logger::log("Remote rino version: ".$rino_remote_version." for ".$contact["url"], Logger::DEBUG);
|
||||
|
||||
if ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) {
|
||||
$page = 2;
|
||||
}
|
||||
|
||||
$final_dfrn_id = '';
|
||||
|
||||
if ($perm) {
|
||||
if ((($perm == 'rw') && !intval($contact['writable']))
|
||||
|| (($perm == 'r') && intval($contact['writable']))
|
||||
) {
|
||||
DBA::update('contact', ['writable' => ($perm == 'rw')], ['id' => $contact['id']]);
|
||||
|
||||
$contact['writable'] = (string) 1 - intval($contact['writable']);
|
||||
}
|
||||
}
|
||||
|
||||
if (($contact['duplex'] && strlen($contact['pubkey']))
|
||||
|| ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY && strlen($contact['pubkey']))
|
||||
|| ($contact['rel'] == Contact::SHARING && strlen($contact['pubkey']))
|
||||
) {
|
||||
openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']);
|
||||
openssl_public_decrypt($challenge, $postvars['challenge'], $contact['pubkey']);
|
||||
} else {
|
||||
openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']);
|
||||
openssl_private_decrypt($challenge, $postvars['challenge'], $contact['prvkey']);
|
||||
}
|
||||
|
||||
$final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
|
||||
|
||||
if (strpos($final_dfrn_id, ':') == 1) {
|
||||
$final_dfrn_id = substr($final_dfrn_id, 2);
|
||||
}
|
||||
|
||||
if ($final_dfrn_id != $orig_id) {
|
||||
Logger::log('dfrn_deliver: wrong dfrn_id.');
|
||||
// did not decode properly - cannot trust this site
|
||||
return 3;
|
||||
}
|
||||
|
||||
$postvars['dfrn_id'] = $idtosend;
|
||||
$postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
|
||||
if ($dissolve) {
|
||||
$postvars['dissolve'] = '1';
|
||||
}
|
||||
|
||||
if ((($contact['rel']) && ($contact['rel'] != Contact::SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) {
|
||||
$postvars['data'] = $atom;
|
||||
$postvars['perm'] = 'rw';
|
||||
} else {
|
||||
$postvars['data'] = str_replace('<dfrn:comment-allow>1', '<dfrn:comment-allow>0', $atom);
|
||||
$postvars['perm'] = 'r';
|
||||
}
|
||||
|
||||
$postvars['ssl_policy'] = $ssl_policy;
|
||||
|
||||
if ($page) {
|
||||
$postvars['page'] = $page;
|
||||
}
|
||||
|
||||
|
||||
if ($rino > 0 && $rino_remote_version > 0 && (! $dissolve)) {
|
||||
Logger::log('rino version: '. $rino_remote_version);
|
||||
|
||||
switch ($rino_remote_version) {
|
||||
case 1:
|
||||
$key = random_bytes(16);
|
||||
$data = self::aesEncrypt($postvars['data'], $key);
|
||||
break;
|
||||
|
||||
default:
|
||||
Logger::log("rino: invalid requested version '$rino_remote_version'");
|
||||
return -8;
|
||||
}
|
||||
|
||||
$postvars['rino'] = $rino_remote_version;
|
||||
$postvars['data'] = bin2hex($data);
|
||||
|
||||
if ($dfrn_version >= 2.1) {
|
||||
if (($contact['duplex'] && strlen($contact['pubkey']))
|
||||
|| ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY && strlen($contact['pubkey']))
|
||||
|| ($contact['rel'] == Contact::SHARING && strlen($contact['pubkey']))
|
||||
) {
|
||||
openssl_public_encrypt($key, $postvars['key'], $contact['pubkey']);
|
||||
} else {
|
||||
openssl_private_encrypt($key, $postvars['key'], $contact['prvkey']);
|
||||
}
|
||||
} else {
|
||||
if (($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) {
|
||||
openssl_private_encrypt($key, $postvars['key'], $contact['prvkey']);
|
||||
} else {
|
||||
openssl_public_encrypt($key, $postvars['key'], $contact['pubkey']);
|
||||
}
|
||||
}
|
||||
|
||||
Logger::log('md5 rawkey ' . md5($postvars['key']));
|
||||
|
||||
$postvars['key'] = bin2hex($postvars['key']);
|
||||
}
|
||||
|
||||
|
||||
Logger::debug('dfrn_deliver', ['post' => $postvars]);
|
||||
|
||||
$postResult = DI::httpRequest()->post($contact['notify'], $postvars);
|
||||
|
||||
$xml = $postResult->getBody();
|
||||
|
||||
Logger::log('dfrn_deliver: ' . "RECEIVED: " . $xml, Logger::DATA);
|
||||
|
||||
$curl_stat = $postResult->getReturnCode();
|
||||
if (empty($curl_stat) || empty($xml)) {
|
||||
return -9; // timed out
|
||||
}
|
||||
|
||||
if (($curl_stat == 503) && stristr($postResult->getHeader(), 'retry-after')) {
|
||||
return -10;
|
||||
}
|
||||
|
||||
if (strpos($xml, '<?xml') === false) {
|
||||
Logger::log('dfrn_deliver: phase 2: no valid XML returned');
|
||||
Logger::log('dfrn_deliver: phase 2: returned XML: ' . $xml, Logger::DATA);
|
||||
return 3;
|
||||
}
|
||||
|
||||
$res = XML::parseString($xml);
|
||||
|
||||
if (!isset($res->status)) {
|
||||
return -11;
|
||||
}
|
||||
|
||||
// Possibly old servers had returned an empty value when everything was okay
|
||||
if (empty($res->status)) {
|
||||
$res->status = 200;
|
||||
}
|
||||
|
||||
if (!empty($res->message)) {
|
||||
Logger::log('Delivery returned status '.$res->status.' - '.$res->message, Logger::DEBUG);
|
||||
}
|
||||
|
||||
return intval($res->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmits atom content to the contacts via the Diaspora transport layer
|
||||
*
|
||||
|
@ -2618,12 +2185,8 @@ class DFRN
|
|||
|
||||
Logger::log("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"], Logger::DEBUG);
|
||||
|
||||
if (!empty($importer['gsid'])) {
|
||||
if ($protocol == Conversation::PARCEL_DIASPORA_DFRN) {
|
||||
GServer::setProtocol($importer['gsid'], Post\DeliveryData::DFRN);
|
||||
} elseif ($protocol == Conversation::PARCEL_LEGACY_DFRN) {
|
||||
GServer::setProtocol($importer['gsid'], Post\DeliveryData::LEGACY_DFRN);
|
||||
}
|
||||
if (!empty($importer['gsid']) && ($protocol == Conversation::PARCEL_DIASPORA_DFRN)) {
|
||||
GServer::setProtocol($importer['gsid'], Post\DeliveryData::DFRN);
|
||||
}
|
||||
|
||||
// is it a public forum? Private forums aren't exposed with this method
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue