Use "author_handle" for the author handle extracted from Diaspora XML messages

- We have structured data under the variable name "author"
- Remove unused $uid parameter from Diaspora::storeByGuid
- Convert $person parameter to just its URL in Diaspora::authorContactByUrl
- Flip parameters in getUriFromGuid to get rid of $onlyfound
This commit is contained in:
Michael 2021-08-28 08:42:20 +00:00 committed by Hypolite Petovan
parent d5e005f90d
commit ace80ca1b4
3 changed files with 416 additions and 154 deletions

View file

@ -42,6 +42,7 @@ use Friendica\Model\Post;
use Friendica\Model\Tag;
use Friendica\Model\User;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Network\Probe;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
@ -161,8 +162,12 @@ class Diaspora
return false;
}
$key = self::key($handle);
try {
$key = self::key(WebFingerUri::fromString($handle));
if ($key == '') {
throw new \InvalidArgumentException();
}
} catch (\InvalidArgumentException $e) {
Logger::notice("Couldn't get a key for handle " . $handle . ". Discarding.");
return false;
}
@ -300,8 +305,13 @@ class Diaspora
}
}
$key = self::key($author_addr);
try {
$author = WebFingerUri::fromString($author_addr);
$key = self::key($author);
if ($key == '') {
throw new \InvalidArgumentException();
}
} catch (\InvalidArgumentException $e) {
Logger::notice("Couldn't get a key for handle " . $author_addr . ". Discarding.");
if ($no_exit) {
return false;
@ -322,7 +332,7 @@ class Diaspora
return [
'message' => (string)Strings::base64UrlDecode($base->data),
'author' => XML::unescape($author_addr),
'author' => $author->getAddr(),
'key' => (string)$key
];
}
@ -356,7 +366,7 @@ class Diaspora
if ($children->header) {
$public = true;
$author_link = str_replace('acct:', '', $children->header->author_id);
$idom = $children->header;
} else {
// This happens with posts from a relais
if (empty($privKey)) {
@ -384,8 +394,13 @@ class Diaspora
$inner_iv = base64_decode($idom->iv);
$inner_aes_key = base64_decode($idom->aes_key);
}
$author_link = str_replace('acct:', '', $idom->author_id);
try {
$author = WebFingerUri::fromString($idom->author_id);
} catch (\Throwable $e) {
Logger::notice('Could not retrieve author URI.', ['idom' => $idom]);
throw new \Friendica\Network\HTTPException\BadRequestException();
}
$dom = $basedom->children(ActivityNamespace::SALMON_ME);
@ -439,17 +454,12 @@ class Diaspora
$inner_decrypted = self::aesDecrypt($inner_aes_key, $inner_iv, $inner_encrypted);
}
if (!$author_link) {
Logger::notice('Could not retrieve author URI.');
throw new \Friendica\Network\HTTPException\BadRequestException();
}
// 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)
// This will also convert diaspora public key from pkcs#1 to pkcs#8
Logger::notice('Fetching key for '.$author_link);
$key = self::key($author_link);
Logger::notice('Fetching key for ' . $author);
$key = self::key($author);
if (!$key) {
Logger::notice('Could not retrieve author key.');
throw new \Friendica\Network\HTTPException\BadRequestException();
@ -465,9 +475,9 @@ class Diaspora
Logger::notice('Message verified.');
return [
'message' => (string)$inner_decrypted,
'author' => XML::unescape($author_link),
'key' => (string)$key
'message' => $inner_decrypted,
'author' => $author->getAddr(),
'key' => $key
];
}
@ -520,7 +530,7 @@ class Diaspora
{
// The sender is the handle of the contact that sent the message.
// This will often be different with relayed messages (for example "like" and "comment")
$sender = $msg['author'];
$sender = WebFingerUri::fromString($msg['author']);
// This is only needed for private postings since this is already done for public ones before
if (is_null($fields)) {
@ -535,7 +545,7 @@ class Diaspora
$type = $fields->getName();
Logger::info('Received message', ['type' => $type, 'sender' => $sender, 'user' => $importer['uid']]);
Logger::info('Received message', ['type' => $type, 'sender' => $sender->getAddr(), 'user' => $importer['uid']]);
switch ($type) {
case 'account_migration':
@ -743,7 +753,7 @@ class Diaspora
}
if (isset($parent_author_signature)) {
$key = self::key($msg['author']);
$key = self::key(WebFingerUri::fromString($msg['author']));
if (empty($key)) {
Logger::info('No key found for parent', ['author' => $msg['author']]);
return false;
@ -755,8 +765,12 @@ class Diaspora
}
}
$key = self::key($fields->author);
try {
$key = self::key(WebFingerUri::fromString($fields->author));
if (empty($key)) {
throw new \InvalidArgumentException();
}
} catch (\Throwable $e) {
Logger::info('No key found', ['author' => $fields->author]);
return false;
}
@ -772,19 +786,17 @@ class Diaspora
/**
* Fetches the public key for a given handle
*
* @param string $handle The handle
* @param WebFingerUri $uri The handle
*
* @return string The public key
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws InternalServerErrorException
* @throws \ImagickException
*/
private static function key(string $handle = null): string
private static function key(WebFingerUri $uri): string
{
$handle = strval($handle);
Logger::notice('Fetching diaspora key', ['handle' => $uri->getAddr(), 'callstack' => System::callstack(20)]);
Logger::notice('Fetching diaspora key', ['handle' => $handle, 'callstack' => System::callstack(20)]);
$fcontact = FContact::getByURL($handle);
$fcontact = FContact::getByURL($uri);
if (!empty($fcontact['pubkey'])) {
return $fcontact['pubkey'];
}
@ -795,18 +807,16 @@ class Diaspora
/**
* Get a contact id for a given handle
*
* @todo Move to Friendica\Model\Contact
*
* @param int $uid The user id
* @param string $handle The handle in the format user@domain.tld
* @param WebFingerUri $uri
*
* @return array Contact data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function contactByHandle(int $uid, string $handle): array
private static function contactByHandle(int $uid, WebFingerUri $uri): array
{
return Contact::getByURL($handle, null, [], $uid);
return Contact::getByURL($uri->getAddr(), null, [], $uid);
}
/**
@ -875,20 +885,21 @@ class Diaspora
* Fetches the contact id for a handle and checks if posting is allowed
*
* @param array $importer Array of the importer user
* @param string $handle The checked handle in the format user@domain.tld
* @param WebFingerUri $contact_uri The checked contact
* @param bool $is_comment Is the check for a comment?
*
* @return array|bool The contact data or false on error
* @throws \Exception
* @throws InternalServerErrorException
* @throws \ImagickException
*/
private static function allowedContactByHandle(array $importer, string $handle, bool $is_comment = false)
private static function allowedContactByHandle(array $importer, WebFingerUri $contact_uri, bool $is_comment = false)
{
$contact = self::contactByHandle($importer['uid'], $handle);
$contact = self::contactByHandle($importer['uid'], $contact_uri);
if (!$contact) {
Logger::notice('A Contact for handle ' . $handle . ' and user ' . $importer['uid'] . ' was not found');
Logger::notice('A Contact for handle ' . $contact_uri . ' and user ' . $importer['uid'] . ' was not found');
// If a contact isn't found, we accept it anyway if it is a comment
if ($is_comment && ($importer['uid'] != 0)) {
return self::contactByHandle(0, $handle);
return self::contactByHandle(0, $contact_uri);
} elseif ($is_comment) {
return $importer;
} else {
@ -897,7 +908,7 @@ class Diaspora
}
if (!self::postAllow($importer, $contact, $is_comment)) {
Logger::notice('The handle: ' . $handle . ' is not allowed to post to user ' . $importer['uid']);
Logger::notice('The handle: ' . $contact_uri . ' is not allowed to post to user ' . $importer['uid']);
return false;
}
return $contact;
@ -1011,7 +1022,7 @@ class Diaspora
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function storeByGuid(string $guid, string $server, bool $force)
private static function storeByGuid(string $guid, string $server, bool $force)
{
$serverparts = parse_url($server);
@ -1092,24 +1103,26 @@ class Diaspora
return self::message($source_xml->root_guid, $server, ++$level);
}
$author = '';
$author_handle = '';
// Fetch the author - for the old and the new Diaspora version
if ($source_xml->post->status_message && $source_xml->post->status_message->diaspora_handle) {
$author = (string)$source_xml->post->status_message->diaspora_handle;
$author_handle = (string)$source_xml->post->status_message->diaspora_handle;
} elseif ($source_xml->author && ($source_xml->getName() == 'status_message')) {
$author = (string)$source_xml->author;
$author_handle = (string)$source_xml->author;
}
try {
$author = WebFingerUri::fromString($author_handle);
} catch (\InvalidArgumentException $e) {
// If this isn't a "status_message" then quit
if (!$author) {
Logger::info("Message doesn't seem to be a status message");
return false;
}
return [
'message' => $x,
'author' => $author,
'author' => $author->getAddr(),
'key' => self::key($author)
];
}
@ -1159,13 +1172,13 @@ class Diaspora
*
* @param int $uid The user id
* @param string $guid message guid
* @param string $author The handle of the item
* @param WebFingerUri $author
* @param array $contact The contact of the item owner
*
* @return array|bool the item record or false on failure
* @throws \Exception
*/
private static function parentItem(int $uid, string $guid, string $author, array $contact)
private static function parentItem(int $uid, string $guid, WebFingerUri $author, array $contact)
{
$fields = ['id', 'parent', 'body', 'wall', 'uri', 'guid', 'private', 'origin',
'author-name', 'author-link', 'author-avatar', 'gravity',
@ -1200,10 +1213,10 @@ class Diaspora
}
/**
* returns contact details
* returns contact details for the given user
*
* @param array $def_contact The default contact if the person isn't found
* @param array $person The record of the person
* @param array $def_contact The default details if the contact isn't found
* @param string $contact_url The url of the contact
* @param int $uid The user id
*
* @return array
@ -1211,9 +1224,9 @@ class Diaspora
* 'network' => network type
* @throws \Exception
*/
private static function authorContactByUrl(array $def_contact, array $person, int $uid): array
private static function authorContactByUrl(array $def_contact, string $contact_url, int $uid): array
{
$condition = ['nurl' => Strings::normaliseLink($person['url']), 'uid' => $uid];
$condition = ['nurl' => Strings::normaliseLink($contact_url), 'uid' => $uid];
$contact = DBA::selectFirst('contact', ['id', 'network'], $condition);
if (DBA::isResult($contact)) {
$cid = $contact['id'];
@ -1318,21 +1331,27 @@ class Diaspora
*/
private static function receiveAccountMigration(array $importer, SimpleXMLElement $data): bool
{
$old_handle = XML::unescape($data->author);
$new_handle = XML::unescape($data->profile->author);
$signature = XML::unescape($data->signature);
$contact = self::contactByHandle($importer['uid'], $old_handle);
if (!$contact) {
Logger::notice('Cannot find contact for sender: ' . $old_handle . ' and user ' . $importer['uid']);
try {
$old_author = WebFingerUri::fromString(XML::unescape($data->author));
$new_author = WebFingerUri::fromString(XML::unescape($data->profile->author));
} catch (\Throwable $e) {
Logger::notice('Cannot find handles for sender and user', ['data' => $data]);
return false;
}
Logger::notice('Got migration for ' . $old_handle . ', to ' . $new_handle . ' with user ' . $importer['uid']);
$signature = XML::unescape($data->signature);
$contact = self::contactByHandle($importer['uid'], $old_author);
if (!$contact) {
Logger::notice('Cannot find contact for sender: ' . $old_author . ' and user ' . $importer['uid']);
return false;
}
Logger::notice('Got migration for ' . $old_author . ', to ' . $new_author . ' with user ' . $importer['uid']);
// Check signature
$signed_text = 'AccountMigration:' . $old_handle . ':' . $new_handle;
$key = self::key($old_handle);
$signed_text = 'AccountMigration:' . $old_author . ':' . $new_author;
$key = self::key($old_author);
if (!Crypto::rsaVerify($signed_text, $signature, $key, 'sha256')) {
Logger::notice('No valid signature for migration.');
return false;
@ -1342,9 +1361,9 @@ class Diaspora
self::receiveProfile($importer, $data->profile);
// change the technical stuff in contact
$data = Probe::uri($new_handle);
$data = Probe::uri($new_author);
if ($data['network'] == Protocol::PHANTOM) {
Logger::notice("Account for " . $new_handle . " couldn't be probed.");
Logger::notice("Account for " . $new_author . " couldn't be probed.");
return false;
}
@ -1360,7 +1379,7 @@ class Diaspora
'network' => $data['network'],
];
Contact::update($fields, ['addr' => $old_handle]);
Contact::update($fields, ['addr' => $old_author->getAddr()]);
Logger::notice('Contacts are updated.');
@ -1377,15 +1396,15 @@ class Diaspora
*/
private static function receiveAccountDeletion(SimpleXMLElement $data): bool
{
$author = XML::unescape($data->author);
$author_handle = XML::unescape($data->author);
$contacts = DBA::select('contact', ['id'], ['addr' => $author]);
$contacts = DBA::select('contact', ['id'], ['addr' => $author_handle]);
while ($contact = DBA::fetch($contacts)) {
Contact::remove($contact['id']);
}
DBA::close($contacts);
Logger::notice('Removed contacts for ' . $author);
Logger::notice('Removed contacts for ' . $author_handle);
return true;
}
@ -1393,21 +1412,20 @@ class Diaspora
/**
* Fetch the uri from our database if we already have this item (maybe from ourselves)
*
* @param string $author Author handle
* @param string $guid Message guid
* @param boolean $onlyfound Only return uri when found in the database
* @param WebFingerUri|null $person_uri Optional person to derive the base URL from
*
* @return string The constructed uri or the one from our database or empty string on if $onlyfound is true
* @return string The constructed uri or the one from our database or empty string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function getUriFromGuid(string $author, string $guid, bool $onlyfound = false): string
private static function getUriFromGuid(string $guid, WebFingerUri $person_uri = null): string
{
$item = Post::selectFirst(['uri'], ['guid' => $guid]);
if (DBA::isResult($item)) {
return $item['uri'];
} elseif (!$onlyfound) {
$person = FContact::getByURL($author);
} elseif ($person_uri) {
$person = FContact::getByURL($person_uri);
$parts = parse_url($person['url']);
unset($parts['path']);
@ -1457,18 +1475,18 @@ class Diaspora
* Processes an incoming comment
*
* @param array $importer Array of the importer user
* @param string $sender The sender of the message
* @param WebFingerUri $sender The sender of the message
* @param SimpleXMLElement $data The message object
* @param string $xml The original XML of the message
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
*
* @return int The message id of the generated comment or "false" if there was an error
* @return bool The message id of the generated comment or "false" if there was an error
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function receiveComment(array $importer, string $sender, SimpleXMLElement $data, string $xml, int $direction): bool
private static function receiveComment(array $importer, WebFingerUri $sender, SimpleXMLElement $data, string $xml, int $direction): bool
{
$author = XML::unescape($data->author);
$author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid);
$text = XML::unescape($data->text);
@ -1481,7 +1499,7 @@ class Diaspora
if (isset($data->thread_parent_guid)) {
$thread_parent_guid = XML::unescape($data->thread_parent_guid);
$thr_parent = self::getUriFromGuid('', $thread_parent_guid, true);
$thr_parent = self::getUriFromGuid($thread_parent_guid);
} else {
$thr_parent = '';
}
@ -1512,7 +1530,7 @@ class Diaspora
}
// Fetch the contact id - if we know this contact
$author_contact = self::authorContactByUrl($contact, $person, $importer['uid']);
$author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']);
$datarray = [];
@ -1530,7 +1548,7 @@ class Diaspora
$datarray = self::setDirection($datarray, $direction);
$datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($author, $guid);
$datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
$datarray['verb'] = Activity::POST;
@ -1601,16 +1619,16 @@ class Diaspora
*/
private static function receiveConversationMessage(array $importer, array $contact, SimpleXMLElement $data, array $msg, $mesg, array $conversation): bool
{
$author = XML::unescape($data->author);
$author_handle = XML::unescape($data->author);
$guid = XML::unescape($data->guid);
$subject = XML::unescape($data->subject);
// "diaspora_handle" is the element name from the old version
// "author" is the element name from the new version
if ($mesg->author) {
$msg_author = XML::unescape($mesg->author);
$msg_author_handle = XML::unescape($mesg->author);
} elseif ($mesg->diaspora_handle) {
$msg_author = XML::unescape($mesg->diaspora_handle);
$msg_author_handle = XML::unescape($mesg->diaspora_handle);
} else {
return false;
}
@ -1626,9 +1644,8 @@ class Diaspora
}
$body = Markdown::toBBCode($msg_text);
$message_uri = $msg_author . ':' . $msg_guid;
$person = FContact::getByURL($msg_author);
$person = FContact::getByURL($msg_author_handle);
return Mail::insert([
'uid' => $importer['uid'],
@ -1640,8 +1657,8 @@ class Diaspora
'contact-id' => $contact['id'],
'title' => $subject,
'body' => $body,
'uri' => $message_uri,
'parent-uri' => $author . ':' . $guid,
'uri' => $msg_author_handle . ':' . $msg_guid,
'parent-uri' => $author_handle . ':' . $guid,
'created' => $msg_created_at
]);
}
@ -1658,7 +1675,7 @@ class Diaspora
*/
private static function receiveConversation(array $importer, array $msg, SimpleXMLElement $data)
{
$author = XML::unescape($data->author);
$author_handle = XML::unescape($data->author);
$guid = XML::unescape($data->guid);
$subject = XML::unescape($data->subject);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
@ -1671,7 +1688,7 @@ class Diaspora
return false;
}
$contact = self::allowedContactByHandle($importer, $msg['author'], true);
$contact = self::allowedContactByHandle($importer, WebFingerUri::fromString($msg['author']), true);
if (!$contact) {
return false;
}
@ -1685,7 +1702,7 @@ class Diaspora
$r = DBA::insert('conv', [
'uid' => $importer['uid'],
'guid' => $guid,
'creator' => $author,
'creator' => $author_handle,
'created' => $created_at,
'updated' => DateTimeFormat::utcNow(),
'subject' => $subject,
@ -1712,7 +1729,7 @@ class Diaspora
* Processes "like" messages
*
* @param array $importer Array of the importer user
* @param string $sender The sender of the message
* @param WebFingerUri $sender The sender of the message
* @param SimpleXMLElement $data The message object
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
*
@ -1720,9 +1737,9 @@ class Diaspora
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function receiveLike(array $importer, string $sender, SimpleXMLElement $data, int $direction): bool
private static function receiveLike(array $importer, WebFingerUri $sender, SimpleXMLElement $data, int $direction): bool
{
$author = XML::unescape($data->author);
$author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid);
$parent_type = XML::unescape($data->parent_type);
@ -1760,7 +1777,7 @@ class Diaspora
}
// Fetch the contact id - if we know this contact
$author_contact = self::authorContactByUrl($contact, $person, $importer['uid']);
$author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']);
// "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora
// We would accept this anyhow.
@ -1784,7 +1801,7 @@ class Diaspora
$datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0);
$datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($author, $guid);
$datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['verb'] = $verb;
$datarray['gravity'] = Item::GRAVITY_ACTIVITY;
@ -1843,7 +1860,7 @@ class Diaspora
*/
private static function receiveMessage(array $importer, SimpleXMLElement $data): bool
{
$author = XML::unescape($data->author);
$author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$conversation_guid = XML::unescape($data->conversation_guid);
$text = XML::unescape($data->text);
@ -1858,18 +1875,13 @@ class Diaspora
GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA);
}
$conversation = null;
$condition = ['uid' => $importer['uid'], 'guid' => $conversation_guid];
$conversation = DBA::selectFirst('conv', [], $condition);
if (!DBA::isResult($conversation)) {
Logger::notice('Conversation not available.');
return false;
}
$message_uri = $author . ':' . $guid;
$person = FContact::getByURL($author);
if (!$person) {
Logger::notice('Unable to find author details');
@ -1891,7 +1903,7 @@ class Diaspora
'title' => $conversation['subject'],
'body' => $body,
'reply' => 1,
'uri' => $message_uri,
'uri' => $author . ':' . $guid,
'parent-uri' => $author . ':' . $conversation['guid'],
'created' => $created_at
]);
@ -1910,7 +1922,7 @@ class Diaspora
*/
private static function receiveParticipation(array $importer, SimpleXMLElement $data, int $direction): bool
{
$author = strtolower(XML::unescape($data->author));
$author = WebFingerUri::fromString(strtolower(XML::unescape($data->author)));
$guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid);
@ -1947,7 +1959,7 @@ class Diaspora
return false;
}
$author_contact = self::authorContactByUrl($contact, $person, $importer['uid']);
$author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']);
// Store participation
$datarray = [];
@ -1964,7 +1976,7 @@ class Diaspora
$datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0);
$datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($author, $guid);
$datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['verb'] = Activity::FOLLOW;
$datarray['gravity'] = Item::GRAVITY_ACTIVITY;
@ -2056,7 +2068,7 @@ class Diaspora
*/
private static function receiveProfile(array $importer, SimpleXMLElement $data): bool
{
$author = strtolower(XML::unescape($data->author));
$author = WebFingerUri::fromString(strtolower(XML::unescape($data->author)));
$contact = self::contactByHandle($importer['uid'], $author);
if (!$contact) {
@ -2084,16 +2096,13 @@ class Diaspora
$keywords = implode(', ', $keywords);
$handle_parts = explode('@', $author);
$nick = $handle_parts[0];
if ($name === '') {
$name = $handle_parts[0];
$name = $author->getUser();
}
if (preg_match('|^https?://|', $image_url) === 0) {
// @TODO No HTTPS here?
$image_url = 'http://' . $handle_parts[1] . $image_url;
$image_url = 'http://' . $author->getFullHost() . $image_url;
}
Contact::updateAvatar($contact['id'], $image_url);
@ -2115,7 +2124,7 @@ class Diaspora
$fields = ['name' => $name, 'location' => $location,
'name-date' => DateTimeFormat::utcNow(), 'about' => $about,
'addr' => $author, 'nick' => $nick, 'keywords' => $keywords,
'addr' => $author->getAddr(), 'nick' => $author->getUser(), 'keywords' => $keywords,
'unsearchable' => !$searchable, 'sensitive' => $nsfw];
if (!empty($birthday)) {
@ -2158,13 +2167,15 @@ class Diaspora
*/
private static function receiveContactRequest(array $importer, SimpleXMLElement $data): bool
{
$author = XML::unescape($data->author);
$author_handle = XML::unescape($data->author);
$recipient = XML::unescape($data->recipient);
if (!$author || !$recipient) {
if (!$author_handle || !$recipient) {
return false;
}
$author = WebFingerUri::fromString($author_handle);
// the current protocol version doesn't know these fields
// That means that we will assume their existance
if (isset($data->following)) {
@ -2265,9 +2276,12 @@ class Diaspora
* @param array $item Array of reshare post
* @param integer $parent_message_id Id of the parent post
* @param string $guid GUID string of reshare action
* @param string $author Author handle
* @param WebFingerUri $author Author handle
* @return false|void
* @throws InternalServerErrorException
* @throws \ImagickException
*/
private static function addReshareActivity(array $item, int $parent_message_id, string $guid, string $author)
private static function addReshareActivity(array $item, int $parent_message_id, string $guid, WebFingerUri $author)
{
$parent = Post::selectFirst(['uri', 'guid'], ['id' => $parent_message_id]);
@ -2284,7 +2298,7 @@ class Diaspora
$datarray['owner-id'] = $datarray['author-id'];
$datarray['guid'] = $parent['guid'] . '-' . $guid;
$datarray['uri'] = self::getUriFromGuid($author, $datarray['guid']);
$datarray['uri'] = self::getUriFromGuid($datarray['guid'], $author);
$datarray['thr-parent'] = $parent['uri'];
$datarray['verb'] = $datarray['body'] = Activity::ANNOUNCE;
@ -2329,7 +2343,7 @@ class Diaspora
*/
private static function receiveReshare(array $importer, SimpleXMLElement $data, string $xml, int $direction): bool
{
$author = XML::unescape($data->author);
$author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$root_author = XML::unescape($data->root_author);
@ -2337,7 +2351,7 @@ class Diaspora
/// @todo handle unprocessed property "provider_display_name"
$public = XML::unescape($data->public);
$contact = self::allowedContactByHandle($importer, $author, false);
$contact = self::allowedContactByHandle($importer, $author);
if (!$contact) {
return false;
}
@ -2369,7 +2383,7 @@ class Diaspora
$datarray['owner-id'] = $datarray['author-id'];
$datarray['guid'] = $guid;
$datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($author, $guid);
$datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
$datarray['verb'] = Activity::POST;
@ -2448,13 +2462,13 @@ class Diaspora
*/
private static function itemRetraction(array $importer, array $contact, SimpleXMLElement $data): bool
{
$author = XML::unescape($data->author);
$author_handle = XML::unescape($data->author);
$target_guid = XML::unescape($data->target_guid);
$target_type = XML::unescape($data->target_type);
$person = FContact::getByURL($author);
$person = FContact::getByURL($author_handle);
if (!is_array($person)) {
Logger::notice('Unable to find author detail for ' . $author);
Logger::notice('Unable to find author detail for ' . $author_handle);
return false;
}
@ -2506,13 +2520,13 @@ class Diaspora
* Receives retraction messages
*
* @param array $importer Array of the importer user
* @param string $sender The sender of the message
* @param WebFingerUri $sender The sender of the message
* @param SimpleXMLElement $data The message object
*
* @return bool Success
* @throws \Exception
*/
private static function receiveRetraction(array $importer, string $sender, SimpleXMLElement $data)
private static function receiveRetraction(array $importer, WebFingerUri $sender, SimpleXMLElement $data)
{
$target_type = XML::unescape($data->target_type);
@ -2639,14 +2653,14 @@ class Diaspora
*/
private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, string $xml, int $direction)
{
$author = XML::unescape($data->author);
$author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$public = XML::unescape($data->public);
$text = XML::unescape($data->text);
$provider_display_name = XML::unescape($data->provider_display_name);
$contact = self::allowedContactByHandle($importer, $author, false);
$contact = self::allowedContactByHandle($importer, $author);
if (!$contact) {
return false;
}
@ -2672,7 +2686,7 @@ class Diaspora
$datarray = [];
$datarray['guid'] = $guid;
$datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($author, $guid);
$datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
// Attach embedded pictures to the body
@ -3089,16 +3103,16 @@ class Diaspora
$owner = User::getOwnerDataById($item['uid']);
}
$author = self::myHandle($owner);
$author_handle = self::myHandle($owner);
$message = [
'author' => $author,
'author' => $author_handle,
'guid' => System::createUUID(),
'parent_type' => 'Post',
'parent_guid' => $item['guid']
];
Logger::info('Send participation for ' . $item['guid'] . ' by ' . $author);
Logger::info('Send participation for ' . $item['guid'] . ' by ' . $author_handle);
// It doesn't matter what we store, we only want to avoid sending repeated notifications for the same item
DI::cache()->set($cachekey, $item['guid'], Duration::QUARTER_HOUR);

View file

@ -0,0 +1,113 @@
<?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;
use GuzzleHttp\Psr7\Uri;
class WebFingerUri
{
/**
* @var string
*/
private $user;
/**
* @var string
*/
private $host;
/**
* @var int|null
*/
private $port;
/**
* @var string|null
*/
private $path;
private function __construct(string $user, string $host, int $port = null, string $path = null)
{
$this->user = $user;
$this->host = $host;
$this->port = $port;
$this->path = $path;
$this->validate();
}
/**
* @param string $addr
* @return WebFingerUri
*/
public static function fromString(string $addr): WebFingerUri
{
$uri = new Uri('acct://' . preg_replace('/^acct:/', '', $addr));
return new self($uri->getUserInfo(), $uri->getHost(), $uri->getPort(), $uri->getPath());
}
private function validate()
{
if (!$this->user) {
throw new \InvalidArgumentException('WebFinger URI User part is required');
}
if (!$this->host) {
throw new \InvalidArgumentException('WebFinger URI Host part is required');
}
}
public function getUser(): string
{
return $this->user;
}
public function getHost(): string
{
return $this->host;
}
public function getFullHost(): string
{
return $this->host
. ($this->port ? ':' . $this->port : '') .
($this->path ?: '');
}
public function getLongForm(): string
{
return 'acct:' . $this->getShortForm();
}
public function getShortForm(): string
{
return $this->user . '@' . $this->getFullHost();
}
public function getAddr(): string
{
return $this->getShortForm();
}
public function __toString(): string
{
return $this->getShortForm();
}
}

View file

@ -0,0 +1,135 @@
<?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/>.
*
* Main database structure configuration file.
*
* Here are described all the tables, fields and indexes Friendica needs to work.
* The entry order is mostly alphabetic - with the exception of tables that are used in foreign keys.
*
* Syntax (braces indicate optionale values):
* "<table name>" => [
* "comment" => "Description of the table",
* "fields" => [
* "<field name>" => [
* "type" => "<field type>{(<field size>)} <unsigned>",
* "not null" => 0|1,
* {"extra" => "auto_increment",}
* {"default" => "<default value>",}
* {"default" => NULL_DATE,} (for datetime fields)
* {"primary" => "1",}
* {"foreign|relation" => ["<foreign key table name>" => "<foreign key field name>"],}
* "comment" => "Description of the fields"
* ],
* ...
* ],
* "indexes" => [
* "PRIMARY" => ["<primary key field name>", ...],
* "<index name>" => [{"UNIQUE",} "<field name>{(<key size>)}", ...]
* ...
* ],
* ],
*
* Whenever possible prefer "foreign" before "relation" with the foreign keys.
* "foreign" adds true foreign keys on the database level, while "relation" is just an indicator of a table relation without any consequences
*
* If you need to make any change, make sure to increment the DB_UPDATE_VERSION constant value below.
*
*/
namespace Friendica\Test\src\Protocol;
use Friendica\Protocol\WebFingerUri;
use PHPUnit\Framework\TestCase;
class WebFingerUriTest extends TestCase
{
public function dataFromString(): array
{
return [
'long' => [
'expectedLong' => 'acct:selma@www.example.com:8080/friend',
'expectedShort' => 'selma@www.example.com:8080/friend',
'input' => 'acct:selma@www.example.com:8080/friend',
],
'short' => [
'expectedLong' => 'acct:selma@www.example.com:8080/friend',
'expectedShort' => 'selma@www.example.com:8080/friend',
'input' => 'selma@www.example.com:8080/friend',
],
'minimal' => [
'expectedLong' => 'acct:bob@example.com',
'expectedShort' => 'bob@example.com',
'input' => 'bob@example.com',
],
'acct:' => [
'expectedLong' => 'acct:alice@example.acct:90',
'expectedShort' => 'alice@example.acct:90',
'input' => 'alice@example.acct:90',
],
];
}
/**
* @dataProvider dataFromString
* @param string $expectedLong
* @param string $expectedShort
* @param string $input
* @return void
*/
public function testFromString(string $expectedLong, string $expectedShort, string $input)
{
$uri = WebFingerUri::fromString($input);
$this->assertEquals($expectedLong, $uri->getLongForm());
$this->assertEquals($expectedShort, $uri->getShortForm());
}
public function dataFromStringFailure()
{
return [
'missing user' => [
'input' => 'example.com',
],
'missing user @' => [
'input' => '@example.com',
],
'missing host' => [
'input' => 'alice',
],
'missing host @' => [
'input' => 'alice@',
],
'missing everything' => [
'input' => '',
],
];
}
/**
* @dataProvider dataFromStringFailure
* @param string $input
* @return void
*/
public function testFromStringFailure(string $input)
{
$this->expectException(\InvalidArgumentException::class);
WebFingerUri::fromString($input);
}
}