[Database version 1499] Create new paradigm classes for Diaspora Contact

- Switch table fcontact for dcontact in views
This commit is contained in:
Hypolite Petovan 2021-09-15 00:36:01 -04:00
parent ace80ca1b4
commit ce69026964
14 changed files with 734 additions and 170 deletions

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2022.12-dev (Giant Rhubarb) -- Friendica 2022.12-dev (Giant Rhubarb)
-- DB_UPDATE_VERSION 1498 -- DB_UPDATE_VERSION 1499
-- ------------------------------------------ -- ------------------------------------------
@ -2836,11 +2836,11 @@ CREATE VIEW `account-view` AS SELECT
`contact`.`blocked` AS `blocked`, `contact`.`blocked` AS `blocked`,
`contact`.`notify` AS `dfrn-notify`, `contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`, `contact`.`poll` AS `dfrn-poll`,
`fcontact`.`guid` AS `diaspora-guid`, `item-uri`.`guid` AS `diaspora-guid`,
`fcontact`.`batch` AS `diaspora-batch`, `diaspora-contact`.`batch` AS `diaspora-batch`,
`fcontact`.`notify` AS `diaspora-notify`, `diaspora-contact`.`notify` AS `diaspora-notify`,
`fcontact`.`poll` AS `diaspora-poll`, `diaspora-contact`.`poll` AS `diaspora-poll`,
`fcontact`.`alias` AS `diaspora-alias`, `diaspora-contact`.`alias` AS `diaspora-alias`,
`apcontact`.`uuid` AS `ap-uuid`, `apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`, `apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`, `apcontact`.`following` AS `ap-following`,
@ -2858,7 +2858,7 @@ CREATE VIEW `account-view` AS SELECT
FROM `contact` FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id` LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`
WHERE `contact`.`uid` = 0; WHERE `contact`.`uid` = 0;
@ -2937,14 +2937,14 @@ CREATE VIEW `account-user-view` AS SELECT
`ucontact`.`reason` AS `reason`, `ucontact`.`reason` AS `reason`,
`contact`.`notify` AS `dfrn-notify`, `contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`, `contact`.`poll` AS `dfrn-poll`,
`fcontact`.`guid` AS `diaspora-guid`, `item-uri`.`guid` AS `diaspora-guid`,
`fcontact`.`batch` AS `diaspora-batch`, `diaspora-contact`.`batch` AS `diaspora-batch`,
`fcontact`.`notify` AS `diaspora-notify`, `diaspora-contact`.`notify` AS `diaspora-notify`,
`fcontact`.`poll` AS `diaspora-poll`, `diaspora-contact`.`poll` AS `diaspora-poll`,
`fcontact`.`alias` AS `diaspora-alias`, `diaspora-contact`.`alias` AS `diaspora-alias`,
`fcontact`.`interacting_count` AS `diaspora-interacting_count`, `diaspora-contact`.`interacting_count` AS `diaspora-interacting_count`,
`fcontact`.`interacted_count` AS `diaspora-interacted_count`, `diaspora-contact`.`interacted_count` AS `diaspora-interacted_count`,
`fcontact`.`post_count` AS `diaspora-post_count`, `diaspora-contact`.`post_count` AS `diaspora-post_count`,
`apcontact`.`uuid` AS `ap-uuid`, `apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`, `apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`, `apcontact`.`following` AS `ap-following`,
@ -2963,7 +2963,7 @@ CREATE VIEW `account-user-view` AS SELECT
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr' LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`; LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`;
-- --

View file

@ -131,9 +131,9 @@ HELP;
$this->out('Updating event table fields'); $this->out('Updating event table fields');
$this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url); $this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url);
$this->out('Updating fcontact table fields'); $this->out('Updating diaspora-contact table fields');
$this->database->replaceInTableFields('fcontact', ['url', 'photo', 'request', 'batch', 'poll', 'confirm', 'alias'], $old_url, $new_url); $this->database->replaceInTableFields('diaspora-contact', ['alias', 'photo', 'photo-medium', 'photo-small', 'batch', 'notify', 'poll', 'subscribe'], $old_url, $new_url);
$this->database->replaceInTableFields('fcontact', ['addr'], $old_host, $new_host); $this->database->replaceInTableFields('diaspora-contact', ['addr'], $old_host, $new_host);
$this->out('Updating fsuggest table fields'); $this->out('Updating fsuggest table fields');
$this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url); $this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url);

View file

@ -599,6 +599,11 @@ abstract class DI
return self::$dice->create(Protocol\Activity::class); return self::$dice->create(Protocol\Activity::class);
} }
public static function dsprContact(): Protocol\Diaspora\Repository\DiasporaContact
{
return self::$dice->create(Protocol\Diaspora\Repository\DiasporaContact::class);
}
// //
// "Security" namespace instances // "Security" namespace instances
// //

View file

@ -833,7 +833,7 @@ class Database
/** /**
* Check if data exists * Check if data exists
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* *
* @return boolean Are there rows for that condition? * @return boolean Are there rows for that condition?
@ -1017,7 +1017,7 @@ class Database
/** /**
* Insert a row into a table. Field value objects will be cast as string. * Insert a row into a table. Field value objects will be cast as string.
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $param parameter array * @param array $param parameter array
* @param int $duplicate_mode What to do on a duplicated entry * @param int $duplicate_mode What to do on a duplicated entry
* *
@ -1068,7 +1068,7 @@ class Database
* Inserts a row with the provided data in the provided table. * Inserts a row with the provided data in the provided table.
* If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead. * If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead.
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $param parameter array * @param array $param parameter array
* @return boolean was the insert successful? * @return boolean was the insert successful?
* @throws \Exception * @throws \Exception
@ -1116,7 +1116,7 @@ class Database
* *
* This function can be extended in the future to accept a table array as well. * This function can be extended in the future to accept a table array as well.
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @return boolean was the lock successful? * @return boolean was the lock successful?
* @throws \Exception * @throws \Exception
*/ */
@ -1314,7 +1314,7 @@ class Database
* Only set $old_fields to a boolean value when you are sure that you will update a single row. * Only set $old_fields to a boolean value when you are sure that you will update a single row.
* When you set $old_fields to "true" then $fields must contain all relevant fields! * When you set $old_fields to "true" then $fields must contain all relevant fields!
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields contains the fields that are updated * @param array $fields contains the fields that are updated
* @param array $condition condition array with the key values * @param array $condition condition array with the key values
* @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields) * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields)
@ -1380,7 +1380,7 @@ class Database
/** /**
* Retrieve a single record from a table and returns it in an associative array * Retrieve a single record from a table and returns it in an associative array
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
@ -1406,7 +1406,7 @@ class Database
/** /**
* Select rows from a table and fills an array with the data * Select rows from a table and fills an array with the data
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
@ -1479,7 +1479,7 @@ class Database
* *
* $data = DBA::select($table, $fields, $condition, $params); * $data = DBA::select($table, $fields, $condition, $params);
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
@ -1519,7 +1519,7 @@ class Database
/** /**
* Counts the rows from a table satisfying the provided condition * Counts the rows from a table satisfying the provided condition
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
* *

View file

@ -1395,7 +1395,7 @@ class Contact
} }
if ($data['network'] == Protocol::DIASPORA) { if ($data['network'] == Protocol::DIASPORA) {
FContact::updateFromProbeArray($data); DI::dsprContact()->updateFromProbeArray($data);
} }
self::updateFromProbeArray($contact_id, $data); self::updateFromProbeArray($contact_id, $data);
@ -2486,7 +2486,7 @@ class Contact
$ret = Probe::uri($contact['url'], $network, $contact['uid']); $ret = Probe::uri($contact['url'], $network, $contact['uid']);
if ($ret['network'] == Protocol::DIASPORA) { if ($ret['network'] == Protocol::DIASPORA) {
FContact::updateFromProbeArray($ret); DI::dsprContact()->updateFromProbeArray($ret);
} }
return self::updateFromProbeArray($id, $ret); return self::updateFromProbeArray($id, $ret);

View file

@ -110,7 +110,8 @@ class Probe
*/ */
private static function rearrangeData(array $data): array private static function rearrangeData(array $data): array
{ {
$fields = ['name', 'nick', 'guid', 'url', 'addr', 'alias', 'photo', 'header', $fields = ['name', 'given_name', 'family_name', 'nick', 'guid', 'url', 'addr', 'alias',
'photo', 'photo_medium', 'photo_small', 'header',
'account-type', 'community', 'keywords', 'location', 'about', 'xmpp', 'matrix', 'account-type', 'community', 'keywords', 'location', 'about', 'xmpp', 'matrix',
'hide', 'batch', 'notify', 'poll', 'request', 'confirm', 'subscribe', 'poco', 'hide', 'batch', 'notify', 'poll', 'request', 'confirm', 'subscribe', 'poco',
'following', 'followers', 'inbox', 'outbox', 'sharedinbox', 'following', 'followers', 'inbox', 'outbox', 'sharedinbox',
@ -124,7 +125,7 @@ class Probe
if (in_array($field, $numeric_fields)) { if (in_array($field, $numeric_fields)) {
$newdata[$field] = (int)$data[$field]; $newdata[$field] = (int)$data[$field];
} else { } else {
$newdata[$field] = $data[$field]; $newdata[$field] = trim($data[$field]);
} }
} elseif (!in_array($field, $numeric_fields)) { } elseif (!in_array($field, $numeric_fields)) {
$newdata[$field] = ''; $newdata[$field] = '';
@ -1290,9 +1291,19 @@ class Probe
$data['name'] = $search->item(0)->nodeValue; $data['name'] = $search->item(0)->nodeValue;
} }
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' given_name ')]", $vcard); // */
if ($search->length > 0) {
$data["given_name"] = $search->item(0)->nodeValue;
}
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' family_name ')]", $vcard); // */
if ($search->length > 0) {
$data["family_name"] = $search->item(0)->nodeValue;
}
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */ $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */
if ($search->length > 0) { if ($search->length > 0) {
$data['searchable'] = $search->item(0)->nodeValue; $data['hide'] = (strtolower($search->item(0)->nodeValue) != 'true');
} }
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */ $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */
@ -1309,7 +1320,7 @@ class Probe
} }
} }
$avatar = []; $avatars = [];
if (!empty($vcard)) { if (!empty($vcard)) {
$photos = $xpath->query("//*[contains(concat(' ', @class, ' '), ' photo ') or contains(concat(' ', @class, ' '), ' avatar ')]", $vcard); // */ $photos = $xpath->query("//*[contains(concat(' ', @class, ' '), ' photo ') or contains(concat(' ', @class, ' '), ' avatar ')]", $vcard); // */
foreach ($photos as $photo) { foreach ($photos as $photo) {
@ -1319,20 +1330,27 @@ class Probe
} }
if (isset($attr['src']) && isset($attr['width'])) { if (isset($attr['src']) && isset($attr['width'])) {
$avatar[$attr['width']] = $attr['src']; $avatars[$attr['width']] = self::fixAvatar($attr['src'], $data['baseurl']);
} }
// We don't have a width. So we just take everything that we got. // We don't have a width. So we just take everything that we got.
// This is a Hubzilla workaround which doesn't send a width. // This is a Hubzilla workaround which doesn't send a width.
if ((sizeof($avatar) == 0) && !empty($attr['src'])) { if (!$avatars && !empty($attr['src'])) {
$avatar[] = $attr['src']; $avatars[] = self::fixAvatar($attr['src'], $data['baseurl']);
} }
} }
} }
if (sizeof($avatar)) { if ($avatars) {
ksort($avatar); ksort($avatars);
$data['photo'] = self::fixAvatar(array_pop($avatar), $data['baseurl']); $data['photo'] = array_pop($avatars);
if ($avatars) {
$data['photo_medium'] = array_pop($avatars);
}
if ($avatars) {
$data['photo_small'] = array_pop($avatars);
}
} }
if ($dfrn) { if ($dfrn) {
@ -1356,7 +1374,6 @@ class Probe
} }
} }
return $data; return $data;
} }

View file

@ -45,6 +45,7 @@ use Friendica\Model\Post;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Model\Tag; use Friendica\Model\Tag;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPException;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Util\Crypto; use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
@ -981,12 +982,12 @@ class DFRN
} }
} }
$fcontact = FContact::getByURL($contact['addr']); try {
if (empty($fcontact)) { $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey;
} catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']); Logger::notice('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']);
return -22; return -22;
} }
$pubkey = $fcontact['pubkey'] ?? '';
} else { } else {
$pubkey = ''; $pubkey = '';
} }

View file

@ -33,7 +33,6 @@ use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Conversation; use Friendica\Model\Conversation;
use Friendica\Model\FContact;
use Friendica\Model\GServer; use Friendica\Model\GServer;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\ItemURI; use Friendica\Model\ItemURI;
@ -42,7 +41,7 @@ use Friendica\Model\Post;
use Friendica\Model\Tag; use Friendica\Model\Tag;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Network\HTTPException;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Util\Crypto; use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
@ -455,9 +454,8 @@ class Diaspora
} }
// Once we have the author URI, go to the web and try to find their public key // Once we have the author URI, go to the web and try to find their public key
// (first this will look it up locally if it is in the fcontact cache) // (first this will look it up locally if it is in the diaspora-contact cache)
// This will also convert diaspora public key from pkcs#1 to pkcs#8 // This will also convert diaspora public key from pkcs#1 to pkcs#8
Logger::notice('Fetching key for ' . $author); Logger::notice('Fetching key for ' . $author);
$key = self::key($author); $key = self::key($author);
if (!$key) { if (!$key) {
@ -795,13 +793,11 @@ class Diaspora
private static function key(WebFingerUri $uri): string private static function key(WebFingerUri $uri): string
{ {
Logger::notice('Fetching diaspora key', ['handle' => $uri->getAddr(), 'callstack' => System::callstack(20)]); Logger::notice('Fetching diaspora key', ['handle' => $uri->getAddr(), 'callstack' => System::callstack(20)]);
try {
$fcontact = FContact::getByURL($uri); return DI::dsprContact()->getByAddr($uri)->pubKey;
if (!empty($fcontact['pubkey'])) { } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
return $fcontact['pubkey']; return '';
} }
return '';
} }
/** /**
@ -822,15 +818,17 @@ class Diaspora
/** /**
* Checks if the given contact url does support ActivityPub * Checks if the given contact url does support ActivityPub
* *
* @param string $url profile url * @param string $url profile url or WebFinger address
* @param boolean $update true = always update, false = never update, null = update when not found or outdated * @param boolean|null $update true = always update, false = never update, null = update when not found or outdated
* @return boolean * @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function isSupportedByContactUrl(string $url, $update = null) public static function isSupportedByContactUrl(string $url, ?bool $update = null): bool
{ {
return !empty(FContact::getByURL($url, $update)); $contact = Contact::getByURL($url, $update);
return DI::dsprContact()->existsByUriId($contact['uri-id'] ?? 0);
} }
/** /**
@ -977,7 +975,7 @@ class Diaspora
// 0 => '[url=/people/0123456789abcdef]Foo Bar[/url]' // 0 => '[url=/people/0123456789abcdef]Foo Bar[/url]'
// 1 => '0123456789abcdef' // 1 => '0123456789abcdef'
// 2 => 'Foo Bar' // 2 => 'Foo Bar'
$handle = FContact::getUrlByGuid($match[1]); $handle = DI::dsprContact()->getUrlByGuid($match[1]);
if ($handle) { if ($handle) {
$return = '@[url=' . $handle . ']' . $match[2] . '[/url]'; $return = '@[url=' . $handle . ']' . $match[2] . '[/url]';
@ -1188,18 +1186,21 @@ class Diaspora
$item = Post::selectFirst($fields, $condition); $item = Post::selectFirst($fields, $condition);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
$person = FContact::getByURL($author); try {
$result = self::storeByGuid($guid, $person['url'], false); $result = self::storeByGuid($guid, DI::dsprContact()->getByAddr($author)->url, false);
// We don't have an url for items that arrived at the public dispatcher // We don't have an url for items that arrived at the public dispatcher
if (!$result && !empty($contact['url'])) { if (!$result && !empty($contact['url'])) {
$result = self::storeByGuid($guid, $contact['url'], false); $result = self::storeByGuid($guid, $contact['url'], false);
} }
if ($result) { if ($result) {
Logger::info('Fetched missing item ' . $guid . ' - result: ' . $result); Logger::info('Fetched missing item ' . $guid . ' - result: ' . $result);
$item = Post::selectFirst($fields, $condition); $item = Post::selectFirst($fields, $condition);
}
} catch (HTTPException\NotFoundException $e) {
Logger::notice('Unable to retrieve author details', ['author' => $author->getAddr()]);
} }
} }
@ -1422,16 +1423,14 @@ class Diaspora
private static function getUriFromGuid(string $guid, WebFingerUri $person_uri = null): string private static function getUriFromGuid(string $guid, WebFingerUri $person_uri = null): string
{ {
$item = Post::selectFirst(['uri'], ['guid' => $guid]); $item = Post::selectFirst(['uri'], ['guid' => $guid]);
if (DBA::isResult($item)) { if ($item) {
return $item['uri']; return $item['uri'];
} elseif ($person_uri) { } elseif ($person_uri) {
$person = FContact::getByURL($person_uri); try {
return DI::dsprContact()->selectOneByAddr($person_uri)->baseurl . '/objects/' . $guid;
$parts = parse_url($person['url']); } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
unset($parts['path']); return '';
$host_url = (string)Uri::fromParts($parts); }
return $host_url . '/objects/' . $guid;
} }
return ''; return '';
@ -1462,12 +1461,12 @@ class Diaspora
continue; continue;
} }
$person = FContact::getByURL($match[3]); try {
if (empty($person)) { $contact = DI::dsprContact()->getByUrl(new Uri($match[3]));
continue; Tag::storeByHash($uriid, $match[1], $contact->name ?: $contact->nick, $contact->url);
} } catch (\Throwable $e) {
Tag::storeByHash($uriid, $match[1], $person['name'] ?: $person['nick'], $person['url']); }
} }
} }
@ -1523,14 +1522,15 @@ class Diaspora
return false; return false;
} }
$person = FContact::getByURL($author); try {
if (!is_array($person)) { $author_url = (string)DI::dsprContact()->getByAddr($author)->url;
Logger::notice('Unable to find author details'); } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('Unable to find author details', ['author' => $author->getAddr()]);
return false; return false;
} }
// Fetch the contact id - if we know this contact // Fetch the contact id - if we know this contact
$author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']); $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']);
$datarray = []; $datarray = [];
@ -1538,11 +1538,11 @@ class Diaspora
$datarray['contact-id'] = $author_contact['cid']; $datarray['contact-id'] = $author_contact['cid'];
$datarray['network'] = $author_contact['network']; $datarray['network'] = $author_contact['network'];
$datarray['author-link'] = $person['url']; $datarray['author-link'] = $author_url;
$datarray['author-id'] = Contact::getIdForURL($person['url'], 0); $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['owner-link'] = $contact['url']; $datarray['owner-link'] = $contact['url'];
$datarray['owner-id'] = Contact::getIdForURL($contact['url'], 0); $datarray['owner-id'] = Contact::getIdForURL($contact['url']);
// Will be overwritten for sharing accounts in Item::insert // Will be overwritten for sharing accounts in Item::insert
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
@ -1569,7 +1569,7 @@ class Diaspora
$datarray['plink'] = self::plink($author, $guid, $toplevel_parent_item['guid']); $datarray['plink'] = self::plink($author, $guid, $toplevel_parent_item['guid']);
$body = Markdown::toBBCode($text); $body = Markdown::toBBCode($text);
$datarray['body'] = self::replacePeopleGuid($body, $person['url']); $datarray['body'] = self::replacePeopleGuid($body, $author_url);
self::storeMentions($datarray['uri-id'], $text); self::storeMentions($datarray['uri-id'], $text);
Tag::storeRawTagsFromBody($datarray['uri-id'], $datarray['body']); Tag::storeRawTagsFromBody($datarray['uri-id'], $datarray['body']);
@ -1633,6 +1633,12 @@ class Diaspora
return false; return false;
} }
try {
$msg_author_uri = WebFingerUri::fromString($msg_author_handle);
} catch (\InvalidArgumentException $e) {
return false;
}
$msg_guid = XML::unescape($mesg->guid); $msg_guid = XML::unescape($mesg->guid);
$msg_conversation_guid = XML::unescape($mesg->conversation_guid); $msg_conversation_guid = XML::unescape($mesg->conversation_guid);
$msg_text = XML::unescape($mesg->text); $msg_text = XML::unescape($mesg->text);
@ -1643,20 +1649,18 @@ class Diaspora
return false; return false;
} }
$body = Markdown::toBBCode($msg_text); $msg_author = DI::dsprContact()->getByAddr($msg_author_uri);
$person = FContact::getByURL($msg_author_handle);
return Mail::insert([ return Mail::insert([
'uid' => $importer['uid'], 'uid' => $importer['uid'],
'guid' => $msg_guid, 'guid' => $msg_guid,
'convid' => $conversation['id'], 'convid' => $conversation['id'],
'from-name' => $person['name'], 'from-name' => $msg_author->name,
'from-photo' => $person['photo'], 'from-photo' => (string)$msg_author->photo,
'from-url' => $person['url'], 'from-url' => (string)$msg_author->url,
'contact-id' => $contact['id'], 'contact-id' => $contact['id'],
'title' => $subject, 'title' => $subject,
'body' => $body, 'body' => Markdown::toBBCode($msg_text),
'uri' => $msg_author_handle . ':' . $msg_guid, 'uri' => $msg_author_handle . ':' . $msg_guid,
'parent-uri' => $author_handle . ':' . $guid, 'parent-uri' => $author_handle . ':' . $guid,
'created' => $msg_created_at 'created' => $msg_created_at
@ -1770,14 +1774,15 @@ class Diaspora
return false; return false;
} }
$person = FContact::getByURL($author); try {
if (!is_array($person)) { $author_url = (string)DI::dsprContact()->getByAddr($author)->url;
Logger::notice('Unable to find author details'); } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('Unable to find author details', ['author' => $author->getAddr()]);
return false; return false;
} }
// Fetch the contact id - if we know this contact // Fetch the contact id - if we know this contact
$author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']); $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']);
// "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora // "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora
// We would accept this anyhow. // We would accept this anyhow.
@ -1797,8 +1802,8 @@ class Diaspora
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
$datarray['owner-link'] = $datarray['author-link'] = $person['url']; $datarray['owner-link'] = $datarray['author-link'] = $author_url;
$datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0); $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['guid'] = $guid; $datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($guid, $author); $datarray['uri'] = self::getUriFromGuid($guid, $author);
@ -1860,13 +1865,13 @@ class Diaspora
*/ */
private static function receiveMessage(array $importer, SimpleXMLElement $data): bool private static function receiveMessage(array $importer, SimpleXMLElement $data): bool
{ {
$author = WebFingerUri::fromString(XML::unescape($data->author)); $author_uri = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$conversation_guid = XML::unescape($data->conversation_guid); $conversation_guid = XML::unescape($data->conversation_guid);
$text = XML::unescape($data->text); $text = XML::unescape($data->text);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$contact = self::allowedContactByHandle($importer, $author, true); $contact = self::allowedContactByHandle($importer, $author_uri, true);
if (!$contact) { if (!$contact) {
return false; return false;
} }
@ -1882,29 +1887,30 @@ class Diaspora
return false; return false;
} }
$person = FContact::getByURL($author); try {
if (!$person) { $author = DI::dsprContact()->getByAddr($author_uri);
Logger::notice('Unable to find author details'); } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('Unable to find author details', ['author' => $author_uri->getAddr()]);
return false; return false;
} }
$body = Markdown::toBBCode($text); $body = Markdown::toBBCode($text);
$body = self::replacePeopleGuid($body, $person['url']); $body = self::replacePeopleGuid($body, $author->url);
return Mail::insert([ return Mail::insert([
'uid' => $importer['uid'], 'uid' => $importer['uid'],
'guid' => $guid, 'guid' => $guid,
'convid' => $conversation['id'], 'convid' => $conversation['id'],
'from-name' => $person['name'], 'from-name' => $author->name,
'from-photo' => $person['photo'], 'from-photo' => (string)$author->photo,
'from-url' => $person['url'], 'from-url' => (string)$author->url,
'contact-id' => $contact['id'], 'contact-id' => $contact['id'],
'title' => $conversation['subject'], 'title' => $conversation['subject'],
'body' => $body, 'body' => $body,
'reply' => 1, 'reply' => 1,
'uri' => $author . ':' . $guid, 'uri' => $author_uri . ':' . $guid,
'parent-uri' => $author . ':' . $conversation['guid'], 'parent-uri' => $author_uri . ':' . $conversation['guid'],
'created' => $created_at 'created' => $created_at
]); ]);
} }
@ -1953,13 +1959,14 @@ class Diaspora
return false; return false;
} }
$person = FContact::getByURL($author); try {
if (!is_array($person)) { $author_url = (string)DI::dsprContact()->getByAddr($author)->url;
Logger::notice('Person not found: ' . $author); } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('unable to find author details', ['author' => $author->getAddr()]);
return false; return false;
} }
$author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']); $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']);
// Store participation // Store participation
$datarray = []; $datarray = [];
@ -1972,8 +1979,8 @@ class Diaspora
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
$datarray['owner-link'] = $datarray['author-link'] = $person['url']; $datarray['owner-link'] = $datarray['author-link'] = $author_url;
$datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0); $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['guid'] = $guid; $datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($guid, $author); $datarray['uri'] = self::getUriFromGuid($guid, $author);
@ -2233,22 +2240,24 @@ class Diaspora
Logger::info("Author " . $author . " wants to listen to us."); Logger::info("Author " . $author . " wants to listen to us.");
} }
$ret = FContact::getByURL($author); try {
$author_url = (string)DI::dsprContact()->getByAddr($author)->url;
if (!$ret || ($ret['network'] != Protocol::DIASPORA)) { } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice("Cannot resolve diaspora handle " . $author . " for ".$recipient); Logger::notice('Cannot resolve diaspora handle for recipient', ['author' => $author->getAddr(), 'recipient' => $recipient]);
return false; return false;
} }
$cid = Contact::getIdForURL($ret['url'], $importer['uid']); $cid = Contact::getIdForURL($author_url, $importer['uid']);
if (!empty($cid)) { if (!empty($cid)) {
$contact = DBA::selectFirst('contact', [], ['id' => $cid, 'network' => Protocol::NATIVE_SUPPORT]); $contact = DBA::selectFirst('contact', [], ['id' => $cid, 'network' => Protocol::NATIVE_SUPPORT]);
} else { } else {
$contact = []; $contact = [];
} }
$item = ['author-id' => Contact::getIdForURL($ret['url']), $item = [
'author-link' => $ret['url']]; 'author-id' => Contact::getIdForURL($author_url),
'author-link' => $author_url
];
$result = Contact::addRelationship($importer, $contact, $item, false); $result = Contact::addRelationship($importer, $contact, $item, false);
if ($result === true) { if ($result === true) {
@ -2346,7 +2355,12 @@ class Diaspora
$author = WebFingerUri::fromString(XML::unescape($data->author)); $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$root_author = XML::unescape($data->root_author); try {
$root_author = WebFingerUri::fromString(XML::unescape($data->root_author));
} catch (\InvalidArgumentException $e) {
return false;
}
$root_guid = XML::unescape($data->root_guid); $root_guid = XML::unescape($data->root_guid);
/// @todo handle unprocessed property "provider_display_name" /// @todo handle unprocessed property "provider_display_name"
$public = XML::unescape($data->public); $public = XML::unescape($data->public);
@ -2365,8 +2379,9 @@ class Diaspora
return true; return true;
} }
$original_person = FContact::getByURL($root_author); try {
if (!$original_person) { $original_person = DI::dsprContact()->getByAddr($root_author);
} catch (HTTPException\NotFoundException $e) {
return false; return false;
} }
@ -2394,7 +2409,7 @@ class Diaspora
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
$datarray['quote-uri-id'] = self::getQuoteUriId($root_guid, $importer['uid'], $original_person['url']); $datarray['quote-uri-id'] = self::getQuoteUriId($root_guid, $importer['uid'], $original_person->url);
if (empty($datarray['quote-uri-id'])) { if (empty($datarray['quote-uri-id'])) {
return false; return false;
} }
@ -2462,19 +2477,18 @@ class Diaspora
*/ */
private static function itemRetraction(array $importer, array $contact, SimpleXMLElement $data): bool private static function itemRetraction(array $importer, array $contact, SimpleXMLElement $data): bool
{ {
$author_handle = XML::unescape($data->author); $author_uri = WebFingerUri::fromString(XML::unescape($data->author));
$target_guid = XML::unescape($data->target_guid); $target_guid = XML::unescape($data->target_guid);
$target_type = XML::unescape($data->target_type); $target_type = XML::unescape($data->target_type);
$person = FContact::getByURL($author_handle); try {
if (!is_array($person)) { $author = DI::dsprContact()->getByAddr($author_uri);
Logger::notice('Unable to find author detail for ' . $author_handle); } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('Unable to find details for author', ['author' => $author_uri->getAddr()]);
return false; return false;
} }
if (empty($contact['url'])) { $contact_url = $contact['url'] ?? '' ?: (string)$author->url;
$contact['url'] = $person['url'];
}
// Fetch items that are about to be deleted // Fetch items that are about to be deleted
$fields = ['uid', 'id', 'parent', 'author-link', 'uri-id']; $fields = ['uid', 'id', 'parent', 'author-link', 'uri-id'];
@ -2502,8 +2516,8 @@ class Diaspora
$parent = Post::selectFirst(['author-link'], ['id' => $item['parent']]); $parent = Post::selectFirst(['author-link'], ['id' => $item['parent']]);
// Only delete it if the parent author really fits // Only delete it if the parent author really fits
if (!Strings::compareLink($parent['author-link'], $contact['url']) && !Strings::compareLink($item['author-link'], $contact['url'])) { if (!Strings::compareLink($parent['author-link'], $contact_url) && !Strings::compareLink($item['author-link'], $contact_url)) {
Logger::info("Thread author " . $parent['author-link'] . " and item author " . $item['author-link'] . " don't fit to expected contact " . $contact['url']); Logger::info("Thread author " . $parent['author-link'] . " and item author " . $item['author-link'] . " don't fit to expected contact " . $contact_url);
continue; continue;
} }
@ -2968,13 +2982,13 @@ class Diaspora
$logid = Strings::getRandomHex(4); $logid = Strings::getRandomHex(4);
// We always try to use the data from the fcontact table. // We always try to use the data from the diaspora-contact table.
// This is important for transmitting data to Friendica servers. // This is important for transmitting data to Friendica servers.
if (!empty($contact['addr'])) { try {
$fcontact = FContact::getByURL($contact['addr']); $target = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']));
if (!empty($fcontact)) { $dest_url = $public_batch ? $target->batch : $target->notify;
$dest_url = ($public_batch ? $fcontact['batch'] : $fcontact['notify']); } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
}
} }
if (empty($dest_url)) { if (empty($dest_url)) {
@ -3043,18 +3057,19 @@ class Diaspora
} }
// When sending content to Friendica contacts using the Diaspora protocol // When sending content to Friendica contacts using the Diaspora protocol
// we have to fetch the public key from the fcontact. // we have to fetch the public key from the diaspora-contact.
// This is due to the fact that legacy DFRN had unique keys for every contact. // This is due to the fact that legacy DFRN had unique keys for every contact.
$pubkey = $contact['pubkey']; $pubkey = $contact['pubkey'];
if (!empty($contact['addr'])) { if (!empty($contact['addr'])) {
$fcontact = FContact::getByURL($contact['addr']); try {
if (!empty($fcontact)) { $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey;
$pubkey = $fcontact['pubkey']; } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
} }
} else { } else {
// The "addr" field should always be filled. // The "addr" field should always be filled.
// If this isn't the case, it will raise a notice some lines later. // If this isn't the case, it will raise a notice some lines later.
// And in the log we will see where it came from and we can handle it there. // And in the log we will see where it came from, and we can handle it there.
Logger::notice('Empty addr', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]); Logger::notice('Empty addr', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]);
} }
@ -4038,6 +4053,8 @@ class Diaspora
* *
* @param integer $parent_id * @param integer $parent_id
* @return boolean * @return boolean
* @throws InternalServerErrorException
* @throws \ImagickException
*/ */
private static function parentSupportDiaspora(int $parent_id): bool private static function parentSupportDiaspora(int $parent_id): bool
{ {
@ -4047,7 +4064,7 @@ class Diaspora
return false; return false;
} }
if (empty(FContact::getByURL($parent_post['author-link'], false))) { if (!self::isSupportedByContactUrl($parent_post['author-link'], false)) {
Logger::info('Parent author is no Diaspora contact.', ['parent-id' => $parent_id]); Logger::info('Parent author is no Diaspora contact.', ['parent-id' => $parent_id]);
return false; return false;
} }

View file

@ -0,0 +1,140 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Protocol\Diaspora\Entity;
use Psr\Http\Message\UriInterface;
/**
* @property-read $uriId
* @property-read $url
* @property-read $guid
* @property-read $addr
* @property-read $alias
* @property-read $nick
* @property-read $name
* @property-read $givenName
* @property-read $familyName
* @property-read $photo
* @property-read $photoMedium
* @property-read $photoSmall
* @property-read $batch
* @property-read $notify
* @property-read $poll
* @property-read $subscribe
* @property-read $searchable
* @property-read $pubKey
* @property-read $baseurl
* @property-read $gsid
* @property-read $created
* @property-read $updated
* @property-read $interacting_count
* @property-read $interacted_count
* @property-read $post_count
*/
class DiasporaContact extends \Friendica\BaseEntity
{
/** @var int */
protected $uriId;
/** @var UriInterface */
protected $url;
/** @var string */
protected $guid;
/** @var string */
protected $addr;
/** @var UriInterface */
protected $alias;
/** @var string */
protected $nick;
/** @var string */
protected $name;
/** @var string */
protected $givenName;
/** @var string */
protected $familyName;
/** @var UriInterface */
protected $photo;
/** @var UriInterface */
protected $photoMedium;
/** @var UriInterface */
protected $photoSmall;
/** @var UriInterface */
protected $batch;
/** @var UriInterface */
protected $notify;
/** @var UriInterface */
protected $poll;
/** @var UriInterface */
protected $subscribe;
/** @var bool */
protected $searchable;
/** @var string */
protected $pubKey;
/** @var UriInterface */
protected $baseurl;
/** @var int */
protected $gsid;
/** @var \DateTime */
protected $created;
/** @var \DateTime */
protected $updated;
/** @var int */
protected $interacting_count;
/** @var int */
protected $interacted_count;
/** @var int */
protected $post_count;
public function __construct(
UriInterface $url, \DateTime $created, string $guid = null, string $addr = null, UriInterface $alias = null,
string $nick = null, string $name = null, string $givenName = null, string $familyName = null,
UriInterface $photo = null, UriInterface $photoMedium = null, UriInterface $photoSmall = null,
UriInterface $batch = null, UriInterface $notify = null, UriInterface $poll = null, UriInterface $subscribe = null,
bool $searchable = null, string $pubKey = null, UriInterface $baseurl = null, int $gsid = null,
\DateTime $updated = null, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0, int $uriId = null
) {
$this->uriId = $uriId;
$this->url = $url;
$this->guid = $guid;
$this->addr = $addr;
$this->alias = $alias;
$this->nick = $nick;
$this->name = $name;
$this->givenName = $givenName;
$this->familyName = $familyName;
$this->photo = $photo;
$this->photoMedium = $photoMedium;
$this->photoSmall = $photoSmall;
$this->batch = $batch;
$this->notify = $notify;
$this->poll = $poll;
$this->subscribe = $subscribe;
$this->searchable = $searchable;
$this->pubKey = $pubKey;
$this->baseurl = $baseurl;
$this->gsid = $gsid;
$this->created = $created;
$this->updated = $updated;
$this->interacting_count = $interacting_count;
$this->interacted_count = $interacted_count;
$this->post_count = $post_count;
}
}

View file

@ -0,0 +1,102 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Protocol\Diaspora\Factory;
use Friendica\Capabilities\ICanCreateFromTableRow;
use Friendica\Database\DBA;
use GuzzleHttp\Psr7\Uri;
class DiasporaContact extends \Friendica\BaseFactory implements ICanCreateFromTableRow
{
public function createFromTableRow(array $row): \Friendica\Protocol\Diaspora\Entity\DiasporaContact
{
return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact(
new Uri($row['url']),
new \DateTime($row['created'], new \DateTimeZone('UTC')),
$row['guid'],
$row['addr'],
$row['alias'] ? new Uri($row['alias']) : null,
$row['nick'],
$row['name'],
$row['given-name'],
$row['family-name'],
$row['photo'] ? new Uri($row['photo']) : null,
$row['photo-medium'] ? new Uri($row['photo-medium']) : null,
$row['photo-small'] ? new Uri($row['photo-small']) : null,
$row['batch'] ? new Uri($row['batch']) : null,
$row['notify'] ? new Uri($row['notify']) : null,
$row['poll'] ? new Uri($row['poll']) : null,
$row['subscribe'] ? new Uri($row['subscribe']) : null,
$row['searchable'],
$row['pubkey'],
$row['baseurl'] ? new Uri($row['baseurl']) : null,
$row['gsid'],
$row['updated'] !== DBA::NULL_DATETIME ? new \DateTime($row['updated'], new \DateTimeZone('UTC')) : null,
$row['interacting_count'],
$row['interacted_count'],
$row['post_count'],
$row['uri-id'],
);
}
/**
* @param array $data Data returned by \Friendica\Network\Probe::uri()
* @param int $uriId The URI ID of the Diaspora contact URL + GUID
* @param \DateTime $created
* @param int $interacting_count
* @param int $interacted_count
* @param int $post_count
* @return \Friendica\Protocol\Diaspora\Entity\DiasporaContact
*/
public function createfromProbeData(array $data, int $uriId, \DateTime $created, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0): \Friendica\Protocol\Diaspora\Entity\DiasporaContact
{
$alias = $data['alias'] != $data['url'] ? $data['alias'] : null;
return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact(
new Uri($data['url']),
$created,
$data['guid'],
$data['addr'],
$alias ? new Uri($alias) : null,
$data['nick'],
$data['name'],
$data['given-name'] ?? '',
$data['family-name'] ?? '',
$data['photo'] ? new Uri($data['photo']) : null,
!empty($data['photo_medium']) ? new Uri($data['photo_medium']) : null,
!empty($data['photo_small']) ? new Uri($data['photo_small']) : null,
$data['batch'] ? new Uri($data['batch']) : null,
$data['notify'] ? new Uri($data['notify']) : null,
$data['poll'] ? new Uri($data['poll']) : null,
$data['subscribe'] ? new Uri($data['subscribe']) : null,
!$data['hide'],
$data['pubkey'],
$data['baseurl'] ? new Uri($data['baseurl']) : null,
$data['gsid'],
null,
$interacting_count,
$interacted_count,
$post_count,
$uriId,
);
}
}

View file

@ -0,0 +1,283 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Protocol\Diaspora\Repository;
use Friendica\BaseRepository;
use Friendica\Core\System;
use Friendica\Database\Database;
use Friendica\Database\Definition\DbaDefinition;
use Friendica\Model\APContact;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Network\HTTPException;
use Friendica\Protocol\Diaspora\Entity;
use Friendica\Protocol\Diaspora\Factory;
use Friendica\Protocol\WebFingerUri;
use Friendica\Util\DateTimeFormat;
use Psr\Http\Message\UriInterface;
use Psr\Log\LoggerInterface;
class DiasporaContact extends BaseRepository
{
const ALWAYS_UPDATE = true;
const NEVER_UPDATE = false;
const UPDATE_IF_MISSING_OR_OUTDATED = null;
protected static $table_name = 'diaspora-contact-view';
/** @var Factory\DiasporaContact */
protected $factory;
/** @var DbaDefinition */
private $definition;
public function __construct(DbaDefinition $definition, Database $database, LoggerInterface $logger, Factory\DiasporaContact $factory)
{
parent::__construct($database, $logger, $factory);
$this->definition = $definition;
}
/**
* @param array $condition
* @param array $params
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOne(array $condition, array $params = []): Entity\DiasporaContact
{
return parent::_selectOne($condition, $params);
}
/**
* @param int $uriId
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOneByUriId(int $uriId): Entity\DiasporaContact
{
return $this->selectOne(['uri-id' => $uriId]);
}
/**
* @param UriInterface $uri
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOneByUri(UriInterface $uri): Entity\DiasporaContact
{
try {
return $this->selectOne(['url' => (string) $uri]);
} catch (HTTPException\NotFoundException $e) {
}
try {
return $this->selectOne(['addr' => (string) $uri]);
} catch (HTTPException\NotFoundException $e) {
}
return $this->selectOne(['alias' => (string) $uri]);
}
/**
* @param WebFingerUri $uri
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOneByAddr(WebFingerUri $uri): Entity\DiasporaContact
{
return $this->selectOne(['addr' => $uri->getAddr()]);
}
/**
* @param int $uriId
* @return bool
* @throws \Exception
*/
public function existsByUriId(int $uriId): bool
{
return $this->db->exists(self::$table_name, ['uri-id' => $uriId]);
}
public function save(Entity\DiasporaContact $DiasporaContact): Entity\DiasporaContact
{
$uriId = $DiasporaContact->uriId ?? ItemURI::insert(['uri' => $DiasporaContact->url, 'guid' => $DiasporaContact->guid]);
$fields = [
'uri-id' => $uriId,
'addr' => $DiasporaContact->addr,
'alias' => (string)$DiasporaContact->alias,
'nick' => $DiasporaContact->nick,
'name' => $DiasporaContact->name,
'given-name' => $DiasporaContact->givenName,
'family-name' => $DiasporaContact->familyName,
'photo' => (string)$DiasporaContact->photo,
'photo-medium' => (string)$DiasporaContact->photoMedium,
'photo-small' => (string)$DiasporaContact->photoSmall,
'batch' => (string)$DiasporaContact->batch,
'notify' => (string)$DiasporaContact->notify,
'poll' => (string)$DiasporaContact->poll,
'subscribe' => (string)$DiasporaContact->subscribe,
'searchable' => $DiasporaContact->searchable,
'pubkey' => $DiasporaContact->pubKey,
'gsid' => $DiasporaContact->gsid,
'created' => $DiasporaContact->created->format(DateTimeFormat::MYSQL),
'updated' => DateTimeFormat::utcNow(),
'interacting_count' => $DiasporaContact->interacting_count,
'interacted_count' => $DiasporaContact->interacted_count,
'post_count' => $DiasporaContact->post_count,
];
// Limit the length on incoming fields
$fields = $this->definition->truncateFieldsForTable('diaspora-contact', $fields);
$this->db->insert('diaspora-contact', $fields, Database::INSERT_UPDATE);
return $this->selectOneByUriId($uriId);
}
/**
* Fetch a Diaspora profile from a given WebFinger address and updates it depending on the mode
*
* @param WebFingerUri $uri Profile address
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function getByAddr(WebFingerUri $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
{
if ($update !== self::ALWAYS_UPDATE) {
try {
$dcontact = $this->selectOneByAddr($uri);
if ($update === self::NEVER_UPDATE) {
return $dcontact;
}
} catch (HTTPException\NotFoundException $e) {
if ($update === self::NEVER_UPDATE) {
throw $e;
}
// This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
// but we still need the result of a probe to create the missing diaspora-contact record.
$update = self::ALWAYS_UPDATE;
}
}
$contact = Contact::getByURL($uri, $update, ['uri-id']);
if (empty($contact['uri-id'])) {
throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
}
return self::selectOneByUriId($contact['uri-id']);
}
/**
* Fetch a Diaspora profile from a given profile URL and updates it depending on the mode
*
* @param UriInterface $uri Profile URL
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function getByUrl(UriInterface $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
{
if ($update !== self::ALWAYS_UPDATE) {
try {
$dcontact = $this->selectOneByUriId(ItemURI::getIdByURI($uri));
if ($update === self::NEVER_UPDATE) {
return $dcontact;
}
} catch (HTTPException\NotFoundException $e) {
if ($update === self::NEVER_UPDATE) {
throw $e;
}
// This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
// but we still need the result of a probe to create the missing diaspora-contact record.
$update = self::ALWAYS_UPDATE;
}
}
$contact = Contact::getByURL($uri, $update, ['uri-id']);
if (empty($contact['uri-id'])) {
throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
}
return self::selectOneByUriId($contact['uri-id']);
}
/**
* Update or create a diaspora-contact entry via a probe array
*
* @param array $data Probe array
* @return Entity\DiasporaContact
* @throws \Exception
*/
public function updateFromProbeArray(array $data): Entity\DiasporaContact
{
$uriId = ItemURI::insert(['uri' => $data['url'], 'guid' => $data['guid']]);
$contact = Contact::getByUriId($uriId, ['id', 'created']);
$apcontact = APContact::getByURL($data['url'], false);
if (!empty($apcontact)) {
$interacting_count = $apcontact['followers_count'];
$interacted_count = $apcontact['following_count'];
$post_count = $apcontact['statuses_count'];
} elseif (!empty($contact['id'])) {
$last_interaction = DateTimeFormat::utc('now - 180 days');
$interacting_count = $this->db->count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
$interacted_count = $this->db->count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
$post_count = $this->db->count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]);
}
$DiasporaContact = $this->factory->createfromProbeData(
$data,
$uriId,
new \DateTime($contact['created'] ?? 'now', new \DateTimeZone('UTC')),
$interacting_count ?? 0,
$interacted_count ?? 0,
$post_count ?? 0
);
$DiasporaContact = $this->save($DiasporaContact);
$this->logger->info('Updated diaspora-contact', ['url' => (string) $DiasporaContact->url, 'callstack' => System::callstack(20)]);
return $DiasporaContact;
}
/**
* get a url (scheme://domain.tld/u/user) from a given contact guid
*
* @param mixed $guid Hexadecimal string guid
*
* @return string the contact url or null
* @throws \Exception
*/
public function getUrlByGuid(string $guid): ?string
{
$diasporaContact = $this->db->selectFirst(self::$table_name, ['url'], ['guid' => $guid]);
return $diasporaContact['url'] ?? null;
}
}

View file

@ -29,7 +29,6 @@ use Friendica\Core\Worker;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\FContact;
use Friendica\Model\GServer; use Friendica\Model\GServer;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
@ -94,7 +93,7 @@ class Delivery
if ($item['verb'] == Activity::ANNOUNCE) { if ($item['verb'] == Activity::ANNOUNCE) {
continue; continue;
} }
if ($item['id'] == $parent_id) { if ($item['id'] == $parent_id) {
$parent = $item; $parent = $item;
} }
@ -278,7 +277,7 @@ class Delivery
private static function deliverDFRN(string $cmd, array $contact, array $owner, array $items, array $target_item, bool $public_message, bool $top_level, bool $followup, int $server_protocol = null) private static function deliverDFRN(string $cmd, array $contact, array $owner, array $items, array $target_item, bool $public_message, bool $top_level, bool $followup, int $server_protocol = null)
{ {
// Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora // Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora
if (Diaspora::getReshareDetails($target_item ?? []) && !empty(FContact::getByURL($contact['addr'], false))) { if (Diaspora::getReshareDetails($target_item ?? []) && Diaspora::isSupportedByContactUrl($contact['addr'], false)) {
Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]); Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]);
self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
return; return;

View file

@ -189,7 +189,7 @@ class ExpirePosts
AND NOT EXISTS(SELECT `uri-id` FROM `user-contact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `user-contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `contact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `apcontact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `apcontact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `fcontact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `diaspora-contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `inbox-status` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `inbox-status` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `inbox-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `inbox-id` = `item-uri`.`id`)

View file

@ -990,11 +990,11 @@
"blocked" => ["contact", "blocked"], "blocked" => ["contact", "blocked"],
"dfrn-notify" => ["contact", "notify"], "dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"], "dfrn-poll" => ["contact", "poll"],
"diaspora-guid" => ["fcontact", "guid"], "diaspora-guid" => ["item-uri", "guid"],
"diaspora-batch" => ["fcontact", "batch"], "diaspora-batch" => ["diaspora-contact", "batch"],
"diaspora-notify" => ["fcontact", "notify"], "diaspora-notify" => ["diaspora-contact", "notify"],
"diaspora-poll" => ["fcontact", "poll"], "diaspora-poll" => ["diaspora-contact", "poll"],
"diaspora-alias" => ["fcontact", "alias"], "diaspora-alias" => ["diaspora-contact", "alias"],
"ap-uuid" => ["apcontact", "uuid"], "ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"], "ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"], "ap-following" => ["apcontact", "following"],
@ -1013,7 +1013,7 @@
"query" => "FROM `contact` "query" => "FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id` LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`
WHERE `contact`.`uid` = 0" WHERE `contact`.`uid` = 0"
], ],
@ -1089,14 +1089,14 @@
"reason" => ["ucontact", "reason"], "reason" => ["ucontact", "reason"],
"dfrn-notify" => ["contact", "notify"], "dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"], "dfrn-poll" => ["contact", "poll"],
"diaspora-guid" => ["fcontact", "guid"], "diaspora-guid" => ["item-uri", "guid"],
"diaspora-batch" => ["fcontact", "batch"], "diaspora-batch" => ["diaspora-contact", "batch"],
"diaspora-notify" => ["fcontact", "notify"], "diaspora-notify" => ["diaspora-contact", "notify"],
"diaspora-poll" => ["fcontact", "poll"], "diaspora-poll" => ["diaspora-contact", "poll"],
"diaspora-alias" => ["fcontact", "alias"], "diaspora-alias" => ["diaspora-contact", "alias"],
"diaspora-interacting_count" => ["fcontact", "interacting_count"], "diaspora-interacting_count" => ["diaspora-contact", "interacting_count"],
"diaspora-interacted_count" => ["fcontact", "interacted_count"], "diaspora-interacted_count" => ["diaspora-contact", "interacted_count"],
"diaspora-post_count" => ["fcontact", "post_count"], "diaspora-post_count" => ["diaspora-contact", "post_count"],
"ap-uuid" => ["apcontact", "uuid"], "ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"], "ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"], "ap-following" => ["apcontact", "following"],
@ -1116,7 +1116,7 @@
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr' LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`" LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`"
], ],
"pending-view" => [ "pending-view" => [