diff --git a/bin/console.php b/bin/console.php index a20f3f7e025..88382e1665c 100755 --- a/bin/console.php +++ b/bin/console.php @@ -13,6 +13,9 @@ if (php_sapi_name() !== 'cli') { exit(); } +// Ensure that te console is executed from the base path of the installation +chdir(dirname(__DIR__)); + require dirname(__DIR__) . '/vendor/autoload.php'; $container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__)); diff --git a/composer.json b/composer.json index ac1f5c9bc9e..99733365905 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "friendica/json-ld": "^1.0", "geekwright/po": "^2.0", "guzzlehttp/guzzle": "^7", - "guzzlehttp/oauth-subscriber": "^0.6", + "guzzlehttp/oauth-subscriber": "^0.8", "kornrunner/blurhash": "^1.2", "league/html-to-markdown": "^4.8", "level-2/dice": "^4", @@ -67,6 +67,7 @@ "patrickschur/language-detection": "^5.0.0", "pear/console_table": "^1.3", "phpseclib/phpseclib": "^3.0", + "phrity/websocket": "^1.7", "pragmarx/google2fa": "^5.0", "pragmarx/recovery": "^0.2", "psr/clock": "^1.0", @@ -76,7 +77,6 @@ "seld/cli-prompt": "^1.0", "smarty/smarty": "^4", "symfony/event-dispatcher": "^5.4", - "textalk/websocket": "^1.6", "ua-parser/uap-php": "^3.9", "xemlock/htmlpurifier-html5": "^0.1.11" }, diff --git a/composer.lock b/composer.lock index bae30155dd9..fb9fadddbd4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "32af97f73ec49df2a6cfe98f11bc1d60", + "content-hash": "897b878d6db24b9a6437bd9f971478be", "packages": [ { "name": "asika/simple-console", @@ -893,22 +893,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.9.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -919,9 +919,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -997,6 +997,10 @@ "rest", "web service" ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -1011,37 +1015,39 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2025-03-27T13:37:11+00:00" }, { "name": "guzzlehttp/oauth-subscriber", - "version": "0.6.0", + "version": "0.8.1", "source": { "type": "git", "url": "https://github.com/guzzle/oauth-subscriber.git", - "reference": "8d6cab29f8397e5712d00a383eeead36108a3c1f" + "reference": "92b619b03bd21396e51c62e6bce83467d2ce8f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/oauth-subscriber/zipball/8d6cab29f8397e5712d00a383eeead36108a3c1f", - "reference": "8d6cab29f8397e5712d00a383eeead36108a3c1f", + "url": "https://api.github.com/repos/guzzle/oauth-subscriber/zipball/92b619b03bd21396e51c62e6bce83467d2ce8f53", + "reference": "92b619b03bd21396e51c62e6bce83467d2ce8f53", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "^6.5|^7.2", - "guzzlehttp/psr7": "^1.7|^2.0", - "php": ">=5.5.0" + "guzzlehttp/guzzle": "^7.9", + "guzzlehttp/psr7": "^2.7", + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.0|^9.3.3" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" }, "suggest": { "ext-openssl": "Required to sign using RSA-SHA1" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "0.6-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { @@ -1054,32 +1060,64 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" } ], "description": "Guzzle OAuth 1.0 subscriber", - "homepage": "http://guzzlephp.org/", "keywords": [ "Guzzle", "oauth" ], - "time": "2021-07-13T12:01:32+00:00" + "support": { + "issues": "https://github.com/guzzle/oauth-subscriber/issues", + "source": "https://github.com/guzzle/oauth-subscriber/tree/0.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/oauth-subscriber", + "type": "tidelift" + } + ], + "time": "2025-01-06T19:15:59+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", "shasum": "" }, "require": { @@ -1087,7 +1125,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", "extra": { @@ -1131,6 +1169,10 @@ "keywords": [ "promise" ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.2.0" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -1145,20 +1187,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2025-03-27T13:27:01+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", "shasum": "" }, "require": { @@ -1173,8 +1215,8 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1243,6 +1285,10 @@ "uri", "url" ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.1" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -1257,7 +1303,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2025-03-27T12:30:47+00:00" }, { "name": "kornrunner/blurhash", @@ -2814,6 +2860,66 @@ ], "time": "2024-03-03T02:14:58+00:00" }, + { + "name": "phrity/net-stream", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-net-stream.git", + "reference": "9105931b65ad90c75f4885a40b268b0f65802e3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-net-stream/zipball/9105931b65ad90c75f4885a40b268b0f65802e3e", + "reference": "9105931b65ad90c75f4885a40b268b0f65802e3e", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "phrity/util-errorhandler": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 | ^2.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^9.0 | ^10.0", + "phrity/net-uri": "^1.1", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Phrity\\Net\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Socket stream classes implementing PSR-7 Stream and PSR-17 StreamFactory", + "homepage": "https://phrity.sirn.se/net-stream", + "keywords": [ + "Socket", + "client", + "psr-17", + "psr-7", + "server", + "stream", + "stream factory" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-net-stream/issues", + "source": "https://github.com/sirn-se/phrity-net-stream/tree/1.3.0" + }, + "time": "2023-10-22T10:47:03+00:00" + }, { "name": "phrity/net-uri", "version": "1.3.0", @@ -2912,6 +3018,67 @@ ], "time": "2024-09-12T06:49:16+00:00" }, + { + "name": "phrity/websocket", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/websocket-php.git", + "reference": "8a525da4457b599ab1960f24183f25626c96ce3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/websocket-php/zipball/8a525da4457b599ab1960f24183f25626c96ce3c", + "reference": "8a525da4457b599ab1960f24183f25626c96ce3c", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "phrity/net-stream": "^1.2", + "phrity/net-uri": "^1.2", + "phrity/util-errorhandler": "^1.0", + "psr/http-message": "^1.1 | ^2.0", + "psr/log": "^1.0 | ^2.0 | ^3.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^9.0 | ^10.0", + "phrity/net-mock": "^1.3", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "WebSocket client and server", + "homepage": "https://phrity.sirn.se/websocket", + "keywords": [ + "client", + "server", + "websocket" + ], + "support": { + "issues": "https://github.com/sirn-se/websocket-php/issues", + "source": "https://github.com/sirn-se/websocket-php/tree/1.7.3" + }, + "time": "2024-05-31T13:43:32+00:00" + }, { "name": "pragmarx/google2fa", "version": "v5.0.0", @@ -3333,24 +3500,27 @@ "psr", "psr-18" ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", + "php": ">=7.1", "psr/http-message": "^1.0 || ^2.0" }, "type": "library", @@ -3374,7 +3544,7 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -3385,7 +3555,10 @@ "request", "response" ], - "time": "2023-04-10T20:10:41+00:00" + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", @@ -3435,6 +3608,9 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, "time": "2023-04-04T09:50:52+00:00" }, { @@ -3525,6 +3701,10 @@ } ], "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, "time": "2019-03-08T08:55:37+00:00" }, { @@ -3580,16 +3760,16 @@ }, { "name": "smarty/smarty", - "version": "v4.5.1", + "version": "v4.5.3", "source": { "type": "git", "url": "https://github.com/smarty-php/smarty.git", - "reference": "42b869e3a098b1c8ee07922ccded0e5a5dceadcd" + "reference": "9fc96a13dbaf546c3d7bcf95466726578cd4e0fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/smarty-php/smarty/zipball/42b869e3a098b1c8ee07922ccded0e5a5dceadcd", - "reference": "42b869e3a098b1c8ee07922ccded0e5a5dceadcd", + "url": "https://api.github.com/repos/smarty-php/smarty/zipball/9fc96a13dbaf546c3d7bcf95466726578cd4e0fa", + "reference": "9fc96a13dbaf546c3d7bcf95466726578cd4e0fa", "shasum": "" }, "require": { @@ -3637,7 +3817,12 @@ "keywords": [ "templating" ], - "time": "2024-03-18T14:19:07+00:00" + "support": { + "forum": "https://github.com/smarty-php/smarty/discussions", + "issues": "https://github.com/smarty-php/smarty/issues", + "source": "https://github.com/smarty-php/smarty/tree/v4.5.3" + }, + "time": "2024-05-28T21:46:01+00:00" }, { "name": "spomky-labs/base64url", @@ -4076,57 +4261,6 @@ ], "time": "2024-09-09T11:45:10+00:00" }, - { - "name": "textalk/websocket", - "version": "1.6.3", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "67de79745b1a357caf812bfc44e0abf481cee012" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/67de79745b1a357caf812bfc44e0abf481cee012", - "reference": "67de79745b1a357caf812bfc44e0abf481cee012", - "shasum": "" - }, - "require": { - "php": "^7.4 | ^8.0", - "phrity/net-uri": "^1.0", - "phrity/util-errorhandler": "^1.0", - "psr/http-message": "^1.0", - "psr/log": "^1.0 | ^2.0 | ^3.0" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.6.3" - }, - "time": "2022-11-07T18:59:33+00:00" - }, { "name": "ua-parser/uap-php", "version": "v3.9.14", diff --git a/mod/message.php b/mod/message.php index 6ee7a8745ad..f3a231601c6 100644 --- a/mod/message.php +++ b/mod/message.php @@ -1,4 +1,5 @@ 'm', ]; - $tpl = Renderer::getMarkupTemplate('message_side.tpl'); + $tpl = Renderer::getMarkupTemplate('message_side.tpl'); DI::page()['aside'] = Renderer::replaceMacros($tpl, [ '$tabs' => $tabs, '$new' => $new, @@ -61,7 +62,7 @@ function message_post() $body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : ''; $recipient = !empty($_REQUEST['recipient']) ? intval($_REQUEST['recipient']) : 0; - $ret = Mail::send($sender_id, $recipient, $body, $subject, $replyto); + $ret = Mail::send($sender_id, $recipient, $body, $subject, $replyto); $norecip = false; switch ($ret) { @@ -131,7 +132,7 @@ function message_content() $cmd = DI::args()->getArgv()[1]; if ($cmd === 'drop') { $message = DBA::selectFirst('mail', ['convid'], ['id' => DI::args()->getArgv()[2], 'uid' => DI::userSession()->getLocalUserId()]); - if(!DBA::isResult($message)){ + if (!DBA::isResult($message)) { DI::sysmsg()->addNotice(DI::l10n()->t('Conversation not found.')); DI::baseUrl()->redirect('message'); } @@ -141,7 +142,7 @@ function message_content() } $conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => DI::userSession()->getLocalUserId()]); - if(!DBA::isResult($conversation)){ + if (!DBA::isResult($conversation)) { DI::baseUrl()->redirect('message'); } @@ -165,7 +166,7 @@ function message_content() $tpl = Renderer::getMarkupTemplate('msg-header.tpl'); DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ '$nickname' => DI::userSession()->getLocalUserNickname(), - '$linkurl' => DI::l10n()->t('Please enter a link URL:') + '$linkurl' => DI::l10n()->t('Please enter a link URL:') ]); $recipientId = DI::args()->getArgv()[2] ?? null; @@ -178,7 +179,7 @@ function message_content() '$to' => DI::l10n()->t('To:'), '$subject' => DI::l10n()->t('Subject:'), '$subjtxt' => $_REQUEST['subject'] ?? '', - '$text' => $_REQUEST['body'] ?? '', + '$text' => $_REQUEST['body'] ?? '', '$readonly' => '', '$yourmessage' => DI::l10n()->t('Your message:'), '$select' => $select, @@ -207,7 +208,7 @@ function message_content() $r = get_messages(DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage()); if (!DBA::isResult($r)) { - DI::sysmsg()->addNotice(DI::l10n()->t('No messages.')); + $o .= DI::l10n()->t('You have no messages.'); return $o; } @@ -222,7 +223,8 @@ function message_content() $o .= $header; - $message = DBA::fetchFirst(" + $message = DBA::fetchFirst( + " SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb` FROM `mail` LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id` @@ -241,11 +243,12 @@ function message_content() if ($message['convid']) { $sql_extra = "AND (`mail`.`parent-uri` = ? OR `mail`.`convid` = ?)"; - $params[] = $message['convid']; + $params[] = $message['convid']; } else { $sql_extra = "AND `mail`.`parent-uri` = ?"; } - $messages_stmt = DBA::p(" + $messages_stmt = DBA::p( + " SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb` FROM `mail` LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id` @@ -270,11 +273,11 @@ function message_content() $tpl = Renderer::getMarkupTemplate('msg-header.tpl'); DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ '$nickname' => DI::userSession()->getLocalUserNickname(), - '$linkurl' => DI::l10n()->t('Please enter a link URL:') + '$linkurl' => DI::l10n()->t('Please enter a link URL:') ]); - $mails = []; - $seen = 0; + $mails = []; + $seen = 0; $unknown = false; foreach ($messages as $message) { @@ -284,18 +287,18 @@ function message_content() if ($message['from-url'] == $myprofile) { $from_url = $myprofile; - $sparkle = ''; + $sparkle = ''; } else { $from_url = Contact::magicLink($message['from-url']); - $sparkle = ' sparkle'; + $sparkle = ' sparkle'; } $from_name_e = $message['from-name']; - $subject_e = $message['title']; - $body_e = BBCode::convertForUriId($message['uri-id'], $message['body']); - $to_name_e = $message['name']; + $subject_e = $message['title']; + $body_e = BBCode::convertForUriId($message['uri-id'], $message['body']); + $to_name_e = $message['name']; - $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); + $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); $from_photo = Contact::getThumb($contact); $mails[] = [ @@ -320,7 +323,7 @@ function message_content() $parent = ''; $tpl = Renderer::getMarkupTemplate('mail_display.tpl'); - $o = Renderer::replaceMacros($tpl, [ + $o = Renderer::replaceMacros($tpl, [ '$thread_id' => DI::args()->getArgv()[1], '$thread_subject' => $message['title'], '$thread_seen' => $seen, @@ -329,19 +332,19 @@ function message_content() '$unknown_text' => DI::l10n()->t("No secure communications available. You may be able to respond from the sender's profile page."), '$mails' => $mails, // reply - '$header' => DI::l10n()->t('Send Reply'), - '$to' => DI::l10n()->t('To:'), - '$subject' => DI::l10n()->t('Subject:'), - '$subjtxt' => $message['title'], - '$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ', - '$yourmessage' => DI::l10n()->t('Your message:'), - '$text' => '', - '$select' => $select, - '$parent' => $parent, - '$upload' => DI::l10n()->t('Upload photo'), - '$insert' => DI::l10n()->t('Insert web link'), - '$submit' => DI::l10n()->t('Submit'), - '$wait' => DI::l10n()->t('Please wait') + '$header' => DI::l10n()->t('Send Reply'), + '$to' => DI::l10n()->t('To:'), + '$subject' => DI::l10n()->t('Subject:'), + '$subjtxt' => $message['title'], + '$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ', + '$yourmessage' => DI::l10n()->t('Your message:'), + '$text' => '', + '$select' => $select, + '$parent' => $parent, + '$upload' => DI::l10n()->t('Upload photo'), + '$insert' => DI::l10n()->t('Insert web link'), + '$submit' => DI::l10n()->t('Submit'), + '$wait' => DI::l10n()->t('Please wait') ]); return $o; @@ -396,13 +399,12 @@ function get_messages(int $uid, int $start, int $limit): array LEFT JOIN `contact` c ON m.`contact-id` = c.`id` WHERE m.`uid` = ? ORDER BY m2.`mailcreated` DESC - LIMIT ?, ?' - , $uid, $uid, $start, $limit)); + LIMIT ?, ?', $uid, $uid, $start, $limit)); } function render_messages(array $msg, string $t): string { - $tpl = Renderer::getMarkupTemplate($t); + $tpl = Renderer::getMarkupTemplate($t); $rslt = ''; $myprofile = DI::baseUrl() . '/profile/' . DI::userSession()->getLocalUserNickname(); @@ -416,7 +418,7 @@ function render_messages(array $msg, string $t): string $participants = DI::l10n()->t("%s and You", $rr['from-name']); } - $body_e = $rr['body']; + $body_e = $rr['body']; $to_name_e = $rr['name']; if (is_null($rr['url'])) { @@ -424,7 +426,7 @@ function render_messages(array $msg, string $t): string continue; } - $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); + $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); $from_photo = Contact::getThumb($contact); $rslt .= Renderer::replaceMacros($tpl, [ diff --git a/mod/photos.php b/mod/photos.php index 82b5e43960d..57076fc9337 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -1022,7 +1022,7 @@ function photos_content() } } $tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr]; - if ($cmd === 'edit') { + if ($cmd === 'edit' && !empty($tag_arr)) { $tags['removeanyurl'] = 'post/' . $link_item['id'] . '/tag/remove?return=' . urlencode(DI::args()->getCommand()); $tags['removetitle'] = DI::l10n()->t('[Select tags to remove]'); } diff --git a/src/App/Page.php b/src/App/Page.php index 623f464086c..a7337485ec4 100644 --- a/src/App/Page.php +++ b/src/App/Page.php @@ -201,8 +201,8 @@ class Page implements ArrayAccess $this->page['title'] = $l10n->t(ucfirst($args->getModuleName())); } - // Prepend the sitename to the page title - $this->page['title'] = $config->get('config', 'sitename', '') . (!empty($this->page['title']) ? ' | ' . $this->page['title'] : ''); + // Append the sitename to the page title + $this->page['title'] = (!empty($this->page['title']) ? $this->page['title'] . ' | ' : '') . $config->get('config', 'sitename', ''); if (!empty(Renderer::$theme['stylesheet'])) { $stylesheet = Renderer::$theme['stylesheet']; diff --git a/src/Content/Conversation.php b/src/Content/Conversation.php index 722aaf27b55..b9815525af5 100644 --- a/src/Content/Conversation.php +++ b/src/Content/Conversation.php @@ -265,7 +265,10 @@ class Conversation $phrase = $this->l10n->tt(' likes this', ' like this', $total, $spanatts); break; case 'dislike': - $phrase = $this->l10n->tt(' doesn\'t like this', ' don\'t like this', $total, $spanatts); + $dislike_translation_plural = ' don\'t like this'; + // @deprecated 2025.04 this translation is scheduled for removal as a new translation has been added without the typo + $dislike_translation_plural = ' don\'t like this'; + $phrase = $this->l10n->tt(' doesn\'t like this', $dislike_translation_plural, $total, $spanatts); break; case 'attendyes': $phrase = $this->l10n->tt(' attends', ' attend', $total, $spanatts); diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index d111437dffc..3d02319e61c 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -913,13 +913,18 @@ class BBCode default: $text = ($is_quote_share ? "\n" : ''); - $contact = Contact::getByURL($attributes['profile'], false, ['network']); + $contact = Contact::getByURL($attributes['profile'], false, ['network', 'url', 'alias']); $network = $contact['network'] ?? Protocol::PHANTOM; + if (!empty($contact)) { + $profile = Contact::getProfileLink($contact); + } else { + $profile = $attributes['profile']; + } $gsid = ContactSelector::getServerIdForProfile($attributes['profile']); $tpl = Renderer::getMarkupTemplate('shared_content.tpl'); $text .= self::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [ - '$profile' => $attributes['profile'], + '$profile' => $profile, '$avatar' => $attributes['avatar'], '$author' => $attributes['author'], '$link' => $attributes['link'], @@ -1205,13 +1210,13 @@ class BBCode */ private static function normalizeVideoLinks(string $text): string { - $text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); - $text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); - $text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/shorts\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); - $text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); - - $text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text); - $text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text); + $text = preg_replace("/\[youtube\]https?:\/\/(www\.)?youtube\.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$2[/youtube]', $text); + $text = preg_replace("/\[youtube\]https?:\/\/(www\.)?youtube\.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$2[/youtube]', $text); + $text = preg_replace("/\[youtube\]https?:\/\/(www\.)?youtube\.com\/shorts\/(.*?)\[\/youtube\]/ism", '[youtube]$2[/youtube]', $text); + $text = preg_replace("/\[youtube\]https?:\/\/youtu\.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); + $text = preg_replace("/\[youtube\]https?:\/\/m\.youtube\.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); + $text = preg_replace("/\[vimeo\]https?:\/\/player\.vimeo\.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text); + $text = preg_replace("/\[vimeo\]https?:\/\/vimeo\.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text); return $text; } @@ -1984,12 +1989,25 @@ class BBCode '', $text ); - } elseif (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::TWITTER_API])) { + } elseif (in_array($simple_html, [self::EXTERNAL, self::TWITTER_API])) { $text = preg_replace( "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text ); + } elseif ($simple_html == self::INTERNAL) { + if (preg_match_all("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", $text, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $contact = Contact::getByURL($match[2], false, ['network', 'url', 'alias']); + if (!empty($contact)) { + $url = Contact::getProfileLink($contact); + } else { + $url = $match[2]; + } + $text = str_replace($match[0], '' . $match[1] . '' . $match[3] . '', $text); + } + + } } elseif ($simple_html == self::MASTODON_API) { $text = preg_replace( "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", diff --git a/src/Content/Text/HTML.php b/src/Content/Text/HTML.php index f5cf2c6eca7..72c61f38cc1 100644 --- a/src/Content/Text/HTML.php +++ b/src/Content/Text/HTML.php @@ -689,7 +689,7 @@ class HTML public static function toMarkdown(string $html): string { DI::profiler()->startRecording('rendering'); - $converter = new HtmlConverter(['hard_break' => true]); + $converter = new HtmlConverter(['hard_break' => true, 'strip_tags' => true]); $markdown = $converter->convert($html); DI::profiler()->stopRecording(); diff --git a/src/Content/Text/Markdown.php b/src/Content/Text/Markdown.php index 8aa94530419..c47cff66e38 100644 --- a/src/Content/Text/Markdown.php +++ b/src/Content/Text/Markdown.php @@ -121,11 +121,14 @@ class Markdown $s = str_replace('♲', html_entity_decode('♲', ENT_QUOTES, 'UTF-8'), $s); //$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)(vimeo|youtu|www\.youtube|soundcloud)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3$4]$2$3$4[/url]',$s); - $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s); - $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s); - $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/shorts\/(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s); - $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/url\]/ism', '[vimeo]$2[/vimeo]', 'url', $s); - $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/vimeo.com\/([0-9]+)\](.*?)\[\/url\]/ism', '[vimeo]$1[/vimeo]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/www\.youtube\.com\/watch\?v\=(.*?)\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/(www\.)?youtube\.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/(www\.)?youtube\.com\/embed\/(.*?)\].*?\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/(www\.)?youtube\.com\/shorts\/(.*?)\].*?\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/m\.youtube\.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/vimeo\.com\/([0-9]+)(.*?)\[\/url\]/ism', '[vimeo]$2[/vimeo]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/player\.vimeo\.com\/video\/([0-9]+)(.*?)\[\/url\]/ism', '[vimeo]$2[/vimeo]', 'url', $s); + $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/vimeo\.com\/([0-9]+)\](.*?)\[\/url\]/ism', '[vimeo]$1[/vimeo]', 'url', $s); // remove duplicate adjacent code tags $s = preg_replace('/(\[code\])+(.*?)(\[\/code\])+/ism', '[code]$2[/code]', $s); diff --git a/src/Content/Widget.php b/src/Content/Widget.php index b86b3e9b1c8..860256ed74d 100644 --- a/src/Content/Widget.php +++ b/src/Content/Widget.php @@ -35,7 +35,7 @@ class Widget return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/follow.tpl'), [ '$connect' => DI::l10n()->t('Add New Contact'), '$desc' => DI::l10n()->t('Enter address or web location'), - '$hint' => DI::l10n()->t('Example: bob@example.com, http://example.com/barbara'), + '$hint' => DI::l10n()->t('user@x.tld, x.tld/user'), '$value' => $value, '$follow' => DI::l10n()->t('Connect') ]); diff --git a/src/Model/Item.php b/src/Model/Item.php index 3eb2b3b90b4..cac836a7db8 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -3330,7 +3330,7 @@ class Item } $dom = new \DOMDocument(); - if (!@$dom->loadHTML($html)) { + if (empty($html) || !@$dom->loadHTML($html)) { return $html; } diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 72ea6fa5a48..584237db917 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -41,13 +41,13 @@ class Site extends BaseAdmin return; } - $sitename = (!empty($_POST['sitename']) ? trim($_POST['sitename']) : ''); + $sitename = (!empty($_POST['sitename']) ? strip_tags(trim($_POST['sitename'])) : ''); $sender_email = (!empty($_POST['sender_email']) ? trim($_POST['sender_email']) : ''); $banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false); $email_banner = (!empty($_POST['email_banner']) ? trim($_POST['email_banner']) : false); $shortcut_icon = (!empty($_POST['shortcut_icon']) ? trim($_POST['shortcut_icon']) : ''); $touch_icon = (!empty($_POST['touch_icon']) ? trim($_POST['touch_icon']) : ''); - $additional_info = (!empty($_POST['additional_info']) ? trim($_POST['additional_info']) : ''); + $additional_info = (!empty($_POST['additional_info']) ? strip_tags(trim($_POST['additional_info'])) : ''); $language = (!empty($_POST['language']) ? trim($_POST['language']) : ''); $theme = (!empty($_POST['theme']) ? trim($_POST['theme']) : ''); $theme_mobile = (!empty($_POST['theme_mobile']) ? trim($_POST['theme_mobile']) : ''); @@ -57,7 +57,7 @@ class Site extends BaseAdmin $jpegimagequality = (!empty($_POST['jpegimagequality']) ? intval(trim($_POST['jpegimagequality'])) : 100); $register_policy = (!empty($_POST['register_policy']) ? intval(trim($_POST['register_policy'])) : 0); - $max_registered_users = (!empty($_POST['max_registered_users']) ? intval(trim($_POST['max_registered_users'])) : 0); + $max_registered_users = (!empty($_POST['max_registered_users']) ? intval(trim($_POST['max_registered_users'])) : 0); $daily_registrations = (!empty($_POST['max_daily_registrations']) ? intval(trim($_POST['max_daily_registrations'])) : 0); $abandon_days = (!empty($_POST['abandon_days']) ? intval(trim($_POST['abandon_days'])) : 0); diff --git a/src/Module/Contact.php b/src/Module/Contact.php index e9981608452..3897b917cd5 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -254,7 +254,9 @@ class Contact extends BaseModule $searching = true; $search_hdr = $search; $search_txt = preg_quote(trim($search, ' @!')); - $sql_extra .= " AND (`name` REGEXP ? OR `url` REGEXP ? OR `nick` REGEXP ? OR `addr` REGEXP ? OR `alias` REGEXP ?)"; + $sql_extra .= " AND (CAST(`name` AS BINARY) REGEXP BINARY ? OR CAST(`url` AS BINARY) REGEXP BINARY ?"; + $sql_extra .= " OR CAST(`nick` AS BINARY) REGEXP BINARY ? OR CAST(`addr` AS BINARY) REGEXP BINARY ?"; + $sql_extra .= " OR CAST(`alias` AS BINARY) REGEXP BINARY ?)"; $sql_values[] = $search_txt; $sql_values[] = $search_txt; $sql_values[] = $search_txt; diff --git a/src/Module/Moderation/Report/Create.php b/src/Module/Moderation/Report/Create.php index 9761686ff0d..660d5ed9b33 100644 --- a/src/Module/Moderation/Report/Create.php +++ b/src/Module/Moderation/Report/Create.php @@ -26,7 +26,6 @@ use Friendica\Moderation\Entity\Report; use Friendica\Module\Response; use Friendica\Navigation\SystemMessages; use Friendica\Network\HTTPException\ForbiddenException; -use Friendica\Util\Network; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; @@ -221,11 +220,19 @@ class Create extends BaseModule } if (DI::mode()->isMobile()) { - $itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network', - DI::config()->get('system', 'itemspage_network_mobile')); + $itemsPerPage = DI::pConfig()->get( + DI::userSession()->getLocalUserId(), + 'system', + 'itemspage_mobile_network', + DI::config()->get('system', 'itemspage_network_mobile') + ); } else { - $itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network', - DI::config()->get('system', 'itemspage_network')); + $itemsPerPage = DI::pConfig()->get( + DI::userSession()->getLocalUserId(), + 'system', + 'itemspage_network', + DI::config()->get('system', 'itemspage_network') + ); } $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage); @@ -260,6 +267,11 @@ class Create extends BaseModule $contact = Contact::getById($request['cid'], ['url']); $tpl = Renderer::getMarkupTemplate('moderation/report/create/summary.tpl'); + + $forward_translation = $this->t('Would you like to forward this report to the remote server?'); + // @deprecated 2025.04 this translation is scheduled for removal as a new translation has been added without the typo + $forward_translation = $this->t('Would you ike to forward this report to the remote server?'); + return Renderer::replaceMacros($tpl, [ '$l10n' => [ 'title' => $this->t('Create Moderation Report'), @@ -280,7 +292,7 @@ class Create extends BaseModule '$block' => ['contact_action', $this->t('Block contact'), self::CONTACT_ACTION_BLOCK, $this->t("Their posts won't appear in your Network page anymore, but their replies can appear in forum threads, with their content collapsed by default. They cannot follow you but still can have access to your public posts by other means.")], '$display_forward' => !$this->baseUrl->isLocalUrl($contact['url']), - '$forward' => ['report_forward', $this->t('Forward report'), self::CONTACT_ACTION_BLOCK, $this->t('Would you ike to forward this report to the remote server?')], + '$forward' => ['report_forward', $this->t('Forward report'), self::CONTACT_ACTION_BLOCK, $forward_translation], '$summary' => $this->getAside($request), ]); @@ -294,12 +306,18 @@ class Create extends BaseModule } switch ($request['category'] ?? 0) { - case Report::CATEGORY_SPAM: $category = $this->t('Spam'); break; - case Report::CATEGORY_ILLEGAL: $category = $this->t('Illegal Content'); break; - case Report::CATEGORY_SAFETY: $category = $this->t('Community Safety'); break; - case Report::CATEGORY_UNWANTED: $category = $this->t('Unwanted Content/Behavior'); break; - case Report::CATEGORY_VIOLATION: $category = $this->t('Rules Violation'); break; - case Report::CATEGORY_OTHER: $category = $this->t('Other'); break; + case Report::CATEGORY_SPAM: $category = $this->t('Spam'); + break; + case Report::CATEGORY_ILLEGAL: $category = $this->t('Illegal Content'); + break; + case Report::CATEGORY_SAFETY: $category = $this->t('Community Safety'); + break; + case Report::CATEGORY_UNWANTED: $category = $this->t('Unwanted Content/Behavior'); + break; + case Report::CATEGORY_VIOLATION: $category = $this->t('Rules Violation'); + break; + case Report::CATEGORY_OTHER: $category = $this->t('Other'); + break; default: $category = ''; } diff --git a/src/Module/Post/Tag/Remove.php b/src/Module/Post/Tag/Remove.php index e5652c79a80..4426c48b7fd 100644 --- a/src/Module/Post/Tag/Remove.php +++ b/src/Module/Post/Tag/Remove.php @@ -54,7 +54,7 @@ class Remove extends \Friendica\BaseModule protected function content(array $request = []): string { - $returnUrl = hex2bin($request['return'] ?? ''); + $returnUrl = $request['return'] ?? ''; if (!$this->session->getLocalUserId()) { $this->baseUrl->redirect($returnUrl); @@ -80,7 +80,7 @@ class Remove extends \Friendica\BaseModule if ($tag_text === '') { $this->baseUrl->redirect($returnUrl); } - + $tags = explode(',', $tag_text); $tag_checkboxes = array_map(function ($tag_text) { @@ -97,7 +97,7 @@ class Remove extends \Friendica\BaseModule ], '$item_id' => $item_id, - '$return' => $returnUrl, + '$return' => urlencode($returnUrl), '$tag_checkboxes' => $tag_checkboxes, ]); } diff --git a/src/Module/Profile/Photos.php b/src/Module/Profile/Photos.php index 6d9bca26e34..5fe798730cb 100644 --- a/src/Module/Profile/Photos.php +++ b/src/Module/Profile/Photos.php @@ -128,8 +128,8 @@ class Photos extends \Friendica\Module\BaseProfile $request = $hook_data['request'] ?? $request; // Determine the album to use - $album = trim($request['album'] ?? ''); - $newalbum = trim($request['newalbum'] ?? ''); + $album = strip_tags(trim($request['album'] ?? '')); + $newalbum = strip_tags(trim($request['newalbum'] ?? '')); $this->logger->debug('album= ' . $album . ' newalbum= ' . $newalbum); diff --git a/src/Module/Settings/Channels.php b/src/Module/Settings/Channels.php index 85ee2ccefa2..0049dece274 100644 --- a/src/Module/Settings/Channels.php +++ b/src/Module/Settings/Channels.php @@ -192,6 +192,11 @@ class Channels extends BaseSettings } $t = Renderer::getMarkupTemplate('settings/channels.tpl'); + + $exclude_tags_translation = $this->t('Comma separated list of tags. If a post contain any of these tags, then it will not be part of this channel.'); + // @deprecated 2025.04 this translation is scheduled for removal as a new translation has been added without the typo + $exclude_tags_translation = $this->t('Comma separated list of tags. If a post contain any of these tags, then it will not be part of nthis channel.'); + return Renderer::replaceMacros($t, [ 'open' => count($channels) == 0, 'label' => ["new_label", $this->t('Label'), '', $this->t('Short name for the channel. It is displayed on the channels widget.'), $this->t('Required')], @@ -199,7 +204,7 @@ class Channels extends BaseSettings 'access_key' => ["new_access_key", $this->t("Access Key"), '', $this->t('When you want to access this channel via an access key, you can define it here. Pay attention to not use an already used one.')], 'circle' => ['new_circle', $this->t('Circle/Channel'), 0, $this->t('Select a circle or channel, that your channel should be based on.'), $circles], 'include_tags' => ["new_include_tags", $this->t("Include Tags"), '', $this->t('Comma separated list of tags. A post will be used when it contains any of the listed tags.')], - 'exclude_tags' => ["new_exclude_tags", $this->t("Exclude Tags"), '', $this->t('Comma separated list of tags. If a post contain any of these tags, then it will not be part of nthis channel.')], + 'exclude_tags' => ["new_exclude_tags", $this->t("Exclude Tags"), '', $exclude_tags_translation], 'min_size' => ["new_min_size", $this->t("Minimum Size"), '', $this->t('Minimum post size. Leave empty for no minimum size. The size is calculated without links, attached posts, mentions or hashtags.')], 'max_size' => ["new_max_size", $this->t("Maximum Size"), '', $this->t('Maximum post size. Leave empty for no maximum size. The size is calculated without links, attached posts, mentions or hashtags.')], 'text_search' => ["new_text_search", $this->t("Full Text Search"), '', $this->t('Search terms for the body, supports the "boolean mode" operators from MariaDB. See the help for a complete list of operators and additional keywords: %s', 'help/Channels')], diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php index 6f7a3a6204e..9a342aa3b36 100644 --- a/src/Module/Settings/Profile/Index.php +++ b/src/Module/Settings/Profile/Index.php @@ -99,7 +99,7 @@ class Index extends BaseSettings new ArrayFilterEvent(ArrayFilterEvent::PROFILE_SETTINGS_POST, $request), )->getArray(); - $dob = trim($request['dob'] ?? ''); + $dob = $this->cleanInput($request['dob'] ?? ''); if ($dob && !in_array($dob, ['0000-00-00', DBA::NULL_DATE])) { $y = substr($dob, 0, 4); @@ -121,18 +121,18 @@ class Index extends BaseSettings } } - $username = trim($request['username'] ?? ''); + $username = $this->cleanInputText($request['username'] ?? ''); if (!$username) { $this->systemMessages->addNotice($this->t('Display Name is required.')); return; } - $about = trim($request['about']); - $address = trim($request['address']); - $locality = trim($request['locality']); - $region = trim($request['region']); - $postal_code = trim($request['postal_code']); - $country_name = trim($request['country_name']); + $about = $this->cleanInputText($request['about']); + $address = $this->cleanInputText($request['address']); + $locality = $this->cleanInputText($request['locality']); + $region = $this->cleanInputText($request['region']); + $postal_code = $this->cleanInputText($request['postal_code']); + $country_name = $this->cleanInputText($request['country_name']); $pub_keywords = self::cleanKeywords(trim($request['pub_keywords'])); $prv_keywords = self::cleanKeywords(trim($request['prv_keywords'])); $xmpp = $this->cleanInput(trim($request['xmpp'])); @@ -377,9 +377,14 @@ class Index extends BaseSettings return $profileFields; } + private function cleanInputText(string $input): string + { + return trim(strip_tags($input)); + } + private function cleanInput(string $input): string { - return str_replace(['<', '>', '"', ' '], '', $input); + return str_replace(['<', '>', '"', "'", ' '], '', $input); } private static function cleanKeywords($keywords): string @@ -389,7 +394,7 @@ class Index extends BaseSettings $cleaned = []; foreach ($keywords as $keyword) { - $keyword = trim($keyword); + $keyword = trim(str_replace(['<', '>', '"', "'"], '', $keyword)); $keyword = trim($keyword, '#'); if ($keyword != '') { $cleaned[] = $keyword; diff --git a/src/Protocol/ATProtocol/Actor.php b/src/Protocol/ATProtocol/Actor.php index db310813b31..4dae8b75cf3 100755 --- a/src/Protocol/ATProtocol/Actor.php +++ b/src/Protocol/ATProtocol/Actor.php @@ -133,7 +133,7 @@ class Actor } $directory = $this->atprotocol->get(ATProtocol::DIRECTORY . '/' . $profile->did); - if (!empty($directory)) { + if (!empty($directory->service)) { foreach ($directory->service as $service) { if (($service->id == '#atproto_pds') && ($service->type == 'AtprotoPersonalDataServer') && !empty($service->serviceEndpoint)) { $fields['baseurl'] = $service->serviceEndpoint; @@ -145,9 +145,11 @@ class Actor $fields['gsid'] = GServer::getRealID($fields['baseurl'], true); } - foreach ($directory->verificationMethod as $method) { - if (!empty($method->publicKeyMultibase)) { - $fields['pubkey'] = $method->publicKeyMultibase; + if (!empty($directory->verificationMethod)) { + foreach ($directory->verificationMethod as $method) { + if (!empty($method->publicKeyMultibase)) { + $fields['pubkey'] = $method->publicKeyMultibase; + } } } } diff --git a/src/Protocol/ATProtocol/Jetstream.php b/src/Protocol/ATProtocol/Jetstream.php index cb37fabeec0..a4771cae441 100755 --- a/src/Protocol/ATProtocol/Jetstream.php +++ b/src/Protocol/ATProtocol/Jetstream.php @@ -84,6 +84,8 @@ class Jetstream $timeout_limit = 10; $timestamp = $this->keyValue->get('jetstream_timestamp') ?? 0; $cursor = ''; + $this->logger->notice('Start listening'); + while (true) { if ($timestamp) { $cursor = '&cursor=' . $timestamp; @@ -97,7 +99,7 @@ class Jetstream $this->client->setTimeout($timeout); $this->client->setLogger($this->logger); } catch (\WebSocket\ConnectionException $e) { - $this->logger->error('Error while trying to establish the connection', ['code' => $e->getCode(), 'message' => $e->getMessage()]); + $this->logger->error('Error while trying to establish the connection', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]); echo "Connection wasn't established.\n"; exit(1); } @@ -106,7 +108,12 @@ class Jetstream while (true) { try { $message = $this->client->receive(); - $data = json_decode($message); + + if (empty($message)) { + $this->logger->notice('Empty message received'); + break; + } + $data = json_decode($message); if (is_object($data)) { $timestamp = $data->time_us; $this->route($data); @@ -124,8 +131,9 @@ class Jetstream break; } $this->logger->notice('Timeout', ['duration' => $timeout_duration, 'timestamp' => $timestamp, 'code' => $e->getCode(), 'message' => $e->getMessage()]); + break; } else { - $this->logger->error('Error', ['code' => $e->getCode(), 'message' => $e->getMessage()]); + $this->logger->error('Error while trying to receive a message', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]); break; } } @@ -134,9 +142,10 @@ class Jetstream try { $this->client->close(); } catch (\WebSocket\ConnectionException $e) { - $this->logger->error('Error while trying to close the connection', ['code' => $e->getCode(), 'message' => $e->getMessage()]); + $this->logger->error('Error while trying to close the connection', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]); } } + $this->logger->notice('Stop listening'); } /** @@ -232,7 +241,7 @@ class Jetstream try { $this->client->send(json_encode($update)); } catch (\WebSocket\ConnectionException $e) { - $this->logger->error('Error while trying to send options.', ['code' => $e->getCode(), 'message' => $e->getMessage()]); + $this->logger->error('Error while trying to send options.', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]); } } diff --git a/src/Protocol/ATProtocol/Processor.php b/src/Protocol/ATProtocol/Processor.php index 2c0e2528c5a..1ac2a9f00cc 100755 --- a/src/Protocol/ATProtocol/Processor.php +++ b/src/Protocol/ATProtocol/Processor.php @@ -506,13 +506,10 @@ class Processor break; case 'app.bsky.richtext.facet#mention': - $contact = Contact::getByURL($feature->did, null, ['id']); - if (!empty($contact['id'])) { - $url = $this->baseURL . '/contact/' . $contact['id']; - if (substr($linktext, 0, 1) == '@') { - $prefix .= '@'; - $linktext = substr($linktext, 1); - } + $url = $feature->did; + if (substr($linktext, 0, 1) == '@') { + $prefix .= '@'; + $linktext = substr($linktext, 1); } break; diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 76f4d31d3d2..c217d491ca9 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -2065,7 +2065,7 @@ class Receiver } foreach ($object_data['tags'] as $tag) { - if (HTTPSignature::isValidContentType($tag['mediaType'] ?? '', $tag['href'])) { + if (HTTPSignature::isValidContentType($tag['mediaType'] ?? '', $tag['href'] ?? '')) { $object_data['quote-url'] = $tag['href']; } } diff --git a/src/Security/PermissionSet/Repository/PermissionSet.php b/src/Security/PermissionSet/Repository/PermissionSet.php index 42b6219e9c4..8eca294662f 100644 --- a/src/Security/PermissionSet/Repository/PermissionSet.php +++ b/src/Security/PermissionSet/Repository/PermissionSet.php @@ -132,13 +132,13 @@ class PermissionSet extends BaseRepository } if (!empty($user_contact_str)) { - $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR LOCATE(?, `deny_cid`) OR deny_gid REGEXP ?) - AND (LOCATE(?, allow_cid) OR LOCATE(?, allow_cid) OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", + $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR LOCATE(?, `deny_cid`) OR CAST(deny_gid AS BINARY) REGEXP BINARY ?) + AND (LOCATE(?, allow_cid) OR LOCATE(?, allow_cid) OR CAST(allow_gid AS BINARY) REGEXP BINARY ? OR (allow_cid = '' AND allow_gid = '')))", $uid, $user_contact_str, $public_contact_str, $circle_str, $user_contact_str, $public_contact_str, $circle_str]; } else { - $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR deny_gid REGEXP ?) - AND (LOCATE(?, allow_cid) OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", + $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR CAST(deny_gid AS BINARY) REGEXP BINARY ?) + AND (LOCATE(?, allow_cid) OR CAST(allow_gid AS BINARY) REGEXP BINARY ? OR (allow_cid = '' AND allow_gid = '')))", $uid, $public_contact_str, $circle_str, $public_contact_str, $circle_str]; } diff --git a/src/Security/Security.php b/src/Security/Security.php index f04af420611..96e3386141b 100644 --- a/src/Security/Security.php +++ b/src/Security/Security.php @@ -105,8 +105,8 @@ class Security } $sql = sprintf( - " AND (NOT (deny_cid REGEXP '<%d>' OR deny_gid REGEXP '%s') - AND (allow_cid REGEXP '<%d>' OR allow_gid REGEXP '%s' + " AND (NOT (CAST(deny_cid AS BINARY) REGEXP BINARY '<%d>' OR CAST(deny_gid AS BINARY) REGEXP BINARY '%s') + AND (CAST(allow_cid AS BINARY) REGEXP BINARY '<%d>' OR CAST(allow_gid AS BINARY) REGEXP BINARY '%s' OR (allow_cid = '' AND allow_gid = ''))" . $acc_sql . ") ", intval($remote_contact), DBA::escape($circleIds), diff --git a/tests/datasets/api.fixture.php b/tests/datasets/api.fixture.php index 15967261148..5a31cfb3b9b 100644 --- a/tests/datasets/api.fixture.php +++ b/tests/datasets/api.fixture.php @@ -13,11 +13,11 @@ use Friendica\Model\Notification; return [ 'gserver' => [ [ - 'url' => 'https://friendica.local', - 'nurl' => 'http://friendica.local', - 'register_policy' => 0, + 'url' => 'https://friendica.local', + 'nurl' => 'http://friendica.local', + 'register_policy' => 0, 'registered-users' => 0, - 'network' => 'unkn', + 'network' => 'unkn', ], ], // Base test config to avoid notice messages @@ -88,6 +88,11 @@ return [ 'uri' => 'https://friendica.local/profile/mutualcontact', 'guid' => '46', ], + [ + 'id' => 49, + 'uri' => 'https://domain.tld/profile/remotecontact', + 'guid' => '49', + ], [ 'id' => 100, 'uri' => 'https://friendica.local/posts/100', @@ -214,6 +219,23 @@ return [ 'network' => Protocol::DFRN, 'location' => 'DFRN', ], + [ + 'id' => 49, + 'uid' => 0, + 'uri-id' => 43, + 'name' => 'Remote user', + 'nick' => 'remotecontact', + 'self' => 0, + 'nurl' => 'http://domain.tld/profile/remotecontact', + 'url' => 'https://domain.tld/profile/remotecontact', + 'alias' => 'https://domain.tld/~remotecontact', + 'about' => 'User used in tests', + 'pending' => 0, + 'blocked' => 0, + 'rel' => Contact::FOLLOWER, + 'network' => Protocol::ACTIVITYPUB, + 'location' => 'AP', + ], ], 'apcontact' => [ [ @@ -343,7 +365,7 @@ return [ 'suscipit aut facilis ut inventore omnis exercitationem quo magnam ' . 'consequatur maxime aut illum soluta quaerat natus unde aspernatur ' . 'et sed beatae nihil ullam temporibus corporis ratione blanditiis', - 'plink' => 'https://friendica.local/display/6', + 'plink' => 'https://friendica.local/display/6', ], [ 'uri-id' => 100, @@ -912,8 +934,8 @@ return [ ], 'profile' => [ [ - 'id' => 1, - 'uid' => 42, + 'id' => 1, + 'uid' => 42, 'locality' => 'DFRN', ], ], @@ -933,18 +955,18 @@ return [ ], 'group_member' => [ [ - 'id' => 1, - 'gid' => 1, + 'id' => 1, + 'gid' => 1, 'contact-id' => 43, ], [ - 'id' => 2, - 'gid' => 1, + 'id' => 2, + 'gid' => 1, 'contact-id' => 43, ], [ - 'id' => 3, - 'gid' => 2, + 'id' => 3, + 'gid' => 2, 'contact-id' => 43, ], ], diff --git a/tests/src/Content/Text/BBCodeTest.php b/tests/src/Content/Text/BBCodeTest.php index 9c61e10887a..ddce6975e90 100644 --- a/tests/src/Content/Text/BBCodeTest.php +++ b/tests/src/Content/Text/BBCodeTest.php @@ -73,51 +73,51 @@ class BBCodeTest extends FixtureTestCase ], 'no-protocol' => [ 'data' => 'example.com/path', - 'assertHTML' => false + 'assertHTML' => false, ], 'wrong-protocol' => [ 'data' => 'ftp://example.com', - 'assertHTML' => false + 'assertHTML' => false, ], 'wrong-domain-without-path' => [ 'data' => 'http://example', - 'assertHTML' => false + 'assertHTML' => false, ], 'wrong-domain-with-path' => [ 'data' => 'http://example/path', - 'assertHTML' => false + 'assertHTML' => false, ], 'bug-6857-domain-start' => [ 'data' => "http://\nexample.com", - 'assertHTML' => false + 'assertHTML' => false, ], 'bug-6857-domain-end' => [ 'data' => "http://example\n.com", - 'assertHTML' => false + 'assertHTML' => false, ], 'bug-6857-tld' => [ 'data' => "http://example.\ncom", - 'assertHTML' => false + 'assertHTML' => false, ], 'bug-6857-end' => [ 'data' => "http://example.com\ntest", - 'assertHTML' => false + 'assertHTML' => false, ], 'bug-6901' => [ 'data' => "http://example.com