diff --git a/src/App.php b/src/App.php index 25770dd78..0d4e8880d 100644 --- a/src/App.php +++ b/src/App.php @@ -631,10 +631,10 @@ class App // ZRL if (!empty($_GET['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend() && !$this->session->getLocalUserId()) { - // Only continue when the given profile link seems valid + // Only continue when the given profile link seems valid. // Valid profile links contain a path with "/profile/" and no query parameters if ((parse_url($_GET['zrl'], PHP_URL_QUERY) == '') && - strstr(parse_url($_GET['zrl'], PHP_URL_PATH), '/profile/')) { + strpos(parse_url($_GET['zrl'], PHP_URL_PATH) ?? '', '/profile/') !== false) { if ($this->session->get('visitor_home') != $_GET['zrl']) { $this->session->set('my_url', $_GET['zrl']); $this->session->set('authenticated', 0); diff --git a/src/Contact/Avatar.php b/src/Contact/Avatar.php index 0cfc8df34..711a8549f 100644 --- a/src/Contact/Avatar.php +++ b/src/Contact/Avatar.php @@ -125,7 +125,7 @@ class Avatar private static function getFilename(string $url): string { - $guid = Item::guidFromUri($url, parse_url($url, PHP_URL_HOST)); + $guid = Item::guidFromUri($url); return substr($guid, 0, 2) . '/' . substr($guid, 3, 2) . '/' . substr($guid, 5, 3) . '/' . substr($guid, 9, 2) .'/' . substr($guid, 11, 2) . '/' . substr($guid, 13, 4). '/' . substr($guid, 18) . '-'; diff --git a/src/Model/APContact.php b/src/Model/APContact.php index 67ce2b66b..fd748f1cd 100644 --- a/src/Model/APContact.php +++ b/src/Model/APContact.php @@ -291,14 +291,11 @@ class APContact return $fetched_contact; } - $parts = parse_url($apcontact['url']); - unset($parts['scheme']); - unset($parts['path']); - if (empty($apcontact['addr'])) { - if (!empty($apcontact['nick']) && is_array($parts)) { - $apcontact['addr'] = $apcontact['nick'] . '@' . str_replace('//', '', Network::unparseURL($parts)); - } else { + try { + $apcontact['addr'] = $apcontact['nick'] . '@' . (new Uri($apcontact['url']))->getAuthority(); + } catch (\Throwable $e) { + Logger::warning('Unable to coerce APContact URL into a UriInterface object', ['url' => $apcontact['url'], 'error' => $e->getMessage()]); $apcontact['addr'] = ''; } } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 873de0890..4a10fd98e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2099,7 +2099,7 @@ class Contact if ($static) { $query_params['static'] = true; } - + return $url . ($guid ?: $cid) . (!empty($query_params) ? '?' . http_build_query($query_params) : ''); } @@ -2675,7 +2675,7 @@ class Contact } $update = false; - $guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url'], parse_url($ret['url'], PHP_URL_HOST)); + $guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url']); // make sure to not overwrite existing values with blank entries except some technical fields $keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl']; diff --git a/src/Model/Item.php b/src/Model/Item.php index ff4791a7c..8b9909f8b 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2034,9 +2034,10 @@ class Item * Posts that are created on this system are using System::createUUID. * Received ActivityPub posts are using Processor::getGUIDByURL. * - * @param string $uri uri of an item entry + * @param string $uri uri of an item entry * @param string|null $host hostname for the GUID prefix * @return string Unique guid + * @throws \Exception */ public static function guidFromUri(string $uri, string $host = null): string { @@ -2047,11 +2048,16 @@ class Item // Remove the scheme to make sure that "https" and "http" doesn't make a difference unset($parsed['scheme']); + $hostPart = $host ?? $parsed['host'] ?? ''; + if (!$hostPart) { + Logger::warning('Empty host GUID part', ['uri' => $uri, 'host' => $host, 'parsed' => $parsed, 'callstack' => System::callstack(10)]); + } + // Glue it together to be able to make a hash from it $host_id = implode('/', $parsed); // Use a mixture of several hashes to provide some GUID like experience - return hash('crc32', $host) . '-'. hash('joaat', $host_id) . '-'. hash('fnv164', $host_id); + return hash('crc32', $hostPart) . '-' . hash('joaat', $host_id) . '-' . hash('fnv164', $host_id); } /** diff --git a/src/Model/Mail.php b/src/Model/Mail.php index 6ccce24ba..9469483d9 100644 --- a/src/Model/Mail.php +++ b/src/Model/Mail.php @@ -59,8 +59,7 @@ class Mail } if (empty($msg['guid'])) { - $host = parse_url($msg['from-url'], PHP_URL_HOST); - $msg['guid'] = Item::guidFromUri($msg['uri'], $host); + $msg['guid'] = Item::guidFromUri($msg['uri'], parse_url($msg['from-url'], PHP_URL_HOST)); } $msg['created'] = (!empty($msg['created']) ? DateTimeFormat::utc($msg['created']) : DateTimeFormat::utcNow()); diff --git a/src/Module/NoScrape.php b/src/Module/NoScrape.php index 8e5850ac0..56f31f2dc 100644 --- a/src/Module/NoScrape.php +++ b/src/Module/NoScrape.php @@ -94,7 +94,7 @@ class NoScrape extends BaseModule // We display the last activity (post or login), reduced to year and week number $last_active = strtotime($owner['last-item']); - if ($last_active < strtotime($owner['last-activity'])) { + if ($owner['last-activity'] && $last_active < strtotime($owner['last-activity'])) { $last_active = strtotime($owner['last-activity']); } $json_info['last-activity'] = date('o-W', $last_active); diff --git a/src/Module/Tos.php b/src/Module/Tos.php index 822aa8813..11cecac44 100644 --- a/src/Module/Tos.php +++ b/src/Module/Tos.php @@ -77,7 +77,7 @@ class Tos extends BaseModule */ protected function content(array $request = []): string { - if (strlen($this->config->get('system', 'singleuser'))) { + if ($this->config->get('system', 'singleuser')) { $this->baseUrl->redirect('profile/' . $this->config->get('system', 'singleuser')); } diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 5beef2ea8..5a8f75e2d 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -687,7 +687,7 @@ class Probe } $parts = parse_url($uri); - if (empty($parts['scheme']) && empty($parts['host']) && !strstr($parts['path'], '@')) { + if (empty($parts['scheme']) && empty($parts['host']) && (empty($parts['path']) || strpos($parts['path'], '@') === false)) { Logger::info('URI was not detectable', ['uri' => $uri]); return []; } diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 8e21a6644..d098ddb69 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -804,7 +804,7 @@ class Processor private static function processContent(array $activity, array $item) { if (!empty($activity['mediatype']) && ($activity['mediatype'] == 'text/markdown')) { - $item['title'] = strip_tags($activity['name']); + $item['title'] = strip_tags($activity['name'] ?? ''); $content = Markdown::toBBCode($activity['content']); } elseif (!empty($activity['mediatype']) && ($activity['mediatype'] == 'text/bbcode')) { $item['title'] = $activity['name']; @@ -1273,8 +1273,11 @@ class Processor foreach ($receivers[$element] as $receiver) { if ($receiver == ActivityPub::PUBLIC_COLLECTION) { $name = Receiver::PUBLIC_COLLECTION; + } elseif ($path = parse_url($receiver, PHP_URL_PATH)) { + $name = trim($path, '/'); } else { - $name = trim(parse_url($receiver, PHP_URL_PATH), '/'); + Logger::warning('Unable to coerce name from receiver', ['receiver' => $receiver]); + $name = ''; } $target = Tag::getTargetType($receiver); diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 4e2bcceaf..893982274 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -209,7 +209,7 @@ class Receiver Logger::notice('No object data found', ['type' => $type, 'object_type' => $object_type, 'object_id' => $object_id, 'actor' => $actor, 'activity' => $activity]); return; } - + if (self::routeActivities($object_data, $type, true)) { Logger::debug('Handled activity', ['type' => $type, 'object_type' => $object_type, 'object_id' => $object_id, 'actor' => $actor]); } else { @@ -1472,7 +1472,7 @@ class Receiver continue; } - $element = ['type' => str_replace('as:', '', JsonLD::fetchElement($tag, '@type')), + $element = ['type' => str_replace('as:', '', JsonLD::fetchElement($tag, '@type') ?? ''), 'href' => JsonLD::fetchElement($tag, 'as:href', '@id'), 'name' => JsonLD::fetchElement($tag, 'as:name', '@value')]; diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index 29b192331..dec463820 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -625,8 +625,8 @@ class Feed $notify = Item::isRemoteSelf($contact, $item); - // Distributed items should have a well formatted URI. - // Additionally we have to avoid conflicts with identical URI between imported feeds and these items. + // Distributed items should have a well-formatted URI. + // Additionally, we have to avoid conflicts with identical URI between imported feeds and these items. if ($notify) { $item['guid'] = Item::guidFromUri($orig_plink, DI::baseUrl()->getHostname()); $item['uri'] = Item::newURI($item['guid']);