diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index e314aa47ce..0116ddc8f5 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -40,6 +40,7 @@ use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Util\Images; use Friendica\Util\Map; use Friendica\Util\Network; @@ -960,7 +961,7 @@ class BBCode $text = DI::cache()->get($cache_key); if (is_null($text)) { - $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]); + $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout'), HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]); if ($curlResult->isSuccess()) { $mimetype = $curlResult->getContentType() ?? ''; } else { @@ -1058,7 +1059,7 @@ class BBCode return $text; } - $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]); + $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout'), HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]); if ($curlResult->isSuccess()) { $mimetype = $curlResult->getContentType() ?? ''; } else { @@ -1355,7 +1356,7 @@ class BBCode $text = self::convertTablesToHtml($text); $text = self::convertSpoilersToHtml($text); $text = self::convertStructuresToHtml($text); - + // We add URL without a surrounding URL at this time, since at a earlier stage it would had been too early, // since the used regular expression won't touch URL inside of BBCode elements, but with the structural ones it should. // At a later stage we won't be able to exclude certain parts of the code. @@ -1365,7 +1366,7 @@ class BBCode } return self::convertSmileysToHtml($text, $simple_html, $for_plaintext); }); - + // Now for some more complex BBCode elements (mostly non standard ones) $text = self::convertAttachmentsToHtml($text, $simple_html, $try_oembed, $uriid); $text = self::convertMapsToHtml($text, $simple_html); @@ -1380,7 +1381,7 @@ class BBCode $text = self::convertIFramesToHtml($text); $text = self::convertMailToHtml($text); $text = self::convertAudioVideoToHtml($text, $simple_html, $try_oembed, $try_oembed_callback); - + // At last, some standard elements. URL has to go last, // since some previous conversions use URL elements. $text = self::convertImagesToHtml($text, $simple_html, $uriid); @@ -1650,10 +1651,12 @@ class BBCode $elements = ['big', 'small']; foreach ($elements as $bbcode) { $text = preg_replace("(\[" . $bbcode . "\](.*?)\[\/" . $bbcode . "\])ism", '$1', $text); - } + } - $elements = ['del' => 's', 'ins' => 'em', 'kbd' => 'code', 'mark' => 'strong', - 'samp' => 'code', 'u' => 'em', 'var' => 'em']; + $elements = [ + 'del' => 's', 'ins' => 'em', 'kbd' => 'code', 'mark' => 'strong', + 'samp' => 'code', 'u' => 'em', 'var' => 'em' + ]; foreach ($elements as $bbcode => $html) { $text = preg_replace("(\[" . $bbcode . "\](.*?)\[\/" . $bbcode . "\])ism", '<' . $html . '>$1', $text); } @@ -1661,8 +1664,10 @@ class BBCode // Several easy to replace HTML elements // @todo add the new elements to the documentation by the end of 2024 so that most systems will support them. - $elements = ['b', 'del', 'em', 'i', 'ins', 'kbd', 'mark', - 's', 'samp', 'small', 'strong', 'sub', 'sup', 'u', 'var']; + $elements = [ + 'b', 'del', 'em', 'i', 'ins', 'kbd', 'mark', + 's', 'samp', 'small', 'strong', 'sub', 'sup', 'u', 'var' + ]; foreach ($elements as $element) { $text = preg_replace("(\[" . $element . "\](.*?)\[\/" . $element . "\])ism", '<' . $element . '>$1', $text); } @@ -1706,7 +1711,7 @@ class BBCode return $text; } - + private static function convertTablesToHtml(string $text): string { $text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '$1', $text); @@ -1875,7 +1880,7 @@ class BBCode $text = self::convertImages($text, $simple_html, $uriid); - return $text; + return $text; } private static function convertCryptToHtml(string $text): string diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 360ee16220..ee7ec03a53 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -32,6 +32,8 @@ use Friendica\Core\Storage\Exception\ReferenceStorageException; use Friendica\Core\Storage\Exception\StorageException; use Friendica\Core\Storage\Type\SystemResource; use Friendica\Network\HTTPClient\Client\HttpClientAccept; +use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Object\Image; use Friendica\Util\DateTimeFormat; use Friendica\Util\Images; @@ -234,7 +236,8 @@ class Photo FROM `photo` WHERE `uid` = ? AND NOT `photo-type` IN (?, ?) $sqlExtra GROUP BY `resource-id` $sqlExtra2", $values - )); + ) + ); } /** @@ -549,7 +552,7 @@ class Photo // get photo to update $photos = self::selectToArray(['backend-class', 'backend-ref'], $conditions); - foreach($photos as $photo) { + foreach ($photos as $photo) { try { $backend_class = DI::storageManager()->getWritableStorageByName($photo['backend-class'] ?? ''); $fields['backend-ref'] = $backend_class->put($image->asString(), $photo['backend-ref']); @@ -580,7 +583,9 @@ class Photo $micro = ''; $photo = DBA::selectFirst( - 'photo', ['resource-id'], ['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR] + 'photo', + ['resource-id'], + ['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR] ); if (!empty($photo['resource-id'])) { $resource_id = $photo['resource-id']; @@ -597,7 +602,7 @@ class Photo $filename = basename($image_url); if (!empty($image_url)) { - $ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE); + $ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE, [HttpClientOptions::REQUEST => HttpClientRequest::MEDIAPROXY]); Logger::debug('Got picture', ['Content-Type' => $ret->getHeader('Content-Type'), 'url' => $image_url]); $img_str = $ret->getBodyString(); $type = $ret->getContentType(); @@ -681,7 +686,9 @@ class Photo } $photo = DBA::selectFirst( - 'photo', ['blurhash'], ['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR] + 'photo', + ['blurhash'], + ['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR] ); return [$image_url, $thumb, $micro, $photo['blurhash']]; @@ -751,7 +758,8 @@ class Photo if (!DI::config()->get('system', 'no_count', false)) { /// @todo This query needs to be renewed. It is really slow // At this time we just store the data in the cache - $albums = DBA::toArray(DBA::p("SELECT COUNT(DISTINCT `resource-id`) AS `total`, `album`, MIN(`created`) AS `created` + $albums = DBA::toArray(DBA::p( + "SELECT COUNT(DISTINCT `resource-id`) AS `total`, `album`, MIN(`created`) AS `created` FROM `photo` WHERE `uid` = ? AND `photo-type` IN (?, ?, ?) $sql_extra GROUP BY `album` ORDER BY `created` DESC", @@ -762,7 +770,8 @@ class Photo )); } else { // This query doesn't do the count and is much faster - $albums = DBA::toArray(DBA::p("SELECT '' AS `total`, `album`, MIN(`created`) AS `created` + $albums = DBA::toArray(DBA::p( + "SELECT '' AS `total`, `album`, MIN(`created`) AS `created` FROM `photo` USE INDEX (`uid_album_scale_created`) WHERE `uid` = ? AND `photo-type` IN (?, ?, ?) $sql_extra GROUP BY `album` ORDER BY `created` DESC", @@ -902,9 +911,11 @@ class Photo */ public static function setPermissionForResource(string $image_rid, int $uid, string $str_contact_allow, string $str_circle_allow, string $str_contact_deny, string $str_circle_deny) { - $fields = ['allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow, - 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny, - 'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false)]; + $fields = [ + 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow, + 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny, + 'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false) + ]; $condition = ['resource-id' => $image_rid, 'uid' => $uid]; Logger::info('Set permissions', ['condition' => $condition, 'permissions' => $fields]); @@ -1046,7 +1057,7 @@ class Photo { $filename = basename($image_url); if (!empty($image_url)) { - $ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE); + $ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE, [HttpClientOptions::REQUEST => HttpClientRequest::MEDIAPROXY]); Logger::debug('Got picture', ['Content-Type' => $ret->getHeader('Content-Type'), 'url' => $image_url]); $img_str = $ret->getBodyString(); $type = $ret->getContentType(); diff --git a/src/Model/Profile.php b/src/Model/Profile.php index 100e747d29..4daf77278a 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -37,6 +37,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Network\HTTPException; use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Protocol\Activity; @@ -781,7 +782,7 @@ class Profile $magic_path = $basepath . '/magic' . '?owa=1&dest=' . $dest . '&' . $addr_request; // We have to check if the remote server does understand /magic without invoking something - $serverret = DI::httpClient()->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML]); + $serverret = DI::httpClient()->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML, HttpClientOptions::REQUEST => HttpClientRequest::MAGICAUTH]); if ($serverret->isSuccess()) { Logger::info('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path); System::externalRedirect($magic_path); diff --git a/src/Module/Contact/Redir.php b/src/Module/Contact/Redir.php index b77ab96172..522b0ad2b7 100644 --- a/src/Module/Contact/Redir.php +++ b/src/Module/Contact/Redir.php @@ -30,6 +30,7 @@ use Friendica\Module\Response; use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Network\HTTPException; use Friendica\Util\Profiler; use Friendica\Util\Strings; @@ -107,7 +108,7 @@ class Redir extends \Friendica\BaseModule } // Test for magic auth on the target system - $response = $this->httpClient->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML]); + $response = $this->httpClient->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML, HttpClientOptions::REQUEST => HttpClientRequest::MAGICAUTH]); if ($response->isSuccess()) { $separator = strpos($target_url, '?') ? '&' : '?'; $target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url); diff --git a/src/Module/Photo.php b/src/Module/Photo.php index 6bf81ac7cb..2386d51548 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -38,6 +38,7 @@ use Friendica\Core\Worker; use Friendica\Model\User; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Network\HTTPException; use Friendica\Network\HTTPException\NotModifiedException; use Friendica\Object\Image; @@ -140,7 +141,7 @@ class Photo extends BaseApi case 'scaled_full': $scale = 1; break; - } + } } $photo = MPhoto::getPhoto($photoid, $scale, self::getCurrentUserID()); @@ -166,7 +167,7 @@ class Photo extends BaseApi if (empty($imgdata) && empty($photo['blurhash'])) { throw new HTTPException\NotFoundException(); } elseif (empty($imgdata) && !empty($photo['blurhash'])) { - $image = New Image('', image_type_to_mime_type(IMAGETYPE_WEBP)); + $image = new Image('', image_type_to_mime_type(IMAGETYPE_WEBP)); $image->getFromBlurHash($photo['blurhash'], $photo['width'], $photo['height']); $imgdata = $image->asString(); $mimetype = $image->getType(); @@ -243,10 +244,12 @@ class Photo extends BaseApi $rest = $total - ($fetch + $data + $checksum + $output); if (!is_null($scale) && ($scale < 4)) { - Logger::debug('Performance:', ['scale' => $scale, 'resource' => $photo['resource-id'], + Logger::debug('Performance:', [ + 'scale' => $scale, 'resource' => $photo['resource-id'], 'total' => number_format($total, 3), 'fetch' => number_format($fetch, 3), 'data' => number_format($data, 3), 'checksum' => number_format($checksum, 3), - 'output' => number_format($output, 3), 'rest' => number_format($rest, 3)]); + 'output' => number_format($output, 3), 'rest' => number_format($rest, 3) + ]); } System::exit(); @@ -262,7 +265,7 @@ class Photo extends BaseApi */ private static function getPhotoById(int $id, string $type, int $customsize) { - switch($type) { + switch ($type) { case 'preview': $media = DBA::selectFirst('post-media', ['preview', 'url', 'preview-height', 'preview-width', 'height', 'width', 'mimetype', 'type', 'uri-id', 'blurhash'], ['id' => $id]); if (empty($media)) { @@ -366,7 +369,7 @@ class Photo extends BaseApi $update = in_array($contact['network'], Protocol::FEDERATED) && !$contact['failed'] && ((time() - strtotime($contact['updated']) > 86400)); if ($update) { - $curlResult = DI::httpClient()->head($url, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::IMAGE]); + $curlResult = DI::httpClient()->head($url, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::IMAGE, HttpClientOptions::REQUEST => HttpClientRequest::MEDIAPROXY]); $update = !$curlResult->isSuccess() && ($curlResult->getReturnCode() == 404); Logger::debug('Got return code for avatar', ['return code' => $curlResult->getReturnCode(), 'cid' => $id, 'url' => $contact['url'], 'avatar' => $url]); } @@ -384,12 +387,13 @@ class Photo extends BaseApi if (!empty($mimetext) && ($mime[0] != 'image') && ($mimetext != 'application/octet-stream')) { Logger::info('Unexpected Content-Type', ['mime' => $mimetext, 'url' => $url]); $mimetext = ''; - } if (!empty($mimetext)) { + } + if (!empty($mimetext)) { Logger::debug('Expected Content-Type', ['mime' => $mimetext, 'url' => $url]); } } if (empty($mimetext) && !empty($contact['blurhash'])) { - $image = New Image('', image_type_to_mime_type(IMAGETYPE_WEBP)); + $image = new Image('', image_type_to_mime_type(IMAGETYPE_WEBP)); $image->getFromBlurHash($contact['blurhash'], $customsize, $customsize); return MPhoto::createPhotoForImageData($image->asString()); } elseif (empty($mimetext)) { @@ -420,7 +424,7 @@ class Photo extends BaseApi return self::getBannerForUser($header_uid); } - If (($contact['uid'] != 0) && empty($contact['header'])) { + if (($contact['uid'] != 0) && empty($contact['header'])) { $contact = Contact::getByURL($contact['url'], false, $fields); } if (!empty($contact['header'])) { @@ -450,7 +454,7 @@ class Photo extends BaseApi if (empty($photo)) { $contact = DBA::selectFirst('contact', [], ['uid' => $id, 'self' => true]) ?: []; - switch($type) { + switch ($type) { case 'profile': case 'custom': $default = Contact::getDefaultAvatar($contact, Proxy::SIZE_SMALL); diff --git a/src/Network/HTTPClient/Capability/ICanSendHttpRequests.php b/src/Network/HTTPClient/Capability/ICanSendHttpRequests.php index 91b5acb795..fdbc2b18a1 100644 --- a/src/Network/HTTPClient/Capability/ICanSendHttpRequests.php +++ b/src/Network/HTTPClient/Capability/ICanSendHttpRequests.php @@ -95,10 +95,11 @@ interface ICanSendHttpRequests * @param mixed $params POST variables (if an array is passed, it will automatically set as formular parameters) * @param array $headers HTTP headers * @param int $timeout The timeout in seconds, default system config value or 60 seconds + * @param string $request The type of the request. This is set in the user agent string * * @return ICanHandleHttpResponses The content */ - public function post(string $url, $params, array $headers = [], int $timeout = 0): ICanHandleHttpResponses; + public function post(string $url, $params, array $headers = [], int $timeout = 0, string $request = ''): ICanHandleHttpResponses; /** * Sends an HTTP request to a given url diff --git a/src/Network/HTTPClient/Client/HttpClient.php b/src/Network/HTTPClient/Client/HttpClient.php index 64d0b0a235..a8fb4e13c4 100644 --- a/src/Network/HTTPClient/Client/HttpClient.php +++ b/src/Network/HTTPClient/Client/HttpClient.php @@ -21,6 +21,7 @@ namespace Friendica\Network\HTTPClient\Client; +use Friendica\App; use Friendica\Core\System; use Friendica\Network\HTTPClient\Response\CurlResult; use Friendica\Network\HTTPClient\Response\GuzzleResponse; @@ -51,13 +52,16 @@ class HttpClient implements ICanSendHttpRequests private $client; /** @var URLResolver */ private $resolver; + /** @var App\BaseURL */ + private $baseUrl; - public function __construct(LoggerInterface $logger, Profiler $profiler, Client $client, URLResolver $resolver) + public function __construct(LoggerInterface $logger, Profiler $profiler, Client $client, URLResolver $resolver, App\BaseURL $baseUrl) { $this->logger = $logger; $this->profiler = $profiler; $this->client = $client; $this->resolver = $resolver; + $this->baseUrl = $baseUrl; } /** @@ -73,7 +77,7 @@ class HttpClient implements ICanSendHttpRequests throw new \InvalidArgumentException('Unable to retrieve the host in URL: ' . $url); } - if(!filter_var($host, FILTER_VALIDATE_IP) && !@dns_get_record($host . '.', DNS_A) && !@dns_get_record($host . '.', DNS_AAAA)) { + if (!filter_var($host, FILTER_VALIDATE_IP) && !@dns_get_record($host . '.', DNS_A) && !@dns_get_record($host . '.', DNS_AAAA)) { $this->logger->debug('URL cannot be resolved.', ['url' => $url]); $this->profiler->stopRecording(); return CurlResult::createErrorCurl($this->logger, $url); @@ -115,7 +119,7 @@ class HttpClient implements ICanSendHttpRequests $conf[RequestOptions::COOKIES] = $jar; } - $headers = []; + $headers = ['User-Agent' => $this->getUserAgent($opts[HttpClientOptions::REQUEST] ?? '')]; if (!empty($opts[HttpClientOptions::ACCEPT_CONTENT])) { $headers['Accept'] = $opts[HttpClientOptions::ACCEPT_CONTENT]; @@ -153,8 +157,10 @@ class HttpClient implements ICanSendHttpRequests } $conf[RequestOptions::ON_HEADERS] = function (ResponseInterface $response) use ($opts) { - if (!empty($opts[HttpClientOptions::CONTENT_LENGTH]) && - (int)$response->getHeaderLine('Content-Length') > $opts[HttpClientOptions::CONTENT_LENGTH]) { + if ( + !empty($opts[HttpClientOptions::CONTENT_LENGTH]) && + (int)$response->getHeaderLine('Content-Length') > $opts[HttpClientOptions::CONTENT_LENGTH] + ) { throw new TransferException('The file is too big!'); } }; @@ -172,8 +178,10 @@ class HttpClient implements ICanSendHttpRequests $response = $this->client->request($method, $url, $conf); return new GuzzleResponse($response, $url); } catch (TransferException $exception) { - if ($exception instanceof RequestException && - $exception->hasResponse()) { + if ( + $exception instanceof RequestException && + $exception->hasResponse() + ) { return new GuzzleResponse($exception->getResponse(), $url, $exception->getCode(), ''); } else { return new CurlResult($this->logger, $url, '', ['http_code' => 500], $exception->getCode(), ''); @@ -209,7 +217,7 @@ class HttpClient implements ICanSendHttpRequests /** * {@inheritDoc} */ - public function post(string $url, $params, array $headers = [], int $timeout = 0): ICanHandleHttpResponses + public function post(string $url, $params, array $headers = [], int $timeout = 0, string $request = ''): ICanHandleHttpResponses { $opts = []; @@ -227,6 +235,10 @@ class HttpClient implements ICanSendHttpRequests $opts[HttpClientOptions::TIMEOUT] = $timeout; } + if (!empty($request)) { + $opts[HttpClientOptions::REQUEST] = $request; + } + return $this->request('post', $url, $opts); } @@ -255,6 +267,7 @@ class HttpClient implements ICanSendHttpRequests $url = trim($url, "'"); + $this->resolver->setUserAgent($this->getUserAgent(HttpClientRequest::RESOLVER)); $urlResult = $this->resolver->resolveURL($url); if ($urlResult->didErrorOccur()) { @@ -288,4 +301,15 @@ class HttpClient implements ICanSendHttpRequests ] ); } + + private function getUserAgent(string $type = ''): string + { + // @see https://developers.whatismybrowser.com/learn/browser-detection/user-agents/user-agent-best-practices + $userAgent = App::PLATFORM . '/' . App::VERSION . ' DatabaseVersion/' . DB_UPDATE_VERSION; + if ($type != '') { + $userAgent .= ' Request/' . $type; + } + $userAgent .= ' +' . $this->baseUrl; + return $userAgent; + } } diff --git a/src/Network/HTTPClient/Client/HttpClientOptions.php b/src/Network/HTTPClient/Client/HttpClientOptions.php index fcc8270bd1..bc27039db2 100644 --- a/src/Network/HTTPClient/Client/HttpClientOptions.php +++ b/src/Network/HTTPClient/Client/HttpClientOptions.php @@ -52,7 +52,10 @@ class HttpClientOptions * content_length: (int) maximum File content length */ const CONTENT_LENGTH = 'content_length'; - + /** + * Request: (string) Type of request (ActivityPub, Diaspora, server discovery, ...) + */ + const REQUEST = 'request'; /** * verify: (bool|string, default=true) Describes the SSL certificate */ diff --git a/src/Network/HTTPClient/Client/HttpClientRequest.php b/src/Network/HTTPClient/Client/HttpClientRequest.php new file mode 100644 index 0000000000..19967a660f --- /dev/null +++ b/src/Network/HTTPClient/Client/HttpClientRequest.php @@ -0,0 +1,39 @@ +. + * + */ + +namespace Friendica\Network\HTTPClient\Client; + +/** + * This class contains a list of request types that are set in the user agent string + */ +class HttpClientRequest +{ + public const ACTIVITYPUB = 'ActivityPub/1'; + public const CONTENTTYPE = 'ContentTypeChecker/1'; + public const DFRN = 'DFRN/1'; + public const DIASPORA = 'Diaspora/1'; + public const MAGICAUTH = 'MagicAuth/1'; + public const MEDIAPROXY = 'MediaProxy/1'; + public const SALMON = 'Salmon/1'; + public const PUBSUB = 'PubSub/1'; + public const RESOLVER = 'URLResolver/1'; + public const VERIFIER = 'URLVerifier/1'; +} diff --git a/src/Network/HTTPClient/Factory/HttpClient.php b/src/Network/HTTPClient/Factory/HttpClient.php index e0de17c75a..3a03290383 100644 --- a/src/Network/HTTPClient/Factory/HttpClient.php +++ b/src/Network/HTTPClient/Factory/HttpClient.php @@ -86,12 +86,6 @@ class HttpClient extends BaseFactory $logger->info('Curl redirect.', ['url' => $request->getUri(), 'to' => $uri, 'method' => $request->getMethod()]); }; - $userAgent = App::PLATFORM . " '" . - App::CODENAME . "' " . - App::VERSION . '-' . - DB_UPDATE_VERSION . '; ' . - $this->baseUrl; - $guzzle = new GuzzleHttp\Client([ RequestOptions::ALLOW_REDIRECTS => [ 'max' => 8, @@ -112,22 +106,19 @@ class HttpClient extends BaseFactory // but it can be overridden RequestOptions::VERIFY => (bool)$this->config->get('system', 'verifyssl'), RequestOptions::PROXY => $proxy, - RequestOptions::HEADERS => [ - 'User-Agent' => $userAgent, - ], + RequestOptions::HEADERS => [], 'handler' => $handlerStack ?? HandlerStack::create(), ]); $resolver = new URLResolver(); - $resolver->setUserAgent($userAgent); $resolver->setMaxRedirects(10); $resolver->setRequestTimeout(10); // if the file is too large then exit $resolver->setMaxResponseDataSize($this->config->get('performance', 'max_response_data_size', 1000000)); // Designate a temporary file that will store cookies during the session. // Some websites test the browser for cookie support, so this enhances results. - $resolver->setCookieJar(System::getTempPath() .'/resolver-cookie-' . Strings::getRandomName(10)); + $resolver->setCookieJar(System::getTempPath() . '/resolver-cookie-' . Strings::getRandomName(10)); - return new Client\HttpClient($logger, $this->profiler, $guzzle, $resolver); + return new Client\HttpClient($logger, $this->profiler, $guzzle, $resolver, $this->baseUrl); } } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 1f02a7f267..7e64ac9bd3 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -44,6 +44,7 @@ use Friendica\Model\Post; use Friendica\Model\Profile; use Friendica\Model\Tag; use Friendica\Model\User; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Network\HTTPException; use Friendica\Network\Probe; use Friendica\Util\Crypto; @@ -1009,7 +1010,7 @@ class DFRN $content_type = ($public_batch ? 'application/magic-envelope+xml' : 'application/json'); - $postResult = DI::httpClient()->post($dest_url, $envelope, ['Content-Type' => $content_type]); + $postResult = DI::httpClient()->post($dest_url, $envelope, ['Content-Type' => $content_type], 0, HttpClientRequest::DFRN); $xml = $postResult->getBodyString(); $curl_stat = $postResult->getReturnCode(); diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index c0c5483539..c74c52e0a6 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -41,6 +41,7 @@ use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Model\User; use Friendica\Network\HTTPClient\Client\HttpClientAccept; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Network\HTTPException; use Friendica\Network\Probe; use Friendica\Protocol\Delivery; @@ -2937,7 +2938,7 @@ class Diaspora * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function transmit(array $owner, array $contact, string $envelope, bool $public_batch, string $guid = ''): int + private static function transmit(array $contact, string $envelope, bool $public_batch, string $guid = ''): int { $enabled = intval(DI::config()->get('system', 'diaspora_enabled')); if (!$enabled) { @@ -2968,7 +2969,7 @@ class Diaspora if (!intval(DI::config()->get('system', 'diaspora_test'))) { $content_type = (($public_batch) ? 'application/magic-envelope+xml' : 'application/json'); - $postResult = DI::httpClient()->post($dest_url . '/', $envelope, ['Content-Type' => $content_type]); + $postResult = DI::httpClient()->post($dest_url . '/', $envelope, ['Content-Type' => $content_type], 0, HttpClientRequest::DIASPORA); $return_code = $postResult->getReturnCode(); } else { Logger::notice('test_mode'); @@ -3042,7 +3043,7 @@ class Diaspora $envelope = self::buildMessage($msg, $owner, $contact, $owner['uprvkey'], $pubkey ?? '', $public_batch); - $return_code = self::transmit($owner, $contact, $envelope, $public_batch, $guid); + $return_code = self::transmit($contact, $envelope, $public_batch, $guid); Logger::info('Transmitted message', ['owner' => $owner['uid'], 'target' => $contact['addr'], 'type' => $type, 'guid' => $guid, 'result' => $return_code]); diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php index 03e2b8b782..7f311a4084 100644 --- a/src/Protocol/Salmon.php +++ b/src/Protocol/Salmon.php @@ -24,6 +24,7 @@ namespace Friendica\Protocol; use Friendica\Core\Logger; use Friendica\DI; use Friendica\Network\HTTPClient\Client\HttpClientAccept; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Network\Probe; use Friendica\Protocol\Salmon\Format\Magic; use Friendica\Util\Crypto; @@ -49,7 +50,7 @@ class Salmon { $ret = []; - Logger::info('Fetching salmon key for '.$uri); + Logger::info('Fetching salmon key for ' . $uri); $arr = Probe::lrdd($uri); @@ -67,7 +68,7 @@ class Salmon // If it's inline, parse it - otherwise get the key if (count($ret) > 0) { - for ($x = 0; $x < count($ret); $x ++) { + for ($x = 0; $x < count($ret); $x++) { if (substr($ret[$x], 0, 5) === 'data:') { if (strstr($ret[$x], ',')) { $ret[$x] = substr($ret[$x], strpos($ret[$x], ',') + 1); @@ -120,12 +121,15 @@ class Salmon } if (!$owner['sprvkey']) { - Logger::notice(sprintf("user '%s' (%d) does not have a salmon private key. Send failed.", - $owner['name'], $owner['uid'])); + Logger::notice(sprintf( + "user '%s' (%d) does not have a salmon private key. Send failed.", + $owner['name'], + $owner['uid'] + )); return -1; } - Logger::info('slapper called for '.$url.'. Data: ' . $slap); + Logger::info('slapper called for ' . $url . '. Data: ' . $slap); // create a magic envelope @@ -166,7 +170,7 @@ class Salmon $postResult = DI::httpClient()->post($url, $salmon, [ 'Content-type' => 'application/magic-envelope+xml', 'Content-length' => strlen($salmon), - ]); + ], 0, HttpClientRequest::SALMON); $return_code = $postResult->getReturnCode(); @@ -193,7 +197,7 @@ class Salmon $postResult = DI::httpClient()->post($url, $salmon, [ 'Content-type' => 'application/magic-envelope+xml', 'Content-length' => strlen($salmon), - ]); + ], 0, HttpClientRequest::SALMON); $return_code = $postResult->getReturnCode(); } @@ -217,13 +221,14 @@ class Salmon // slap them $postResult = DI::httpClient()->post($url, $salmon, [ 'Content-type' => 'application/magic-envelope+xml', - 'Content-length' => strlen($salmon)]); + 'Content-length' => strlen($salmon) + ], 0, HttpClientRequest::SALMON); $return_code = $postResult->getReturnCode(); } - Logger::info('slapper for '.$url.' returned ' . $return_code); + Logger::info('slapper for ' . $url . ' returned ' . $return_code); - if (! $return_code) { + if (!$return_code) { return -1; } diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index 095b73ea7c..82ee1981f1 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -34,6 +34,7 @@ use Friendica\Model\User; use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; /** * Implements HTTP Signatures per draft-cavage-http-signatures-07. @@ -69,7 +70,7 @@ class HTTPSignature // Decide if $data arrived via controller submission or curl. $headers = []; - $headers['(request-target)'] = strtolower(DI::args()->getMethod()).' '.$_SERVER['REQUEST_URI']; + $headers['(request-target)'] = strtolower(DI::args()->getMethod()) . ' ' . $_SERVER['REQUEST_URI']; foreach ($_SERVER as $k => $v) { if (strpos($k, 'HTTP_') === 0) { @@ -293,7 +294,7 @@ class HTTPSignature 'Host' => $host ]; - $signed_data = "(request-target): post " . $path . "\ndate: ". $date . "\ncontent-length: " . $content_length . "\ndigest: " . $digest . "\nhost: " . $host; + $signed_data = "(request-target): post " . $path . "\ndate: " . $date . "\ncontent-length: " . $content_length . "\ndigest: " . $digest . "\nhost: " . $host; $signature = base64_encode(Crypto::rsaSign($signed_data, $owner['uprvkey'], 'sha256')); @@ -301,7 +302,7 @@ class HTTPSignature $headers['Content-Type'] = 'application/activity+json'; - $postResult = DI::httpClient()->post($target, $content, $headers, DI::config()->get('system', 'curl_timeout')); + $postResult = DI::httpClient()->post($target, $content, $headers, DI::config()->get('system', 'curl_timeout'), HttpClientRequest::ACTIVITYPUB); $return_code = $postResult->getReturnCode(); Logger::info('Transmit to ' . $target . ' returned ' . $return_code); @@ -508,7 +509,7 @@ class HTTPSignature $header['Date'] = $date; $header['Host'] = $host; - $signed_data = "(request-target): get " . $path . "\ndate: ". $date . "\nhost: " . $host; + $signed_data = "(request-target): get " . $path . "\ndate: " . $date . "\nhost: " . $host; $signature = base64_encode(Crypto::rsaSign($signed_data, $owner['uprvkey'], 'sha256')); @@ -517,6 +518,7 @@ class HTTPSignature $curl_opts = $opts; $curl_opts[HttpClientOptions::HEADERS] = $header; + $curl_opts[HttpClientOptions::REQUEST] = HttpClientRequest::ACTIVITYPUB; if (!empty($opts['nobody'])) { $curlResult = DI::httpClient()->head($request, $curl_opts); diff --git a/src/Util/Network.php b/src/Util/Network.php index 5ea1552920..d6c91d317d 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -27,6 +27,7 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Network\HTTPException\NotModifiedException; use GuzzleHttp\Psr7\Uri; use Psr\Http\Message\UriInterface; @@ -78,7 +79,8 @@ class Network } if (in_array(parse_url($url, PHP_URL_SCHEME), ['https', 'http'])) { - $options = [HttpClientOptions::VERIFY => true, HttpClientOptions::TIMEOUT => $xrd_timeout]; + $options = [HttpClientOptions::VERIFY => true, HttpClientOptions::TIMEOUT => $xrd_timeout, + HttpClientOptions::REQUEST => HttpClientRequest::VERIFIER]; try { $curlResult = DI::httpClient()->head($url, $options); } catch (\Exception $e) { diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php index 093a8233d0..5687aea3c5 100644 --- a/src/Util/ParseUrl.php +++ b/src/Util/ParseUrl.php @@ -33,6 +33,7 @@ use Friendica\DI; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPException; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; /** * Get information about a given URL @@ -64,9 +65,9 @@ class ParseUrl public static function getContentType(string $url, string $accept = HttpClientAccept::DEFAULT, int $timeout = 0): array { if (!empty($timeout)) { - $options = [HttpClientOptions::TIMEOUT => $timeout]; + $options = [HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]; } else { - $options = []; + $options = [HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]; } try { diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index 8544f3e783..0991e4157a 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -33,6 +33,7 @@ use Friendica\Model\Post; use Friendica\Model\User; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Email; @@ -487,7 +488,7 @@ class OnePoll Contact::update(['hub-verify' => $verify_token], ['id' => $contact['id']]); } - $postResult = DI::httpClient()->post($url, $params); + $postResult = DI::httpClient()->post($url, $params, [], 0, HttpClientRequest::PUBSUB); Logger::info('Hub subscription done', ['result' => $postResult->getReturnCode()]); diff --git a/src/Worker/PubSubPublish.php b/src/Worker/PubSubPublish.php index 66c33ca71a..b68582591c 100644 --- a/src/Worker/PubSubPublish.php +++ b/src/Worker/PubSubPublish.php @@ -25,6 +25,7 @@ use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\PushSubscriber; +use Friendica\Network\HTTPClient\Client\HttpClientRequest; use Friendica\Protocol\OStatus; class PubSubPublish @@ -73,14 +74,17 @@ class PubSubPublish $headers = [ 'Content-type' => 'application/atom+xml', - 'Link' => sprintf('<%s>;rel=hub,<%s>;rel=self', - DI::baseUrl() . '/pubsubhubbub/' . $subscriber['nickname'], - $subscriber['topic']), - 'X-Hub-Signature' => 'sha1=' . $hmac_sig]; + 'Link' => sprintf( + '<%s>;rel=hub,<%s>;rel=self', + DI::baseUrl() . '/pubsubhubbub/' . $subscriber['nickname'], + $subscriber['topic'] + ), + 'X-Hub-Signature' => 'sha1=' . $hmac_sig + ]; Logger::debug('POST', ['headers' => $headers, 'params' => $params]); - $postResult = DI::httpClient()->post($subscriber['callback_url'], $params, $headers); + $postResult = DI::httpClient()->post($subscriber['callback_url'], $params, $headers, 0, HttpClientRequest::PUBSUB); $ret = $postResult->getReturnCode(); if ($ret >= 200 && $ret <= 299) {