diff --git a/bin/console.php b/bin/console.php index a20f3f7e02..88382e1665 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 ac1f5c9bc9..9973336590 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 bae30155dd..fb9fadddbd 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 6ee7a8745a..f3a231601c 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 82b5e43960..57076fc933 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 623f464086..a7337485ec 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 722aaf27b5..b9815525af 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 d111437dff..3d02319e61 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 f5cf2c6eca..72c61f38cc 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 8aa9453041..c47cff66e3 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 b86b3e9b1c..860256ed74 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 3eb2b3b90b..cac836a7db 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 72ea6fa5a4..584237db91 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 e998160845..3897b917cd 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 9761686ff0..660d5ed9b3 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 e5652c79a8..4426c48b7f 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 6d9bca26e3..5fe798730c 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 85ee2ccefa..0049dece27 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 6f7a3a6204..9a342aa3b3 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 db310813b3..4dae8b75cf 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 cb37fabeec..a4771cae44 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 2c0e2528c5..1ac2a9f00c 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 76f4d31d3d..c217d491ca 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 42b6219e9c..8eca294662 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 f04af42061..96e3386141 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 1596726114..5a31cfb3b9 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 9c61e10887..ddce6975e9 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