Table "apcontact" is now in use / added functionality to handle JSON-LD
This commit is contained in:
parent
5de4afecf1
commit
f20bed67a9
2 changed files with 128 additions and 52 deletions
|
@ -336,7 +336,7 @@ class Probe
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array(defaults($data, 'network', ''), ['', Protocol::PHANTOM])) {
|
if (in_array(defaults($data, 'network', ''), ['', Protocol::PHANTOM])) {
|
||||||
$ap_profile = ActivityPub::fetchProfile($uri);
|
$ap_profile = ActivityPub::probeProfile($uri);
|
||||||
if (!empty($ap_profile) && ($ap_profile['network'] == Protocol::ACTIVITYPUB)) {
|
if (!empty($ap_profile) && ($ap_profile['network'] == Protocol::ACTIVITYPUB)) {
|
||||||
$data = $ap_profile;
|
$data = $ap_profile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ use Friendica\Util\DateTimeFormat;
|
||||||
use Friendica\Util\Crypto;
|
use Friendica\Util\Crypto;
|
||||||
use Friendica\Content\Text\BBCode;
|
use Friendica\Content\Text\BBCode;
|
||||||
use Friendica\Content\Text\HTML;
|
use Friendica\Content\Text\HTML;
|
||||||
use Friendica\Network\Probe;
|
use Friendica\Core\Cache;
|
||||||
|
use digitalbazaar\jsonld;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ActivityPub Protocol class
|
* @brief ActivityPub Protocol class
|
||||||
|
@ -57,6 +58,51 @@ class ActivityPub
|
||||||
{
|
{
|
||||||
const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public';
|
const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public';
|
||||||
|
|
||||||
|
public static function jsonld_document_loader($url)
|
||||||
|
{
|
||||||
|
$recursion = 0;
|
||||||
|
|
||||||
|
$x = debug_backtrace();
|
||||||
|
if ($x) {
|
||||||
|
foreach ($x as $n) {
|
||||||
|
if ($n['function'] === __FUNCTION__) {
|
||||||
|
$recursion ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recursion > 5) {
|
||||||
|
logger('jsonld bomb detected at: ' . $url);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Cache::get('jsonld_document_loader:' . $url);
|
||||||
|
if (!is_null($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = jsonld_default_document_loader($url);
|
||||||
|
Cache::set('jsonld_document_loader:' . $url, $data, CACHE_DAY);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function compactJsonLD($json)
|
||||||
|
{
|
||||||
|
jsonld_set_document_loader('Friendica\Protocol\ActivityPub::jsonld_document_loader');
|
||||||
|
|
||||||
|
$context = (object)['as' => 'https://www.w3.org/ns/activitystreams',
|
||||||
|
'w3sec' => 'https://w3id.org/security',
|
||||||
|
'ostatus' => (object)['@id' => 'http://ostatus.org#', '@type' => '@id'],
|
||||||
|
'vcard' => (object)['@id' => 'http://www.w3.org/2006/vcard/ns#', '@type' => '@id'],
|
||||||
|
'uuid' => (object)['@id' => 'http://schema.org/identifier', '@type' => '@id']];
|
||||||
|
|
||||||
|
$jsonobj = json_decode(json_encode($json));
|
||||||
|
|
||||||
|
$compacted = jsonld_compact($jsonobj, $context);
|
||||||
|
|
||||||
|
return json_decode(json_encode($compacted), true);
|
||||||
|
}
|
||||||
|
|
||||||
public static function isRequest()
|
public static function isRequest()
|
||||||
{
|
{
|
||||||
return stristr(defaults($_SERVER, 'HTTP_ACCEPT', ''), 'application/activity+json') ||
|
return stristr(defaults($_SERVER, 'HTTP_ACCEPT', ''), 'application/activity+json') ||
|
||||||
|
@ -167,8 +213,8 @@ class ActivityPub
|
||||||
if ($term['type'] != TERM_MENTION) {
|
if ($term['type'] != TERM_MENTION) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$profile = Probe::uri($term['url'], Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($term['url']);
|
||||||
if ($profile['network'] == Protocol::ACTIVITYPUB) {
|
if (!empty($profile)) {
|
||||||
$data['cc'][] = $profile['url'];
|
$data['cc'][] = $profile['url'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,9 +267,9 @@ class ActivityPub
|
||||||
if ($term['type'] != TERM_MENTION) {
|
if ($term['type'] != TERM_MENTION) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$profile = Probe::uri($term['url'], Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($term['url']);
|
||||||
if ($profile['network'] == Protocol::ACTIVITYPUB) {
|
if (!empty($profile)) {
|
||||||
$target = defaults($profile, 'batch', $profile['notify']);
|
$target = defaults($profile, 'sharedinbox', $profile['inbox']);
|
||||||
$inboxes[$target] = $target;
|
$inboxes[$target] = $target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,9 +285,9 @@ class ActivityPub
|
||||||
$cid = Contact::getIdForURL($term['url'], $item['uid']);
|
$cid = Contact::getIdForURL($term['url'], $item['uid']);
|
||||||
if (!empty($cid) && in_array($cid, $receiver_list)) {
|
if (!empty($cid) && in_array($cid, $receiver_list)) {
|
||||||
$contact = DBA::selectFirst('contact', ['url'], ['id' => $cid, 'network' => Protocol::ACTIVITYPUB]);
|
$contact = DBA::selectFirst('contact', ['url'], ['id' => $cid, 'network' => Protocol::ACTIVITYPUB]);
|
||||||
$profile = Probe::uri($contact['url'], Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($contact['url']);
|
||||||
if ($profile['network'] == Protocol::ACTIVITYPUB) {
|
if (!empty($profile['network'])) {
|
||||||
$target = defaults($profile, 'batch', $profile['notify']);
|
$target = defaults($profile, 'sharedinbox', $profile['inbox']);
|
||||||
$inboxes[$target] = $target;
|
$inboxes[$target] = $target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,21 +295,21 @@ class ActivityPub
|
||||||
|
|
||||||
foreach ($receiver_list as $receiver) {
|
foreach ($receiver_list as $receiver) {
|
||||||
$contact = DBA::selectFirst('contact', ['url'], ['id' => $receiver, 'network' => Protocol::ACTIVITYPUB]);
|
$contact = DBA::selectFirst('contact', ['url'], ['id' => $receiver, 'network' => Protocol::ACTIVITYPUB]);
|
||||||
$profile = Probe::uri($contact['url'], Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($contact['url']);
|
||||||
if ($profile['network'] == Protocol::ACTIVITYPUB) {
|
if (!empty($profile['network'])) {
|
||||||
$target = defaults($profile, 'batch', $profile['notify']);
|
$target = defaults($profile, 'sharedinbox', $profile['inbox']);
|
||||||
$inboxes[$target] = $target;
|
$inboxes[$target] = $target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$profile = Probe::uri($target, Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($item['author-link']);
|
||||||
if (!empty($profile['batch'])) {
|
if (!empty($profile['sharedinbox'])) {
|
||||||
unset($inboxes[$profile['batch']]);
|
unset($inboxes[$profile['sharedinbox']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($profile['notify'])) {
|
if (!empty($profile['inbox'])) {
|
||||||
unset($inboxes[$profile['notify']]);
|
unset($inboxes[$profile['inbox']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $inboxes;
|
return $inboxes;
|
||||||
|
@ -389,7 +435,7 @@ class ActivityPub
|
||||||
|
|
||||||
public static function transmitActivity($activity, $target, $uid)
|
public static function transmitActivity($activity, $target, $uid)
|
||||||
{
|
{
|
||||||
$profile = Probe::uri($target, Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($target);
|
||||||
|
|
||||||
$owner = User::getOwnerDataById($uid);
|
$owner = User::getOwnerDataById($uid);
|
||||||
|
|
||||||
|
@ -401,12 +447,12 @@ class ActivityPub
|
||||||
'to' => $profile['url']];
|
'to' => $profile['url']];
|
||||||
|
|
||||||
logger('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid, LOGGER_DEBUG);
|
logger('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid, LOGGER_DEBUG);
|
||||||
return self::transmit($data, $profile['notify'], $uid);
|
return self::transmit($data, $profile['inbox'], $uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function transmitContactAccept($target, $id, $uid)
|
public static function transmitContactAccept($target, $id, $uid)
|
||||||
{
|
{
|
||||||
$profile = Probe::uri($target, Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($target);
|
||||||
|
|
||||||
$owner = User::getOwnerDataById($uid);
|
$owner = User::getOwnerDataById($uid);
|
||||||
$data = ['@context' => 'https://www.w3.org/ns/activitystreams',
|
$data = ['@context' => 'https://www.w3.org/ns/activitystreams',
|
||||||
|
@ -419,12 +465,12 @@ class ActivityPub
|
||||||
'to' => $profile['url']];
|
'to' => $profile['url']];
|
||||||
|
|
||||||
logger('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
|
logger('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
|
||||||
return self::transmit($data, $profile['notify'], $uid);
|
return self::transmit($data, $profile['inbox'], $uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function transmitContactReject($target, $id, $uid)
|
public static function transmitContactReject($target, $id, $uid)
|
||||||
{
|
{
|
||||||
$profile = Probe::uri($target, Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($target);
|
||||||
|
|
||||||
$owner = User::getOwnerDataById($uid);
|
$owner = User::getOwnerDataById($uid);
|
||||||
$data = ['@context' => 'https://www.w3.org/ns/activitystreams',
|
$data = ['@context' => 'https://www.w3.org/ns/activitystreams',
|
||||||
|
@ -437,12 +483,12 @@ class ActivityPub
|
||||||
'to' => $profile['url']];
|
'to' => $profile['url']];
|
||||||
|
|
||||||
logger('Sending reject to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
|
logger('Sending reject to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
|
||||||
return self::transmit($data, $profile['notify'], $uid);
|
return self::transmit($data, $profile['inbox'], $uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function transmitContactUndo($target, $uid)
|
public static function transmitContactUndo($target, $uid)
|
||||||
{
|
{
|
||||||
$profile = Probe::uri($target, Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($target);
|
||||||
|
|
||||||
$id = System::baseUrl() . '/activity/' . System::createGUID();
|
$id = System::baseUrl() . '/activity/' . System::createGUID();
|
||||||
|
|
||||||
|
@ -457,7 +503,7 @@ class ActivityPub
|
||||||
'to' => $profile['url']];
|
'to' => $profile['url']];
|
||||||
|
|
||||||
logger('Sending undo to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
|
logger('Sending undo to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
|
||||||
return self::transmit($data, $profile['notify'], $uid);
|
return self::transmit($data, $profile['inbox'], $uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -616,11 +662,11 @@ class ActivityPub
|
||||||
{
|
{
|
||||||
$url = (strpos($id, '#') ? substr($id, 0, strpos($id, '#')) : $id);
|
$url = (strpos($id, '#') ? substr($id, 0, strpos($id, '#')) : $id);
|
||||||
|
|
||||||
$profile = Probe::uri($url, Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($url);
|
||||||
if (!empty($profile)) {
|
if (!empty($profile)) {
|
||||||
return $profile['pubkey'];
|
return $profile['pubkey'];
|
||||||
} elseif ($url != $actor) {
|
} elseif ($url != $actor) {
|
||||||
$profile = Probe::uri($actor, Protocol::ACTIVITYPUB);
|
$profile = self::fetchprofile($actor);
|
||||||
if (!empty($profile)) {
|
if (!empty($profile)) {
|
||||||
return $profile['pubkey'];
|
return $profile['pubkey'];
|
||||||
}
|
}
|
||||||
|
@ -663,14 +709,29 @@ class ActivityPub
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function fetchprofile($url, $update = false)
|
||||||
* Fetches a profile from the given url
|
|
||||||
*
|
|
||||||
* @param string $url profile url
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function fetchProfile($url)
|
|
||||||
{
|
{
|
||||||
|
if (empty($url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$update) {
|
||||||
|
$apcontact = DBA::selectFirst('apcontact', [], ['url' => $url]);
|
||||||
|
if (DBA::isResult($apcontact)) {
|
||||||
|
return $apcontact;
|
||||||
|
}
|
||||||
|
|
||||||
|
$apcontact = DBA::selectFirst('apcontact', [], ['alias' => $url]);
|
||||||
|
if (DBA::isResult($apcontact)) {
|
||||||
|
return $apcontact;
|
||||||
|
}
|
||||||
|
|
||||||
|
$apcontact = DBA::selectFirst('apcontact', [], ['addr' => $url]);
|
||||||
|
if (DBA::isResult($apcontact)) {
|
||||||
|
return $apcontact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (empty(parse_url($url, PHP_URL_SCHEME))) {
|
if (empty(parse_url($url, PHP_URL_SCHEME))) {
|
||||||
$url = self::addrToUrl($url);
|
$url = self::addrToUrl($url);
|
||||||
if (empty($url)) {
|
if (empty($url)) {
|
||||||
|
@ -704,7 +765,19 @@ class ActivityPub
|
||||||
unset($parts['path']);
|
unset($parts['path']);
|
||||||
$apcontact['addr'] = $apcontact['nick'] . '@' . str_replace('//', '', Network::unparseURL($parts));
|
$apcontact['addr'] = $apcontact['nick'] . '@' . str_replace('//', '', Network::unparseURL($parts));
|
||||||
|
|
||||||
$apcontact['pubkey'] = self::processElement($data, 'publicKey', 'publicKeyPem');
|
$apcontact['pubkey'] = trim(self::processElement($data, 'publicKey', 'publicKeyPem'));
|
||||||
|
|
||||||
|
// To-Do
|
||||||
|
// manuallyApprovesFollowers
|
||||||
|
|
||||||
|
// Unhandled
|
||||||
|
// @context, tag, attachment, image, nomadicLocations, signature, following, followers, featured, movedTo, liked
|
||||||
|
|
||||||
|
// Unhandled from Misskey
|
||||||
|
// sharedInbox, isCat
|
||||||
|
|
||||||
|
// Unhandled from Kroeg
|
||||||
|
// kroeg:blocks, updated
|
||||||
|
|
||||||
// Check if the address is resolvable
|
// Check if the address is resolvable
|
||||||
if (self::addrToUrl($apcontact['addr']) == $apcontact['url']) {
|
if (self::addrToUrl($apcontact['addr']) == $apcontact['url']) {
|
||||||
|
@ -723,7 +796,22 @@ class ActivityPub
|
||||||
|
|
||||||
DBA::update('apcontact', $apcontact, ['url' => $url], true);
|
DBA::update('apcontact', $apcontact, ['url' => $url], true);
|
||||||
|
|
||||||
// Array that is compatible to Probe::uri
|
return $apcontact;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a profile from the given url into an array that is compatible to Probe::uri
|
||||||
|
*
|
||||||
|
* @param string $url profile url
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function probeProfile($url)
|
||||||
|
{
|
||||||
|
$apcontact = self::fetchprofile($url, true);
|
||||||
|
if (empty($apcontact)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$profile = ['network' => Protocol::ACTIVITYPUB];
|
$profile = ['network' => Protocol::ACTIVITYPUB];
|
||||||
$profile['nick'] = $apcontact['nick'];
|
$profile['nick'] = $apcontact['nick'];
|
||||||
$profile['name'] = $apcontact['name'];
|
$profile['name'] = $apcontact['name'];
|
||||||
|
@ -749,18 +837,6 @@ class ActivityPub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To-Do
|
|
||||||
// type, manuallyApprovesFollowers
|
|
||||||
|
|
||||||
// Unhandled
|
|
||||||
// @context, tag, attachment, image, nomadicLocations, signature, following, followers, featured, movedTo, liked
|
|
||||||
|
|
||||||
// Unhandled from Misskey
|
|
||||||
// sharedInbox, isCat
|
|
||||||
|
|
||||||
// Unhandled from Kroeg
|
|
||||||
// kroeg:blocks, updated
|
|
||||||
|
|
||||||
return $profile;
|
return $profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,8 +1029,8 @@ class ActivityPub
|
||||||
$receivers = [];
|
$receivers = [];
|
||||||
|
|
||||||
if (!empty($actor)) {
|
if (!empty($actor)) {
|
||||||
$data = self::fetchContent($actor);
|
$profile = self::fetchprofile($actor);
|
||||||
$followers = defaults($data, 'followers', '');
|
$followers = defaults($profile, 'followers', '');
|
||||||
|
|
||||||
logger('Actor: ' . $actor . ' - Followers: ' . $followers, LOGGER_DEBUG);
|
logger('Actor: ' . $actor . ' - Followers: ' . $followers, LOGGER_DEBUG);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1365,7 +1441,7 @@ class ActivityPub
|
||||||
$activity['id'] = $object['id'];
|
$activity['id'] = $object['id'];
|
||||||
$activity['to'] = defaults($object, 'to', []);
|
$activity['to'] = defaults($object, 'to', []);
|
||||||
$activity['cc'] = defaults($object, 'cc', []);
|
$activity['cc'] = defaults($object, 'cc', []);
|
||||||
$activity['actor'] = $activity['author'];
|
$activity['actor'] = $child['author'];
|
||||||
$activity['object'] = $object;
|
$activity['object'] = $object;
|
||||||
$activity['published'] = $object['published'];
|
$activity['published'] = $object['published'];
|
||||||
$activity['type'] = 'Create';
|
$activity['type'] = 'Create';
|
||||||
|
|
Loading…
Reference in a new issue