Merge pull request #14668 from annando/mime

Use the fetched mimetype if present / improved detection of quoted posts
This commit is contained in:
Hypolite Petovan 2025-01-05 10:34:24 -05:00 committed by GitHub
commit 7a7a74d93e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 267 additions and 214 deletions

View file

@ -90,6 +90,7 @@ class Media
}
$media['url'] = Network::sanitizeUrl($media['url']);
$media = self::unsetEmptyFields($media);
$media = DI::dbaDefinition()->truncateFieldsForTable('post-media', $media);
@ -157,8 +158,11 @@ class Media
public static function getAttachElement(string $href, int $length, string $type, string $title = ''): string
{
$media = self::fetchAdditionalData([
'type' => self::DOCUMENT, 'url' => $href,
'size' => $length, 'mimetype' => $type, 'description' => $title
'type' => self::DOCUMENT,
'url' => $href,
'size' => $length,
'mimetype' => $type,
'description' => $title
]);
return '[attach]href="' . $media['url'] . '" length="' . $media['size'] .
@ -184,7 +188,7 @@ class Media
}
// Fetch the mimetype or size if missing.
if (Network::isValidHttpUrl($media['url']) && empty($media['mimetype']) && !in_array($media['type'], [self::IMAGE, self::HLS])) {
if (Network::isValidHttpUrl($media['url']) && (empty($media['mimetype']) || $media['type'] == self::HTML) && !in_array($media['type'], [self::IMAGE, self::HLS])) {
$timeout = DI::config()->get('system', 'xrd_timeout');
try {
$curlResult = DI::httpClient()->head($media['url'], [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::AS_DEFAULT, HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]);
@ -194,8 +198,8 @@ class Media
$curlResult = DI::httpClient()->get($media['url'], HttpClientAccept::AS_DEFAULT, [HttpClientOptions::TIMEOUT => $timeout]);
}
if ($curlResult->isSuccess()) {
if (empty($media['mimetype'])) {
$media['mimetype'] = $curlResult->getContentType() ?? '';
if (!empty($curlResult->getContentType())) {
$media['mimetype'] = $curlResult->getContentType();
}
if (empty($media['size'])) {
$media['size'] = (int)($curlResult->getHeader('Content-Length')[0] ?? strlen($curlResult->getBodyString() ?? ''));
@ -611,21 +615,33 @@ class Media
if (self::isLinkToImagePage($picture[1], $picture[2])) {
$body = str_replace($picture[0], '', $body);
$image = str_replace(['-1.', '-2.'], '-0.', $picture[2]);
$attachments[$image] = [
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image,
'preview' => $picture[2], 'description' => $picture[3]
'uri-id' => $uriid,
'type' => self::IMAGE,
'url' => $image,
'preview' => $picture[2],
'description' => $picture[3]
];
} elseif (self::isLinkToPhoto($picture[1], $picture[2])) {
$body = str_replace($picture[0], '', $body);
$attachments[$picture[1]] = [
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1],
'preview' => $picture[2], 'description' => $picture[3]
'uri-id' => $uriid,
'type' => self::IMAGE,
'url' => $picture[1],
'preview' => $picture[2],
'description' => $picture[3]
];
} elseif ($removepicturelinks) {
$body = str_replace($picture[0], '', $body);
$attachments[$picture[1]] = [
'uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $picture[1],
'preview' => $picture[2], 'description' => $picture[3]
'uri-id' => $uriid,
'type' => self::UNKNOWN,
'url' => $picture[1],
'preview' => $picture[2],
'description' => $picture[3]
];
}
}
@ -634,6 +650,7 @@ class Media
if (preg_match_all("/\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]$endmatchpattern/Usi", $body, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
$body = str_replace($picture[0], '', $body);
$attachments[$picture[1]] = ['uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1], 'description' => $picture[2]];
}
}
@ -643,21 +660,33 @@ class Media
if (self::isLinkToImagePage($picture[1], $picture[2])) {
$body = str_replace($picture[0], '', $body);
$image = str_replace(['-1.', '-2.'], '-0.', $picture[2]);
$attachments[$image] = [
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image,
'preview' => $picture[2], 'description' => null
'uri-id' => $uriid,
'type' => self::IMAGE,
'url' => $image,
'preview' => $picture[2],
'description' => null
];
} elseif (self::isLinkToPhoto($picture[1], $picture[2])) {
$body = str_replace($picture[0], '', $body);
$attachments[$picture[1]] = [
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1],
'preview' => $picture[2], 'description' => null
'uri-id' => $uriid,
'type' => self::IMAGE,
'url' => $picture[1],
'preview' => $picture[2],
'description' => null
];
} elseif ($removepicturelinks) {
$body = str_replace($picture[0], '', $body);
$attachments[$picture[1]] = [
'uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $picture[1],
'preview' => $picture[2], 'description' => null
'uri-id' => $uriid,
'type' => self::UNKNOWN,
'url' => $picture[1],
'preview' => $picture[2],
'description' => null
];
}
}
@ -666,6 +695,7 @@ class Media
if (preg_match_all("/\[img\]([^\[\]]*)\[\/img\]$endmatchpattern/ism", $body, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
$body = str_replace($picture[0], '', $body);
$attachments[$picture[1]] = ['uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1]];
}
}
@ -673,6 +703,7 @@ class Media
if (preg_match_all("/\[audio\]([^\[\]]*)\[\/audio\]$endmatchpattern/ism", $body, $audios, PREG_SET_ORDER)) {
foreach ($audios as $audio) {
$body = str_replace($audio[0], '', $body);
$attachments[$audio[1]] = ['uri-id' => $uriid, 'type' => self::AUDIO, 'url' => $audio[1]];
}
}
@ -680,6 +711,7 @@ class Media
if (preg_match_all("/\[video\]([^\[\]]*)\[\/video\]$endmatchpattern/ism", $body, $videos, PREG_SET_ORDER)) {
foreach ($videos as $video) {
$body = str_replace($video[0], '', $body);
$attachments[$video[1]] = ['uri-id' => $uriid, 'type' => self::VIDEO, 'url' => $video[1]];
}
}

View file

@ -47,6 +47,7 @@ use Friendica\Util\Strings;
class Receiver
{
const PUBLIC_COLLECTION = 'as:Public';
const ACCOUNT_TYPES = ['as:Person', 'as:Organization', 'as:Service', 'as:Group', 'as:Application'];
const CONTENT_TYPES = ['as:Note', 'as:Article', 'as:Video', 'as:Image', 'as:Event', 'as:Audio', 'as:Page', 'as:Question'];
const ACTIVITY_TYPES = ['as:Like', 'as:Dislike', 'as:Accept', 'as:Reject', 'as:TentativeAccept', 'as:View', 'as:Read', 'litepub:EmojiReact'];
@ -373,6 +374,7 @@ class Receiver
$receivers = array_replace($receivers, $additional);
if (empty($activity['thread-completion']) && (empty($reception_types[$uid]) || in_array($reception_types[$uid], [self::TARGET_UNKNOWN, self::TARGET_FOLLOWER, self::TARGET_ANSWER, self::TARGET_GLOBAL]))) {
$reception_types[$uid] = self::TARGET_BCC;
$owner = User::getOwnerDataById($uid);
if (!empty($owner['url'])) {
$urls['as:bcc'][] = $owner['url'];
@ -400,12 +402,14 @@ class Receiver
// Any activities on account types must not be altered
if (in_array($type, ['as:Flag'])) {
$object_data = [];
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
$object_data['object_ids'] = JsonLD::fetchElementArray($activity, 'as:object', '@id');
$object_data['content'] = JsonLD::fetchElement($activity, 'as:content', '@type');
} elseif (in_array($object_type, self::ACCOUNT_TYPES)) {
$object_data = [];
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
$object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor', '@id');
@ -436,12 +440,14 @@ class Receiver
// Create a mostly empty array out of the activity data (instead of the object).
// This way we later don't have to check for the existence of each individual array element.
$object_data = self::processObject($activity, $original_actor);
$object_data['name'] = $type;
$object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@id');
$object_data['object_id'] = $object_id;
$object_data['object_type'] = ''; // Since we don't fetch the object, we don't know the type
} elseif (in_array($type, ['as:Add', 'as:Remove', 'as:Move'])) {
$object_data = [];
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
$object_data['target_id'] = JsonLD::fetchElement($activity, 'as:target', '@id');
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
@ -449,6 +455,7 @@ class Receiver
$object_data['object_content'] = JsonLD::fetchElement($activity['as:object'], 'as:content', '@type');
} else {
$object_data = [];
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
$object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor', '@id');
@ -555,6 +562,7 @@ class Receiver
$user = User::getById(array_key_first($receivers), ['language']);
$l10n = DI::l10n()->withLang($user['language']);
$object_data['name'] = $l10n->t('Chat');
$mail = DBA::selectFirst('mail', ['uri'], ['uid' => array_key_first($receivers), 'title' => $object_data['name']], ['order' => ['id' => true]]);
@ -1237,8 +1245,10 @@ class Receiver
// Exception: The receiver is targetted via "to" or this is a comment
if ((($element != 'as:to') && empty($replyto)) || ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) {
$networks = Protocol::FEDERATED;
$condition = ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
'network' => $networks, 'archive' => false, 'pending' => false, 'uid' => $contact['uid']];
$condition = [
'nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
'network' => $networks, 'archive' => false, 'pending' => false, 'uid' => $contact['uid']
];
// Group posts are only accepted from group contacts
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
@ -1277,6 +1287,7 @@ class Receiver
if (empty($receivers) && !empty($parent['parent-author-link'])) {
$uid = User::getIdForURL($parent['parent-author-link']);
$receivers[$uid] = ['uid' => $uid, 'type' => self::TARGET_BTO];
}
@ -1319,8 +1330,10 @@ class Receiver
*/
private static function getReceiverForActor(array $tags, array $receivers, int $target_type, array $profile): array
{
$basecondition = ['rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
'network' => Protocol::FEDERATED, 'archive' => false, 'pending' => false];
$basecondition = [
'rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
'network' => Protocol::FEDERATED, 'archive' => false, 'pending' => false
];
$condition = DBA::mergeConditions($basecondition, ["`uri-id` = ? AND `uid` != ?", $profile['uri-id'], 0]);
$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
@ -1545,10 +1558,9 @@ class Receiver
continue;
}
$url = JsonLD::fetchElement($emoji['as:icon'], 'as:url', '@id');
$element = [
'name' => JsonLD::fetchElement($emoji, 'as:name', '@value'),
'href' => $url
'href' => JsonLD::fetchElement($emoji['as:icon'], 'as:url', '@id')
];
$emojilist[] = $element;
@ -1847,6 +1859,7 @@ class Receiver
}
$size = (int)JsonLD::fetchElement($url, 'pt:size', '@value');
$attachments[] = ['type' => $filetype, 'mediaType' => $mediatype, 'url' => $href, 'height' => $height, 'size' => $size, 'name' => ''];
} elseif (in_array($mediatype, ['application/x-bittorrent', 'application/x-bittorrent;x-scheme-handler/magnet'])) {
$height = (int)JsonLD::fetchElement($url, 'as:height', '@value');
@ -1909,7 +1922,8 @@ class Receiver
return $object_data;
}
private static function getCapabilities($object) {
private static function getCapabilities($object)
{
$capabilities = [];
foreach (['pixelfed:canAnnounce', 'pixelfed:canLike', 'pixelfed:canReply'] as $element) {
$capabilities_list = JsonLD::fetchElementArray($object['pixelfed:capabilities'], $element, '@id');
@ -1931,6 +1945,7 @@ class Receiver
public static function getObjectDataFromActivity(array $object): array
{
$object_data = [];
$object_data['object_type'] = JsonLD::fetchElement($object, '@type');
$object_data['id'] = JsonLD::fetchElement($object, '@id');
$object_data['reply-to-id'] = JsonLD::fetchElement($object, 'as:inReplyTo', '@id');
@ -2037,9 +2052,15 @@ class Receiver
// Support for quoted posts (Pleroma, Fedibird and Misskey)
$object_data['quote-url'] = JsonLD::fetchElement($object, 'as:quoteUrl', '@id');
if (empty($object_data['quote-url'])) {
$object_data['quote-url'] = JsonLD::fetchElement($object, 'as:quoteUrl', '@value');
}
if (empty($object_data['quote-url'])) {
$object_data['quote-url'] = JsonLD::fetchElement($object, 'fedibird:quoteUri', '@id');
}
if (empty($object_data['quote-url'])) {
$object_data['quote-url'] = JsonLD::fetchElement($object, 'fedibird:quoteUri', '@value');
}
if (empty($object_data['quote-url'])) {
$object_data['quote-url'] = JsonLD::fetchElement($object, 'misskey:_misskey_quote', '@id');
}