Improved user agent string
authorMichael <heluecht@pirati.ca>
Fri, 10 May 2024 09:01:43 +0000 (09:01 +0000)
committerMichael <heluecht@pirati.ca>
Fri, 10 May 2024 09:01:43 +0000 (09:01 +0000)
18 files changed:
src/Content/Text/BBCode.php
src/Model/Photo.php
src/Model/Profile.php
src/Module/Contact/Redir.php
src/Module/Photo.php
src/Network/HTTPClient/Capability/ICanSendHttpRequests.php
src/Network/HTTPClient/Client/HttpClient.php
src/Network/HTTPClient/Client/HttpClientOptions.php
src/Network/HTTPClient/Client/HttpClientRequest.php [new file with mode: 0644]
src/Network/HTTPClient/Factory/HttpClient.php
src/Protocol/DFRN.php
src/Protocol/Diaspora.php
src/Protocol/Salmon.php
src/Util/HTTPSignature.php
src/Util/Network.php
src/Util/ParseUrl.php
src/Worker/OnePoll.php
src/Worker/PubSubPublish.php

index e314aa4..0116ddc 100644 (file)
@@ -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</' . $html . '>', $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</' . $element . '>', $text);
                }
@@ -1706,7 +1711,7 @@ class BBCode
 
                return $text;
        }
-       
+
        private static function convertTablesToHtml(string $text): string
        {
                $text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>', $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
index 360ee16..ee7ec03 100644 (file)
@@ -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();
index 100e747..4daf772 100644 (file)
@@ -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);
index b77ab96..522b0ad 100644 (file)
@@ -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);
index 6bf81ac..2386d51 100644 (file)
@@ -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);
index 91b5acb..fdbc2b1 100644 (file)
@@ -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
index 64d0b0a..a8fb4e1 100644 (file)
@@ -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;
+       }
 }
index fcc8270..bc27039 100644 (file)
@@ -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 (file)
index 0000000..19967a6
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2024, 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\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';
+}
index e0de17c..3a03290 100644 (file)
@@ -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);
        }
 }
index 1f02a7f..7e64ac9 100644 (file)
@@ -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();
index c0c5483..c74c52e 100644 (file)
@@ -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]);
 
index 03e2b8b..7f311a4 100644 (file)
@@ -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;
                }
 
index 095b73e..82ee198 100644 (file)
@@ -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);
index 5ea1552..d6c91d3 100644 (file)
@@ -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) {
index 093a823..5687aea 100644 (file)
@@ -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 {
index 8544f3e..0991e41 100644 (file)
@@ -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()]);
 
index 66c33ca..b685825 100644 (file)
@@ -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) {