From 51c984849fffd07230ec01e6b95153a6f4cf3aac Mon Sep 17 00:00:00 2001 From: Diogo Peralta Cordeiro Date: Wed, 27 Oct 2021 04:14:01 +0100 Subject: [PATCH] [ActivityPub] Port Explorer --- composer.json | 5 +- composer.lock | 530 +++++++++++------- plugins/ActivityPub/ActivityPub.php | 62 +- .../Entity/ActivitypubActivity.php | 15 +- .../ActivityPub/Entity/ActivitypubActor.php | 99 ++++ plugins/ActivityPub/Util/DiscoveryHints.php | 179 ++++++ plugins/ActivityPub/Util/Explorer.php | 373 ++++++++++++ .../Util/Model/AS2ToEntity/AS2ToEntity.php | 84 +-- .../Util/Model/AS2ToEntity/AS2ToGSActor.php | 49 -- .../Util/Model/AS2ToEntity/AS2ToNote.php | 13 +- plugins/ActivityPub/composer.json | 6 + plugins/ImageEncoder/composer.json | 1 + symfony.lock | 6 + 13 files changed, 1112 insertions(+), 310 deletions(-) create mode 100644 plugins/ActivityPub/Util/DiscoveryHints.php create mode 100644 plugins/ActivityPub/Util/Explorer.php delete mode 100644 plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToGSActor.php create mode 100644 plugins/ActivityPub/composer.json diff --git a/composer.json b/composer.json index 7f87883ba5..0daa4ba7a7 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "ext-ctype": "*", "ext-curl": "*", "ext-iconv": "*", - "ext-vips": "*", + "ext-openssl": "*", "alchemy/zippy": "v0.5.x-dev", "composer/package-versions-deprecated": "1.11.99.3", "doctrine/annotations": "^1.0", @@ -62,8 +62,7 @@ "twig/extra-bundle": "^2.12|^3.0", "twig/markdown-extra": "^3.0", "twig/twig": "^2.12|^3.0", - "wikimedia/composer-merge-plugin": "^2.0", - "ext-openssl": "*" + "wikimedia/composer-merge-plugin": "^2.0" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "^3.4", diff --git a/composer.lock b/composer.lock index e2465865fe..f0074b04a0 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": "ee4ca7e7786d602257aa7ba8ef3b35ee", + "content-hash": "79e11464cb6735c434fcf6baa7079812", "packages": [ { "name": "alchemy/binary-driver", @@ -651,16 +651,16 @@ }, { "name": "doctrine/common", - "version": "3.1.2", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "a036d90c303f3163b5be8b8fde9b6755b2be4a3a" + "reference": "6d970a11479275300b5144e9373ce5feacfa9b91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/a036d90c303f3163b5be8b8fde9b6755b2be4a3a", - "reference": "a036d90c303f3163b5be8b8fde9b6755b2be4a3a", + "url": "https://api.github.com/repos/doctrine/common/zipball/6d970a11479275300b5144e9373ce5feacfa9b91", + "reference": "6d970a11479275300b5144e9373ce5feacfa9b91", "shasum": "" }, "require": { @@ -721,7 +721,7 @@ ], "support": { "issues": "https://github.com/doctrine/common/issues", - "source": "https://github.com/doctrine/common/tree/3.1.2" + "source": "https://github.com/doctrine/common/tree/3.2.0" }, "funding": [ { @@ -737,38 +737,39 @@ "type": "tidelift" } ], - "time": "2021-02-10T20:18:51+00:00" + "time": "2021-10-19T06:47:22+00:00" }, { "name": "doctrine/dbal", - "version": "2.13.4", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "2411a55a2a628e6d8dd598388ab13474802c7b6e" + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/2411a55a2a628e6d8dd598388ab13474802c7b6e", - "reference": "2411a55a2a628e6d8dd598388ab13474802c7b6e", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/96b0053775a544b4a6ab47654dac0621be8b4cf8", + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8", "shasum": "" }, "require": { + "composer/package-versions-deprecated": "^1.11.99", "doctrine/cache": "^1.0|^2.0", "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0", - "ext-pdo": "*", - "php": "^7.1 || ^8" + "php": "^7.3 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "9.0.0", "jetbrains/phpstorm-stubs": "2021.1", "phpstan/phpstan": "0.12.99", - "phpunit/phpunit": "^7.5.20|^8.5|9.5.10", + "phpstan/phpstan-strict-rules": "^0.12.11", + "phpunit/phpunit": "9.5.10", "psalm/plugin-phpunit": "0.16.1", "squizlabs/php_codesniffer": "3.6.0", - "symfony/cache": "^4.4", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", + "symfony/cache": "^5.2|^6.0", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", "vimeo/psalm": "4.10.0" }, "suggest": { @@ -780,7 +781,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -823,14 +824,13 @@ "queryobject", "sasql", "sql", - "sqlanywhere", "sqlite", "sqlserver", "sqlsrv" ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/2.13.4" + "source": "https://github.com/doctrine/dbal/tree/3.1.3" }, "funding": [ { @@ -846,7 +846,7 @@ "type": "tidelift" } ], - "time": "2021-10-02T15:59:26+00:00" + "time": "2021-10-02T16:15:05+00:00" }, { "name": "doctrine/deprecations", @@ -1006,21 +1006,21 @@ }, { "name": "doctrine/doctrine-migrations-bundle", - "version": "3.1.1", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "91f0a5e2356029575f3038432cc188b12f9d5da5" + "reference": "7ad66566ecce0925786707654df15203782f583a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/91f0a5e2356029575f3038432cc188b12f9d5da5", - "reference": "91f0a5e2356029575f3038432cc188b12f9d5da5", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/7ad66566ecce0925786707654df15203782f583a", + "reference": "7ad66566ecce0925786707654df15203782f583a", "shasum": "" }, "require": { "doctrine/doctrine-bundle": "~1.0|~2.0", - "doctrine/migrations": "^3.1", + "doctrine/migrations": "^3.2", "php": "^7.2|^8.0", "symfony/framework-bundle": "~3.4|~4.0|~5.0" }, @@ -1070,7 +1070,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", - "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.1.1" + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.2.0" }, "funding": [ { @@ -1086,7 +1086,7 @@ "type": "tidelift" } ], - "time": "2021-04-10T16:48:53+00:00" + "time": "2021-10-19T16:25:22+00:00" }, { "name": "doctrine/event-manager", @@ -1184,34 +1184,30 @@ }, { "name": "doctrine/inflector", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210" + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/9cf661f4eb38f7c881cac67c75ea9b00bf97b210", - "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^7.0", - "phpstan/phpstan": "^0.11", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-strict-rules": "^0.11", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "doctrine/coding-standard": "^8.2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.10" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" @@ -1259,7 +1255,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.x" + "source": "https://github.com/doctrine/inflector/tree/2.0.4" }, "funding": [ { @@ -1275,7 +1271,7 @@ "type": "tidelift" } ], - "time": "2020-05-29T15:13:26+00:00" + "time": "2021-10-22T20:16:43+00:00" }, { "name": "doctrine/instantiator", @@ -1428,21 +1424,21 @@ }, { "name": "doctrine/migrations", - "version": "3.2.1", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "818e31703b4fb353c0c23caa714273fc64efa675" + "reference": "1967775546df997eb97057911d80184c91c92b9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/818e31703b4fb353c0c23caa714273fc64efa675", - "reference": "818e31703b4fb353c0c23caa714273fc64efa675", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/1967775546df997eb97057911d80184c91c92b9a", + "reference": "1967775546df997eb97057911d80184c91c92b9a", "shasum": "" }, "require": { "composer/package-versions-deprecated": "^1.8", - "doctrine/dbal": "^2.11", + "doctrine/dbal": "^2.11 || ^3.0", "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0", "friendsofphp/proxy-manager-lts": "^1.0", @@ -1514,7 +1510,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.2.1" + "source": "https://github.com/doctrine/migrations/tree/3.3.0" }, "funding": [ { @@ -1530,20 +1526,20 @@ "type": "tidelift" } ], - "time": "2021-08-03T11:49:27+00:00" + "time": "2021-10-15T16:53:47+00:00" }, { "name": "doctrine/orm", - "version": "2.10.1", + "version": "2.10.2", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "f346379c7bf39a9f46771cfd9043e0eb2dd98793" + "reference": "81d472f6f96b8b571cafefe8d2fef89ed9446a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/f346379c7bf39a9f46771cfd9043e0eb2dd98793", - "reference": "f346379c7bf39a9f46771cfd9043e0eb2dd98793", + "url": "https://api.github.com/repos/doctrine/orm/zipball/81d472f6f96b8b571cafefe8d2fef89ed9446a62", + "reference": "81d472f6f96b8b571cafefe8d2fef89ed9446a62", "shasum": "" }, "require": { @@ -1575,7 +1571,7 @@ "phpbench/phpbench": "^0.16.10 || ^1.0", "phpstan/phpstan": "0.12.99", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", - "squizlabs/php_codesniffer": "3.6.0", + "squizlabs/php_codesniffer": "3.6.1", "symfony/cache": "^4.4 || ^5.2", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", "vimeo/psalm": "4.10.0" @@ -1627,9 +1623,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.10.1" + "source": "https://github.com/doctrine/orm/tree/2.10.2" }, - "time": "2021-10-05T13:04:30+00:00" + "time": "2021-10-21T17:57:02+00:00" }, { "name": "doctrine/persistence", @@ -1777,16 +1773,16 @@ }, { "name": "egulias/email-validator", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "c81f18a3efb941d8c4d2e025f6183b5c6d697307" + "reference": "ee0db30118f661fb166bcffbf5d82032df484697" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/c81f18a3efb941d8c4d2e025f6183b5c6d697307", - "reference": "c81f18a3efb941d8c4d2e025f6183b5c6d697307", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ee0db30118f661fb166bcffbf5d82032df484697", + "reference": "ee0db30118f661fb166bcffbf5d82032df484697", "shasum": "" }, "require": { @@ -1833,7 +1829,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/3.1.1" + "source": "https://github.com/egulias/EmailValidator/tree/3.1.2" }, "funding": [ { @@ -1841,20 +1837,20 @@ "type": "github" } ], - "time": "2021-04-01T18:37:14+00:00" + "time": "2021-10-11T09:18:27+00:00" }, { "name": "embed/embed", - "version": "v4.3.4", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/oscarotero/Embed.git", - "reference": "3efed0e92bc3e26e4c77d6ead4b230076ce766ec" + "reference": "4dd2b05119462142ea4adfcf42e75f31dc056975" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/oscarotero/Embed/zipball/3efed0e92bc3e26e4c77d6ead4b230076ce766ec", - "reference": "3efed0e92bc3e26e4c77d6ead4b230076ce766ec", + "url": "https://api.github.com/repos/oscarotero/Embed/zipball/4dd2b05119462142ea4adfcf42e75f31dc056975", + "reference": "4dd2b05119462142ea4adfcf42e75f31dc056975", "shasum": "" }, "require": { @@ -1914,9 +1910,9 @@ "support": { "email": "oom@oscarotero.com", "issues": "https://github.com/oscarotero/Embed/issues", - "source": "https://github.com/oscarotero/Embed/tree/v4.3.4" + "source": "https://github.com/oscarotero/Embed/tree/v4.3.5" }, - "time": "2021-06-22T18:10:12+00:00" + "time": "2021-10-10T17:10:44+00:00" }, { "name": "erusev/parsedown", @@ -2099,16 +2095,16 @@ }, { "name": "giggsey/libphonenumber-for-php", - "version": "8.12.34", + "version": "8.12.36", "source": { "type": "git", "url": "https://github.com/giggsey/libphonenumber-for-php.git", - "reference": "3161a9e7ffb11cf1355845f13af2c947f4f43d55" + "reference": "385660e434e7ad4bf023dde39302e561199b9bb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/3161a9e7ffb11cf1355845f13af2c947f4f43d55", - "reference": "3161a9e7ffb11cf1355845f13af2c947f4f43d55", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/385660e434e7ad4bf023dde39302e561199b9bb4", + "reference": "385660e434e7ad4bf023dde39302e561199b9bb4", "shasum": "" }, "require": { @@ -2168,7 +2164,7 @@ "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", "source": "https://github.com/giggsey/libphonenumber-for-php" }, - "time": "2021-10-06T08:07:58+00:00" + "time": "2021-10-26T09:45:49+00:00" }, { "name": "giggsey/locale", @@ -2644,6 +2640,66 @@ }, "time": "2021-07-01T14:25:37+00:00" }, + { + "name": "mf2/mf2", + "version": "0.4.6", + "source": { + "type": "git", + "url": "https://github.com/microformats/php-mf2.git", + "reference": "00b70ee7eb7f5b0585b1bd467f6c9cbd75055d23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/microformats/php-mf2/zipball/00b70ee7eb7f5b0585b1bd467f6c9cbd75055d23", + "reference": "00b70ee7eb7f5b0585b1bd467f6c9cbd75055d23", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "mf2/tests": "@dev", + "phpdocumentor/phpdocumentor": "v2.8.4", + "phpunit/phpunit": "4.8.*" + }, + "suggest": { + "barnabywalters/mf-cleaner": "To more easily handle the canonical data php-mf2 gives you", + "masterminds/html5": "Alternative HTML parser for PHP, for better HTML5 support." + }, + "bin": [ + "bin/fetch-mf2", + "bin/parse-mf2" + ], + "type": "library", + "autoload": { + "files": [ + "Mf2/Parser.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "CC0-1.0" + ], + "authors": [ + { + "name": "Barnaby Walters", + "homepage": "http://waterpigs.co.uk" + } + ], + "description": "A pure, generic microformats2 parser — makes HTML as easy to consume as a JSON API", + "keywords": [ + "html", + "microformats", + "microformats 2", + "parser", + "semantic" + ], + "support": { + "issues": "https://github.com/microformats/php-mf2/issues", + "source": "https://github.com/microformats/php-mf2/tree/master" + }, + "time": "2018-08-24T14:47:04+00:00" + }, { "name": "ml/iri", "version": "1.1.4", @@ -3445,16 +3501,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { @@ -3465,7 +3521,8 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -3495,9 +3552,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -4769,16 +4826,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v5.2.12", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "0212d2d1e17a3e7403475b76e26fc1f0f1add04b" + "reference": "212521017d81686bdc84a132fb5de2b03867a7e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/0212d2d1e17a3e7403475b76e26fc1f0f1add04b", - "reference": "0212d2d1e17a3e7403475b76e26fc1f0f1add04b", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/212521017d81686bdc84a132fb5de2b03867a7e7", + "reference": "212521017d81686bdc84a132fb5de2b03867a7e7", "shasum": "" }, "require": { @@ -4793,6 +4850,7 @@ }, "conflict": { "doctrine/dbal": "<2.10", + "doctrine/orm": "<2.7.3", "phpunit/phpunit": "<5.4.3", "symfony/dependency-injection": "<4.4", "symfony/form": "<5.1", @@ -4800,7 +4858,7 @@ "symfony/messenger": "<4.4", "symfony/property-info": "<5", "symfony/security-bundle": "<5", - "symfony/security-core": "<5", + "symfony/security-core": "<5.3", "symfony/validator": "<5.2" }, "require-dev": { @@ -4821,7 +4879,7 @@ "symfony/property-access": "^4.4|^5.0", "symfony/property-info": "^5.0", "symfony/proxy-manager-bridge": "^4.4|^5.0", - "symfony/security-core": "^5.0", + "symfony/security-core": "^5.3", "symfony/stopwatch": "^4.4|^5.0", "symfony/translation": "^4.4|^5.0", "symfony/uid": "^5.1", @@ -4862,7 +4920,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v5.2.12" + "source": "https://github.com/symfony/doctrine-bridge/tree/v5.3.8" }, "funding": [ { @@ -4878,20 +4936,20 @@ "type": "tidelift" } ], - "time": "2021-07-21T13:17:02+00:00" + "time": "2021-09-11T18:11:56+00:00" }, { "name": "symfony/doctrine-messenger", - "version": "v5.2.12", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-messenger.git", - "reference": "f67e8f9f7d0e14d079c655c4c51440c388238e76" + "reference": "e11b87557afae8631f83c5afaa1de84232a8043e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/f67e8f9f7d0e14d079c655c4c51440c388238e76", - "reference": "f67e8f9f7d0e14d079c655c4c51440c388238e76", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/e11b87557afae8631f83c5afaa1de84232a8043e", + "reference": "e11b87557afae8631f83c5afaa1de84232a8043e", "shasum": "" }, "require": { @@ -4935,7 +4993,7 @@ "description": "Symfony Doctrine Messenger Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-messenger/tree/v5.2.12" + "source": "https://github.com/symfony/doctrine-messenger/tree/v5.3.8" }, "funding": [ { @@ -4951,7 +5009,7 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:47:41+00:00" + "time": "2021-09-01T13:32:48+00:00" }, { "name": "symfony/dom-crawler", @@ -5099,16 +5157,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.2.12", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "7ca5fa510345f6b8def43b18c900852defaee362" + "reference": "3bc60d0fba00ae8d1eaa9eb5ab11a2bbdd1fc321" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/7ca5fa510345f6b8def43b18c900852defaee362", - "reference": "7ca5fa510345f6b8def43b18c900852defaee362", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/3bc60d0fba00ae8d1eaa9eb5ab11a2bbdd1fc321", + "reference": "3bc60d0fba00ae8d1eaa9eb5ab11a2bbdd1fc321", "shasum": "" }, "require": { @@ -5147,7 +5205,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.2.12" + "source": "https://github.com/symfony/error-handler/tree/v5.3.7" }, "funding": [ { @@ -5163,7 +5221,7 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:54:19+00:00" + "time": "2021-08-28T15:07:08+00:00" }, { "name": "symfony/event-dispatcher", @@ -5458,16 +5516,16 @@ }, { "name": "symfony/finder", - "version": "v5.2.12", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "17f50e06018baec41551a71a15731287dbaab186" + "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/17f50e06018baec41551a71a15731287dbaab186", - "reference": "17f50e06018baec41551a71a15731287dbaab186", + "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", + "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", "shasum": "" }, "require": { @@ -5500,7 +5558,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.2.12" + "source": "https://github.com/symfony/finder/tree/v5.3.7" }, "funding": [ { @@ -5516,20 +5574,20 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:54:19+00:00" + "time": "2021-08-04T21:20:46+00:00" }, { "name": "symfony/flex", - "version": "v1.16.3", + "version": "v1.17.2", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "f05406b33681409b83285f4d2ff4efbca7c55b03" + "reference": "0170279814f86648c62aede39b100a343ea29962" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/f05406b33681409b83285f4d2ff4efbca7c55b03", - "reference": "f05406b33681409b83285f4d2ff4efbca7c55b03", + "url": "https://api.github.com/repos/symfony/flex/zipball/0170279814f86648c62aede39b100a343ea29962", + "reference": "0170279814f86648c62aede39b100a343ea29962", "shasum": "" }, "require": { @@ -5546,7 +5604,7 @@ "type": "composer-plugin", "extra": { "branch-alias": { - "dev-main": "1.16-dev" + "dev-main": "1.17-dev" }, "class": "Symfony\\Flex\\Flex" }, @@ -5568,7 +5626,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v1.16.3" + "source": "https://github.com/symfony/flex/tree/v1.17.2" }, "funding": [ { @@ -5584,7 +5642,7 @@ "type": "tidelift" } ], - "time": "2021-09-28T17:00:52+00:00" + "time": "2021-10-21T08:39:19+00:00" }, { "name": "symfony/form", @@ -6002,16 +6060,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.2.14", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "2a247de56fc8f5efdf1e098192128e8e509d370c" + "reference": "e36c8e5502b4f3f0190c675f1c1f1248a64f04e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/2a247de56fc8f5efdf1e098192128e8e509d370c", - "reference": "2a247de56fc8f5efdf1e098192128e8e509d370c", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e36c8e5502b4f3f0190c675f1c1f1248a64f04e5", + "reference": "e36c8e5502b4f3f0190c675f1c1f1248a64f04e5", "shasum": "" }, "require": { @@ -6055,7 +6113,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.2.14" + "source": "https://github.com/symfony/http-foundation/tree/v5.3.7" }, "funding": [ { @@ -6071,7 +6129,7 @@ "type": "tidelift" } ], - "time": "2021-07-29T06:18:06+00:00" + "time": "2021-08-27T11:20:35+00:00" }, { "name": "symfony/http-kernel", @@ -6777,16 +6835,16 @@ }, { "name": "symfony/options-resolver", - "version": "v5.2.12", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "1935d2e5329aba28cbb9ef6cc5687d007619d96d" + "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1935d2e5329aba28cbb9ef6cc5687d007619d96d", - "reference": "1935d2e5329aba28cbb9ef6cc5687d007619d96d", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4b78e55b179003a42523a362cc0e8327f7a69b5e", + "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e", "shasum": "" }, "require": { @@ -6826,7 +6884,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.2.12" + "source": "https://github.com/symfony/options-resolver/tree/v5.3.7" }, "funding": [ { @@ -6842,7 +6900,80 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:54:19+00:00" + "time": "2021-08-04T21:20:46+00:00" + }, + { + "name": "symfony/password-hasher", + "version": "v5.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/password-hasher.git", + "reference": "4bdaa0cca1fb3521bc1825160f3b5490c130bbda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/4bdaa0cca1fb3521bc1825160f3b5490c130bbda", + "reference": "4bdaa0cca1fb3521bc1825160f3b5490c130bbda", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.15" + }, + "conflict": { + "symfony/security-core": "<5.3" + }, + "require-dev": { + "symfony/console": "^5", + "symfony/security-core": "^5.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PasswordHasher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Chalas", + "email": "robin.chalas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides password hashing utilities", + "homepage": "https://symfony.com", + "keywords": [ + "hashing", + "password" + ], + "support": { + "source": "https://github.com/symfony/password-hasher/tree/v5.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-09-03T12:22:16+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -7807,16 +7938,16 @@ }, { "name": "symfony/redis-messenger", - "version": "v5.2.12", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/redis-messenger.git", - "reference": "451d5e2dc8d5e0ebb26edf9312292027f231bddc" + "reference": "4d775c1728fc3124a58376dc746e643d8ae80849" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/redis-messenger/zipball/451d5e2dc8d5e0ebb26edf9312292027f231bddc", - "reference": "451d5e2dc8d5e0ebb26edf9312292027f231bddc", + "url": "https://api.github.com/repos/symfony/redis-messenger/zipball/4d775c1728fc3124a58376dc746e643d8ae80849", + "reference": "4d775c1728fc3124a58376dc746e643d8ae80849", "shasum": "" }, "require": { @@ -7854,7 +7985,7 @@ "description": "Symfony Redis extension Messenger Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/redis-messenger/tree/v5.2.12" + "source": "https://github.com/symfony/redis-messenger/tree/v5.3.8" }, "funding": [ { @@ -7870,7 +8001,7 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:38:00+00:00" + "time": "2021-09-25T08:57:14+00:00" }, { "name": "symfony/routing", @@ -8063,37 +8194,41 @@ }, { "name": "symfony/security-core", - "version": "v5.2.14", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "4ce2b1e532fb3bb591ad4efa8cc43afc9242a247" + "reference": "62e5943d042aa5fc43d44b40209aa7304f68c56e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/4ce2b1e532fb3bb591ad4efa8cc43afc9242a247", - "reference": "4ce2b1e532fb3bb591ad4efa8cc43afc9242a247", + "url": "https://api.github.com/repos/symfony/security-core/zipball/62e5943d042aa5fc43d44b40209aa7304f68c56e", + "reference": "62e5943d042aa5fc43d44b40209aa7304f68c56e", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1", "symfony/event-dispatcher-contracts": "^1.1|^2", + "symfony/password-hasher": "^5.3", "symfony/polyfill-php80": "^1.16", "symfony/service-contracts": "^1.1.6|^2" }, "conflict": { "symfony/event-dispatcher": "<4.4", + "symfony/http-foundation": "<5.3", "symfony/ldap": "<4.4", "symfony/security-guard": "<4.4", "symfony/validator": "<5.2" }, "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", "psr/container": "^1.0|^2.0", "psr/log": "^1|^2|^3", + "symfony/cache": "^4.4|^5.0", "symfony/event-dispatcher": "^4.4|^5.0", "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", + "symfony/http-foundation": "^5.3", "symfony/ldap": "^4.4|^5.0", "symfony/translation": "^4.4|^5.0", "symfony/validator": "^5.2" @@ -8132,7 +8267,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v5.2.14" + "source": "https://github.com/symfony/security-core/tree/v5.3.8" }, "funding": [ { @@ -8148,20 +8283,20 @@ "type": "tidelift" } ], - "time": "2021-07-29T06:36:20+00:00" + "time": "2021-09-21T20:52:44+00:00" }, { "name": "symfony/security-csrf", - "version": "v5.2.12", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/symfony/security-csrf.git", - "reference": "f0af6689451582e55f6b3439362e72e536e916e4" + "reference": "94b533195cf7fb21f3fae8ce349861c6401d969e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/f0af6689451582e55f6b3439362e72e536e916e4", - "reference": "f0af6689451582e55f6b3439362e72e536e916e4", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/94b533195cf7fb21f3fae8ce349861c6401d969e", + "reference": "94b533195cf7fb21f3fae8ce349861c6401d969e", "shasum": "" }, "require": { @@ -8170,10 +8305,10 @@ "symfony/security-core": "^4.4|^5.0" }, "conflict": { - "symfony/http-foundation": "<4.4" + "symfony/http-foundation": "<5.3" }, "require-dev": { - "symfony/http-foundation": "^4.4|^5.0" + "symfony/http-foundation": "^5.3" }, "suggest": { "symfony/http-foundation": "For using the class SessionTokenStorage." @@ -8204,7 +8339,7 @@ "description": "Symfony Security Component - CSRF Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-csrf/tree/v5.2.12" + "source": "https://github.com/symfony/security-csrf/tree/v5.3.4" }, "funding": [ { @@ -8220,7 +8355,7 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:38:00+00:00" + "time": "2021-07-21T12:40:44+00:00" }, { "name": "symfony/security-guard", @@ -8553,16 +8688,16 @@ }, { "name": "symfony/stopwatch", - "version": "v5.2.12", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "24744393b122b8309bbcc7965972ae51a29a602d" + "reference": "b24c6a92c6db316fee69e38c80591e080e41536c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/24744393b122b8309bbcc7965972ae51a29a602d", - "reference": "24744393b122b8309bbcc7965972ae51a29a602d", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b24c6a92c6db316fee69e38c80591e080e41536c", + "reference": "b24c6a92c6db316fee69e38c80591e080e41536c", "shasum": "" }, "require": { @@ -8595,7 +8730,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.2.12" + "source": "https://github.com/symfony/stopwatch/tree/v5.3.4" }, "funding": [ { @@ -8611,7 +8746,7 @@ "type": "tidelift" } ], - "time": "2021-07-10T08:55:45+00:00" + "time": "2021-07-10T08:58:57+00:00" }, { "name": "symfony/string", @@ -9186,16 +9321,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.2.12", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "d5f42c357a6672d4e5960bba85e437850e9a7abb" + "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d5f42c357a6672d4e5960bba85e437850e9a7abb", - "reference": "d5f42c357a6672d4e5960bba85e437850e9a7abb", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eaaea4098be1c90c8285543e1356a09c8aa5c8da", + "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da", "shasum": "" }, "require": { @@ -9254,7 +9389,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.2.12" + "source": "https://github.com/symfony/var-dumper/tree/v5.3.8" }, "funding": [ { @@ -9270,20 +9405,20 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:54:19+00:00" + "time": "2021-09-24T15:59:58+00:00" }, { "name": "symfony/var-exporter", - "version": "v5.2.12", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "b7898a65fc91e7c41de7a88c7db9aee9c0d432f0" + "reference": "a7604de14bcf472fe8e33f758e9e5b7bf07d3b91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b7898a65fc91e7c41de7a88c7db9aee9c0d432f0", - "reference": "b7898a65fc91e7c41de7a88c7db9aee9c0d432f0", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/a7604de14bcf472fe8e33f758e9e5b7bf07d3b91", + "reference": "a7604de14bcf472fe8e33f758e9e5b7bf07d3b91", "shasum": "" }, "require": { @@ -9327,7 +9462,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v5.2.12" + "source": "https://github.com/symfony/var-exporter/tree/v5.3.8" }, "funding": [ { @@ -9343,7 +9478,7 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:38:00+00:00" + "time": "2021-08-31T12:49:16+00:00" }, { "name": "symfony/web-link", @@ -10047,16 +10182,16 @@ "packages-dev": [ { "name": "composer/semver", - "version": "3.2.5", + "version": "3.2.6", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9" + "reference": "83e511e247de329283478496f7a1e114c9517506" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/31f3ea725711245195f62e54ffa402d8ef2fdba9", - "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9", + "url": "https://api.github.com/repos/composer/semver/zipball/83e511e247de329283478496f7a1e114c9517506", + "reference": "83e511e247de329283478496f7a1e114c9517506", "shasum": "" }, "require": { @@ -10108,7 +10243,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.2.5" + "source": "https://github.com/composer/semver/tree/3.2.6" }, "funding": [ { @@ -10124,7 +10259,7 @@ "type": "tidelift" } ], - "time": "2021-05-24T12:41:47+00:00" + "time": "2021-10-25T11:34:17+00:00" }, { "name": "composer/xdebug-handler", @@ -11365,16 +11500,16 @@ }, { "name": "psy/psysh", - "version": "v0.10.8", + "version": "v0.10.9", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3" + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/e4573f47750dd6c92dca5aee543fa77513cbd8d3", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/01281336c4ae557fe4a994544f30d3a1bc204375", + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375", "shasum": "" }, "require": { @@ -11434,9 +11569,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.10.8" + "source": "https://github.com/bobthecow/psysh/tree/v0.10.9" }, - "time": "2021-04-10T16:23:39+00:00" + "time": "2021-10-10T13:37:39+00:00" }, { "name": "sebastian/cli-parser", @@ -12404,16 +12539,16 @@ }, { "name": "symfony/browser-kit", - "version": "v5.2.12", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "ed7ebe262cef150742f5b61d48a94d37513c5758" + "reference": "c1e3f64fcc631c96e2c5843b666db66679ced11c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ed7ebe262cef150742f5b61d48a94d37513c5758", - "reference": "ed7ebe262cef150742f5b61d48a94d37513c5758", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c1e3f64fcc631c96e2c5843b666db66679ced11c", + "reference": "c1e3f64fcc631c96e2c5843b666db66679ced11c", "shasum": "" }, "require": { @@ -12456,7 +12591,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.2.12" + "source": "https://github.com/symfony/browser-kit/tree/v5.3.4" }, "funding": [ { @@ -12472,11 +12607,11 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:38:00+00:00" + "time": "2021-07-21T12:40:44+00:00" }, { "name": "symfony/css-selector", - "version": "v5.2.12", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -12522,7 +12657,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.2.12" + "source": "https://github.com/symfony/css-selector/tree/v5.3.4" }, "funding": [ { @@ -12542,16 +12677,16 @@ }, { "name": "symfony/debug-bundle", - "version": "v5.2.12", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/symfony/debug-bundle.git", - "reference": "2cb76e25ca75afb0d52c1ba83d77cd4190ed5552" + "reference": "356c7d2acb6bc93b1c091255068ccfb9ad55a3e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/2cb76e25ca75afb0d52c1ba83d77cd4190ed5552", - "reference": "2cb76e25ca75afb0d52c1ba83d77cd4190ed5552", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/356c7d2acb6bc93b1c091255068ccfb9ad55a3e0", + "reference": "356c7d2acb6bc93b1c091255068ccfb9ad55a3e0", "shasum": "" }, "require": { @@ -12601,7 +12736,7 @@ "description": "Provides a tight integration of the Symfony Debug component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug-bundle/tree/v5.2.12" + "source": "https://github.com/symfony/debug-bundle/tree/v5.3.4" }, "funding": [ { @@ -12617,20 +12752,20 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:38:00+00:00" + "time": "2021-07-21T12:40:44+00:00" }, { "name": "symfony/maker-bundle", - "version": "v1.34.0", + "version": "v1.34.1", "source": { "type": "git", "url": "https://github.com/symfony/maker-bundle.git", - "reference": "c1ead8581cddeb4b2b9dcc8a3f00910093c4e5e8" + "reference": "c9ae401f3fa2b42881120d33ad79416630d1f2be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/c1ead8581cddeb4b2b9dcc8a3f00910093c4e5e8", - "reference": "c1ead8581cddeb4b2b9dcc8a3f00910093c4e5e8", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/c9ae401f3fa2b42881120d33ad79416630d1f2be", + "reference": "c9ae401f3fa2b42881120d33ad79416630d1f2be", "shasum": "" }, "require": { @@ -12689,7 +12824,7 @@ ], "support": { "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.34.0" + "source": "https://github.com/symfony/maker-bundle/tree/v1.34.1" }, "funding": [ { @@ -12705,7 +12840,7 @@ "type": "tidelift" } ], - "time": "2021-09-27T14:29:38+00:00" + "time": "2021-10-13T15:58:56+00:00" }, { "name": "symfony/phpunit-bridge", @@ -13107,6 +13242,7 @@ "ext-ctype": "*", "ext-curl": "*", "ext-iconv": "*", + "ext-openssl": "*", "ext-vips": "*" }, "platform-dev": [], diff --git a/plugins/ActivityPub/ActivityPub.php b/plugins/ActivityPub/ActivityPub.php index 805d0f2e6a..8a2e39beb7 100644 --- a/plugins/ActivityPub/ActivityPub.php +++ b/plugins/ActivityPub/ActivityPub.php @@ -9,8 +9,12 @@ use App\Core\Modules\Plugin; use App\Core\Router\RouteLoader; use App\Core\Router\Router; use App\Entity\Actor; +use App\Entity\LocalUser; +use App\Util\Exception\NoSuchActorException; +use App\Util\Nickname; use Exception; use Plugin\ActivityPub\Controller\Inbox; +use Plugin\ActivityPub\Entity\ActivitypubActor; use Plugin\ActivityPub\Util\Response\ActorResponse; use Plugin\ActivityPub\Util\Response\NoteResponse; use Plugin\ActivityPub\Util\Response\TypeResponse; @@ -19,6 +23,7 @@ use XML_XRD_Element_Link; class ActivityPub extends Plugin { + // ActivityStreams 2.0 Accept Headers public static array $accept_headers = [ 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', 'application/activity+json', @@ -26,6 +31,16 @@ class ActivityPub extends Plugin 'application/ld+json', ]; + // So that this isn't hardcoded everywhere + public const PUBLIC_TO = ['https://www.w3.org/ns/activitystreams#Public', + 'Public', + 'as:Public', + ]; + public const HTTP_CLIENT_HEADERS = [ + 'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'User-Agent' => 'GNUsocialBot ' . GNUSOCIAL_VERSION . ' - ' . GNUSOCIAL_PROJECT_URL, + ]; + public function version(): string { return '3.0.0'; @@ -43,23 +58,52 @@ class ActivityPub extends Plugin 'activitypub_inbox', '/inbox.json', [Inbox::class, 'handle'], - options: ['accept' => self::$accept_headers, 'format' => self::$accept_headers[0]] + options: ['accept' => self::$accept_headers, 'format' => self::$accept_headers[0]], ); $r->connect( 'activitypub_actor_inbox', '/actor/{gsactor_id<\d+>}/inbox.json', [Inbox::class, 'handle'], - options: ['accept' => self::$accept_headers, 'format' => self::$accept_headers[0]] + options: ['accept' => self::$accept_headers, 'format' => self::$accept_headers[0]], ); $r->connect( 'activitypub_actor_outbox', '/actor/{gsactor_id<\d+>}/outbox.json', [Inbox::class, 'handle'], - options: ['accept' => self::$accept_headers, 'format' => self::$accept_headers[0]] + options: ['accept' => self::$accept_headers, 'format' => self::$accept_headers[0]], ); return Event::next; } + public static function getActorByUri(string $resource, ?bool $attempt_fetch = true): Actor + { + // Try local + if (filter_var($resource, \FILTER_VALIDATE_URL) !== false) { + // This means $resource is a valid url + $resource_parts = parse_url($resource); + // TODO: Use URLMatcher + if ($resource_parts['host'] === $_ENV['SOCIAL_DOMAIN']) { // XXX: Common::config('site', 'server')) { + $str = $resource_parts['path']; + // actor_view_nickname + $renick = '/\/@(' . Nickname::DISPLAY_FMT . ')\/?/m'; + // actor_view_id + $reuri = '/\/actor\/(\d+)\/?/m'; + if (preg_match_all($renick, $str, $matches, \PREG_SET_ORDER, 0) === 1) { + return LocalUser::getWithPK(['nickname' => $matches[0][1]])->getActor(); + } elseif (preg_match_all($reuri, $str, $matches, \PREG_SET_ORDER, 0) === 1) { + return Actor::getById((int) $matches[0][1]); + } + } + } + // Try remote + $aprofile = ActivitypubActor::getByAddr($resource); + if ($aprofile instanceof ActivitypubActor) { + return Actor::getById($aprofile->getActorId()); + } else { + throw new NoSuchActorException("From URI: {$resource}"); + } + } + /** * @throws Exception */ @@ -99,21 +143,19 @@ class ActivityPub extends Plugin /** * Add activity+json mimetype on WebFinger * - * @param XML_XRD $xrd - * @param Managed_DataObject $object - * * @throws Exception */ - public function onEndWebFingerProfileLinks(XML_XRD $xrd, Actor $object) + public function onEndWebFingerProfileLinks(XML_XRD $xrd, Actor $object): bool { if ($object->isPerson()) { $link = new XML_XRD_Element_Link( - 'self', - $object->getUri(Router::ABSOLUTE_URL),//Router::url('actor_view_id', ['id' => $object->getId()], Router::ABSOLUTE_URL), - 'application/activity+json' + rel: 'self', + href: $object->getUri(Router::ABSOLUTE_URL),//Router::url('actor_view_id', ['id' => $object->getId()], Router::ABSOLUTE_URL), + type: 'application/activity+json', ); $xrd->links[] = clone $link; } + return Event::next; } public function onFreeNetworkGenerateLocalActorUri(int $actor_id, ?array &$actor_uri): bool diff --git a/plugins/ActivityPub/Entity/ActivitypubActivity.php b/plugins/ActivityPub/Entity/ActivitypubActivity.php index 6449bc5265..7549162852 100644 --- a/plugins/ActivityPub/Entity/ActivitypubActivity.php +++ b/plugins/ActivityPub/Entity/ActivitypubActivity.php @@ -42,7 +42,6 @@ class ActivitypubActivity extends Entity { // {{{ Autocode // @codeCoverageIgnoreStart - private int $id; private string $activity_uri; private int $actor_id; private string $verb; @@ -54,17 +53,6 @@ class ActivitypubActivity extends Entity private DateTimeInterface $created; private DateTimeInterface $modified; - public function setId(int $id): self - { - $this->id = $id; - return $this; - } - - public function getId(): int - { - return $this->id; - } - public function getActivityUri(): string { return $this->activity_uri; @@ -183,7 +171,6 @@ class ActivitypubActivity extends Entity return [ 'name' => 'activitypub_activity', 'fields' => [ - 'id' => ['type' => 'serial', 'not null' => true], 'activity_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Activity\'s URI'], 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'who made the note'], 'verb' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'internal activity verb, influenced by activity pub verbs'], @@ -195,7 +182,7 @@ class ActivitypubActivity extends Entity 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], ], - 'primary key' => ['id'], + 'primary key' => ['activity_uri'], 'indexes' => [ 'activity_activity_uri_idx' => ['activity_uri'], 'activity_object_uri_idx' => ['object_uri'], diff --git a/plugins/ActivityPub/Entity/ActivitypubActor.php b/plugins/ActivityPub/Entity/ActivitypubActor.php index 7aa241e38d..c4b0f9d6a9 100644 --- a/plugins/ActivityPub/Entity/ActivitypubActor.php +++ b/plugins/ActivityPub/Entity/ActivitypubActor.php @@ -32,8 +32,16 @@ declare(strict_types = 1); namespace Plugin\ActivityPub\Entity; +use App\Core\Cache; use App\Core\Entity; +use function App\Core\I18n\_m; +use App\Core\Log; +use App\Entity\Actor; +use Component\FreeNetwork\Util\Discovery; use DateTimeInterface; +use Exception; +use Plugin\ActivityPub\Util\DiscoveryHints; +use Plugin\ActivityPub\Util\Explorer; /** * Table Definition for activitypub_actor @@ -121,6 +129,97 @@ class ActivitypubActor extends Entity // @codeCoverageIgnoreEnd // }}} Autocode + /** + * Look up, and if necessary create, an Activitypub_profile for the remote + * entity with the given WebFinger address. + * This should never return null -- you will either get an object or + * an exception will be thrown. + * + * @param string $addr WebFinger address + * + * @throws Exception on error conditions + */ + public static function getByAddr(string $addr): self + { + // Normalize $addr, i.e. add 'acct:' if missing + $addr = Discovery::normalize($addr); + + // Try the cache + $uri = Cache::get(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), fn () => false); + + if ($uri !== false) { + if (\is_null($uri)) { + // TRANS: Exception. + throw new Exception(_m('Not a valid WebFinger address (via cache).')); + } + try { + return self::fromUri($uri); + } catch (Exception $e) { + Log::error(sprintf(__METHOD__ . ': WebFinger address cache inconsistent with database, did not find Activitypub_profile uri==%s', $uri)); + Cache::set(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), false); + } + } + + // Now, try some discovery + + $disco = new Discovery(); + + try { + $xrd = $disco->lookup($addr); + } catch (Exception $e) { + // Save negative cache entry so we don't waste time looking it up again. + // @todo FIXME: Distinguish temporary failures? + Cache::set(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), null); + // TRANS: Exception. + throw new Exception(_m('Not a valid WebFinger address: ' . $e->getMessage())); + } + + $hints = array_merge( + ['webfinger' => $addr], + DiscoveryHints::fromXRD($xrd), + ); + + if (\array_key_exists('activitypub', $hints)) { + $uri = $hints['activitypub']; + try { + LOG::info("Discovery on acct:{$addr} with URI:{$uri}"); + $aprofile = self::fromUri($hints['activitypub']); + Cache::set(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), $aprofile->getUri()); + return $aprofile; + } catch (Exception $e) { + Log::warning("Failed creating profile from URI:'{$uri}', error:" . $e->getMessage()); + throw $e; + // keep looking + // + // @todo FIXME: This means an error discovering from profile page + // may give us a corrupt entry using the webfinger URI, which + // will obscure the correct page-keyed profile later on. + } + } + + // XXX: try hcard + // XXX: try FOAF + + // TRANS: Exception. %s is a WebFinger address. + throw new Exception(sprintf(_m('Could not find a valid profile for "%s".'), $addr)); + } + + /** + * Ensures a valid Activitypub_profile when provided with a valid URI. + * + * @param bool $grab_online whether to try online grabbing, defaults to true + * + * @throws Exception if it isn't possible to return an Activitypub_profile + */ + public static function fromUri(string $url, bool $grab_online = true): self + { + try { + return Explorer::get_profile_from_url($url, $grab_online); + } catch (Exception $e) { + throw new Exception('No valid ActivityPub profile found for given URI.', previous: $e); + } + } + public static function schemaDef(): array { return [ diff --git a/plugins/ActivityPub/Util/DiscoveryHints.php b/plugins/ActivityPub/Util/DiscoveryHints.php new file mode 100644 index 0000000000..3e9a662e28 --- /dev/null +++ b/plugins/ActivityPub/Util/DiscoveryHints.php @@ -0,0 +1,179 @@ +. +use App\Core\Event; +use App\Core\HTTPClient; +use Component\FreeNetwork\Util\Discovery; +use Component\FreeNetwork\Util\WebfingerResource\WebfingerResourceActor; +use Mf2 as Mf2; +use XML_XRD; + +/** + * ActivityPub implementation for GNU social + * + * @package GNUsocial + * + * @author Evan Prodromou + * @author Brion Vibber + * @author James Walker + * @author Siebrand Mazeland + * @author Mikael Nordfeldth + * @author Diogo Cordeiro + * @copyright 2010, 2019, 2021 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + * + * @see http://www.gnu.org/software/social/ + */ +class DiscoveryHints +{ + public static function fromXRD(XML_XRD $xrd) + { + $hints = []; + + if (Event::handle('StartDiscoveryHintsFromXRD', [$xrd, &$hints])) { + foreach ($xrd->links as $link) { + if ($link->rel === 'self' && $link->type === 'application/activity+json') { + $hints['activitypub'] = $link->href; + break; + } + } + Event::handle('EndDiscoveryHintsFromXRD', [$xrd, &$hints]); + } + + return $hints; + } +} +//class DiscoveryHints +//{ +// public static function fromXRD(XML_XRD $xrd) +// { +// $hints = []; +// +// if (Event::handle('StartDiscoveryHintsFromXRD', [$xrd, &$hints])) { +// foreach ($xrd->links as $link) { +// switch ($link->rel) { +// case WebfingerResourceActor::PROFILEPAGE: +// $hints['profileurl'] = $link->href; +// break; +// case Discovery::UPDATESFROM: +// if (empty($link->type) || $link->type == 'application/atom+xml') { +// $hints['feedurl'] = $link->href; +// } +// break; +// case Discovery::HCARD: +// case Discovery::MF2_HCARD: +// $hints['hcard'] = $link->href; +// break; +// default: +// break; +// } +// } +// Event::handle('EndDiscoveryHintsFromXRD', [$xrd, &$hints]); +// } +// +// return $hints; +// } +// +// public static function fromHcardUrl($url) +// { +// $response = HTTPClient::get($url, ['headers' => ['Accept' => 'text/html,application/xhtml+xml']]); +// +// if (!HTTPClient::statusCodeIsOkay($response)) { +// return null; +// } +// +// return self::hcardHints( +// $response->getContent(), +// HTTPClient::getEffectiveUrl($response) +// ); +// } +// +// public static function hcardHints($body, $url) +// { +// $hcard = self::hcard($body, $url); +// +// if (empty($hcard)) { +// return []; +// } +// +// $hints = []; +// +// // XXX: don't copy stuff into an array and then copy it again +// +// if (array_key_exists('nickname', $hcard) && !empty($hcard['nickname'][0])) { +// $hints['nickname'] = $hcard['nickname'][0]; +// } +// +// if (array_key_exists('name', $hcard) && !empty($hcard['name'][0])) { +// $hints['fullname'] = $hcard['name'][0]; +// } +// +// if (array_key_exists('photo', $hcard) && count($hcard['photo'])) { +// $hints['avatar'] = $hcard['photo'][0]; +// } +// +// if (array_key_exists('note', $hcard) && !empty($hcard['note'][0])) { +// $hints['bio'] = $hcard['note'][0]; +// } +// +// if (array_key_exists('adr', $hcard) && !empty($hcard['adr'][0])) { +// $hints['location'] = $hcard['adr'][0]['value']; +// } +// +// if (array_key_exists('url', $hcard) && !empty($hcard['url'][0])) { +// $hints['homepage'] = $hcard['url'][0]; +// } +// +// return $hints; +// } +// +// private static function hcard($body, $url) +// { +// $mf2 = new Mf2\Parser($body, $url); +// $mf2 = $mf2->parse(); +// +// if (empty($mf2['items'])) { +// return null; +// } +// +// $hcards = []; +// +// foreach ($mf2['items'] as $item) { +// if (!in_array('h-card', $item['type'])) { +// continue; +// } +// +// // We found a match, return it immediately +// if (isset($item['properties']['url']) && in_array($url, $item['properties']['url'])) { +// return $item['properties']; +// } +// +// // Let's keep all the hcards for later, to return one of them at least +// $hcards[] = $item['properties']; +// } +// +// // No match immediately for the url we expected, but there were h-cards found +// if (count($hcards) > 0) { +// return $hcards[0]; +// } +// +// return null; +// } +//} diff --git a/plugins/ActivityPub/Util/Explorer.php b/plugins/ActivityPub/Util/Explorer.php new file mode 100644 index 0000000000..ecf2363948 --- /dev/null +++ b/plugins/ActivityPub/Util/Explorer.php @@ -0,0 +1,373 @@ +. + +namespace Plugin\ActivityPub\Util; + +use App\Core\DB\DB; +use App\Core\HTTPClient; +use App\Core\Log; +use App\Core\Security; +use App\Entity\Actor; +use App\Util\Exception\NoSuchActorException; +use App\Util\Formatting; +use DateTime; +use Exception; +use Plugin\ActivityPub\ActivityPub; +use Plugin\ActivityPub\Entity\ActivitypubActor; +use Plugin\ActivityPub\Entity\ActivitypubRsa; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; + +/** + * ActivityPub implementation for GNU social + * + * @package GNUsocial + * + * @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + * + * @see http://www.gnu.org/software/social/ + */ + +/** + * ActivityPub's own Explorer + * + * Allows to discovery new remote actors + * + * @author Diogo Peralta Cordeiro (@diogo.site) + * + * @category Plugin + * @package GNUsocial + * + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + */ +class Explorer +{ + private $discovered_actor_profiles = []; + + /** + * Shortcut function to get a single profile from its URL. + * + * @param bool $grab_online whether to try online grabbing, defaults to true + * + * @throws ClientExceptionInterface + * @throws NoSuchActorException + * @throws RedirectionExceptionInterface + * @throws ServerExceptionInterface + * @throws TransportExceptionInterface + * + * @return Actor + */ + public static function get_profile_from_url(string $url, bool $grab_online = true): ActivitypubActor + { + $discovery = new self(); + // Get valid Actor object + $actor_profile = $discovery->lookup($url, $grab_online); + if (!empty($actor_profile)) { + return $actor_profile[0]; + } + throw new NoSuchActorException('Invalid Actor.'); + } + + /** + * Get every profile from the given URL + * This function cleans the $this->discovered_actor_profiles array + * so that there is no erroneous data + * + * @param string $url User's url + * @param bool $grab_online whether to try online grabbing, defaults to true + * + * @throws ClientExceptionInterface + * @throws NoSuchActorException + * @throws RedirectionExceptionInterface + * @throws ServerExceptionInterface + * @throws TransportExceptionInterface + * + * @return array of Profile objects + */ + public function lookup(string $url, bool $grab_online = true) + { + if (\in_array($url, ActivityPub::PUBLIC_TO)) { + return []; + } + + Log::debug('ActivityPub Explorer: Started now looking for ' . $url); + $this->discovered_actor_profiles = []; + + return $this->_lookup($url, $grab_online); + } + + /** + * Get every profile from the given URL + * This is a recursive function that will accumulate the results on + * $discovered_actor_profiles array + * + * @param string $url User's url + * @param bool $grab_online whether to try online grabbing, defaults to true + * + * @throws ClientExceptionInterface + * @throws NoSuchActorException + * @throws RedirectionExceptionInterface + * @throws ServerExceptionInterface + * @throws TransportExceptionInterface + * + * @return array of Profile objects + */ + private function _lookup(string $url, bool $grab_online = true): array + { + $grab_known = $this->grab_known_user($url); + + // First check if we already have it locally and, if so, return it. + // If the known fetch fails and remote grab is required: store locally and return. + if (!$grab_known && (!$grab_online || !$this->grab_remote_user($url))) { + throw new NoSuchActorException('Actor not found.'); + } + + return $this->discovered_actor_profiles; + } + + /** + * Get a known user profile from its URL and joins it on + * $this->discovered_actor_profiles + * + * @param string $uri Actor's uri + * + * @throws Exception + * @throws NoSuchActorException + * + * @return bool success state + */ + private function grab_known_user(string $uri): bool + { + Log::debug('ActivityPub Explorer: Searching locally for ' . $uri . ' offline.'); + + // Try standard ActivityPub route + // Is this a known filthy little mudblood? + $aprofile = self::get_aprofile_by_url($uri); + if ($aprofile instanceof ActivitypubActor) { + Log::debug('ActivityPub Explorer: Found a known Aprofile for ' . $uri); + + // We found something! + $this->discovered_actor_profiles[] = $aprofile; + return true; + } else { + Log::debug('ActivityPub Explorer: Unable to find a known Aprofile for ' . $uri); + } + + return false; + } + + /** + * Get a remote user(s) profile(s) from its URL and joins it on + * $this->discovered_actor_profiles + * + * @param string $url User's url + * + * @throws ClientExceptionInterface + * @throws NoSuchActorException + * @throws RedirectionExceptionInterface + * @throws ServerExceptionInterface + * @throws TransportExceptionInterface + * + * @return bool success state + */ + private function grab_remote_user(string $url): bool + { + Log::debug('ActivityPub Explorer: Trying to grab a remote actor for ' . $url); + $response = HTTPClient::get($url, ['headers' => ACTIVITYPUB::HTTP_CLIENT_HEADERS]); + $res = json_decode($response->getContent(), true); + if ($response->getStatusCode() == 410) { // If it was deleted + return true; // Nothing to add. + } elseif (!HTTPClient::statusCodeIsOkay($response)) { // If it is unavailable + return false; // Try to add at another time. + } + if (\is_null($res)) { + Log::debug('ActivityPub Explorer: Invalid JSON returned from given Actor URL: ' . $response->getContent()); + return true; // Nothing to add. + } + + if (isset($res['type']) && $res['type'] === 'OrderedCollection' && isset($res['first'])) { // It's a potential collection of actors!!! + Log::debug('ActivityPub Explorer: Found a collection of actors for ' . $url); + $this->travel_collection($res['first']); + return true; + } elseif (self::validate_remote_response($res)) { + Log::debug('ActivityPub Explorer: Found a valid remote actor for ' . $url); + $this->discovered_actor_profiles[] = $this->store_profile($res); + return true; + } else { + Log::debug('ActivityPub Explorer: Invalid potential remote actor while grabbing remotely: ' . $url . '. He returned the following: ' . json_encode($res, \JSON_UNESCAPED_SLASHES)); + return false; + } + + return false; + } + + /** + * Save remote user profile in known instance + * + * @param array $res remote response + * + * @throws Exception + * @throws NoSuchActorException + * + * @return Actor remote Profile object + */ + private function store_profile(array $res): ActivitypubActor + { + // Actor + $actor_map = [ + 'nickname' => $res['preferredUsername'], + 'fullname' => $res['name'] ?? null, + 'created' => new DateTime($res['published'] ?? 'now'), + 'bio' => isset($res['summary']) ? mb_substr(Security::sanitize($res['summary']), 0, 1000) : null, + 'homepage' => $res['url'] ?? $res['id'], + 'modified' => new DateTime(), + ]; + + $actor = new Actor(); + foreach ($actor_map as $prop => $val) { + $set = Formatting::snakeCaseToCamelCase("set_{$prop}"); + $actor->{$set}($val); + } + + DB::persist($actor); + + // ActivityPub Actor + $aprofile = new ActivitypubActor(); + $aprofile->setInboxUri($res['inbox']); + $aprofile->setInboxSharedUri($res['endpoints']['sharedInbox'] ?? $res['inbox']); + $aprofile->setUri($res['id']); + $aprofile->setActorId($actor->getId()); + $aprofile->setCreated(new DateTime()); + $aprofile->setModified(new DateTime()); + + DB::persist($aprofile); + + // Public Key + $apRSA = new ActivitypubRsa(); + $apRSA->setActorId($actor->getID()); + $apRSA->setPublicKey($res['publicKey']['publicKeyPem']); + $apRSA->setCreated(new DateTime()); + $apRSA->setModified(new DateTime()); + + DB::persist($apRSA); + + // Avatar + //if (isset($res['icon']['url'])) { + // try { + // $this->update_avatar($profile, $res['icon']['url']); + // } catch (Exception $e) { + // // Let the exception go, it isn't a serious issue + // Log::debug('ActivityPub Explorer: An error ocurred while grabbing remote avatar: ' . $e->getMessage()); + // } + //} + + return $aprofile; + } + + /** + * Validates a remote response in order to determine whether this + * response is a valid profile or not + * + * @param array $res remote response + * + * @return bool success state + */ + public static function validate_remote_response(array $res): bool + { + return !(!isset($res['id'], $res['preferredUsername'], $res['inbox'], $res['publicKey']['publicKeyPem'])); + } + + /** + * Get a ActivityPub Profile from it's uri + * + * @param string $v URL + * + * @return ActivitypubActor|bool false if fails | Aprofile object if successful + */ + public static function get_aprofile_by_url(string $v): ActivitypubActor|bool + { + $aprofile = ActivitypubActor::getWithPK(['uri' => $v]); + return \is_null($aprofile) ? false : ActivitypubActor::getWithPK(['uri' => $v]); + } + + /** + * Allows the Explorer to transverse a collection of persons. + * + * @throws NoSuchActorException + */ + private function travel_collection(string $url): bool + { + $response = HTTPClient::get($url, ['headers' => ACTIVITYPUB::HTTP_CLIENT_HEADERS]); + $res = json_decode($response->getContent(), true); + + if (!isset($res['orderedItems'])) { + return false; + } + + foreach ($res['orderedItems'] as $profile) { + if ($this->_lookup($profile) == false) { + Log::debug('ActivityPub Explorer: Found an invalid actor for ' . $profile); + } + } + // Go through entire collection + if (!\is_null($res['next'])) { + $this->travel_collection($res['next']); + } + + return true; + } + + /** + * Get a remote user array from its URL (this function is only used for + * profile updating and shall not be used for anything else) + * + * @param string $url User's url + * + * @throws ClientExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ServerExceptionInterface + * @throws TransportExceptionInterface + * + * @return array|false If it is able to fetch, false if it's gone + * // Exceptions when network issues or unsupported Activity format + */ + public static function get_remote_user_activity(string $url): bool|array + { + $response = HTTPClient::get($url, ['headers' => ACTIVITYPUB::HTTP_CLIENT_HEADERS]); + // If it was deleted + if ($response->getStatusCode() == 410) { + return false; + } elseif (!HTTPClient::statusCodeIsOkay($response)) { // If it is unavailable + throw new Exception('Non Ok Status Code for given Actor URL.'); + } + $res = json_decode($response->getContent(), true); + if (\is_null($res)) { + Log::debug('ActivityPub Explorer: Invalid JSON returned from given Actor URL: ' . $response->getContent()); + throw new Exception('Given Actor URL didn\'t return a valid JSON.'); + } + if (self::validate_remote_response($res)) { + Log::debug('ActivityPub Explorer: Found a valid remote actor for ' . $url); + return $res; + } + throw new Exception('ActivityPub Explorer: Failed to get activity.'); + } +} diff --git a/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToEntity.php b/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToEntity.php index 3f072debb0..105842dfaf 100644 --- a/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToEntity.php +++ b/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToEntity.php @@ -6,10 +6,12 @@ namespace Plugin\ActivityPub\Util\Model\AS2ToEntity; use App\Core\DB\DB; use App\Core\Event; +use App\Entity\Actor; +use App\Entity\Note; use App\Util\Exception\ClientException; use App\Util\Formatting; -use Component\FreeNetwork\Entity\FreenetworkActor; use DateTime; +use Plugin\ActivityPub\ActivityPub; use Plugin\ActivityPub\Entity\ActivitypubActivity; abstract class AS2ToEntity @@ -35,40 +37,56 @@ abstract class AS2ToEntity */ public static function store(array $activity, ?string $source = null): array { - $map = [ - 'activity_uri' => $activity['id'], - 'actor_id' => FreenetworkActor::getOrCreateByRemoteUri(actor_uri: $activity['actor'])->getActorId(), - 'verb' => self::activity_stream_two_verb_to_gs_verb($activity['type']), - 'object_type' => self::activity_stream_two_object_type_to_gs_table($activity['object']['type']), - 'object_uri' => $activity['object']['id'], - 'is_local' => false, - 'created' => new DateTime($activity['published'] ?? 'now'), - 'modified' => new DateTime(), - 'source' => $source, - ]; + $act = ActivitypubActivity::getWithPK(['activity_uri' => $activity['id']]); + if (\is_null($act)) { + $actor = ActivityPub::getActorByUri($activity['actor']); + $map = [ + 'activity_uri' => $activity['id'], + 'actor_id' => $actor->getId(), + 'verb' => self::activity_stream_two_verb_to_gs_verb($activity['type']), + 'object_type' => self::activity_stream_two_object_type_to_gs_table($activity['object']['type']), + 'object_uri' => $activity['object']['id'], + 'is_local' => false, + 'created' => new DateTime($activity['published'] ?? 'now'), + 'modified' => new DateTime(), + 'source' => $source, + ]; - $act = new ActivitypubActivity(); - foreach ($map as $prop => $val) { - $set = Formatting::snakeCaseToCamelCase("set_{$prop}"); - $act->{$set}($val); + $act = new ActivitypubActivity(); + foreach ($map as $prop => $val) { + $set = Formatting::snakeCaseToCamelCase("set_{$prop}"); + $act->{$set}($val); + } + + $obj = null; + switch ($activity['object']['type']) { + case 'Note': + $obj = AS2ToNote::translate($activity['object'], $source, $activity['actor'], $act); + break; + default: + if (!Event::handle('ActivityPubObject', [$activity['object']['type'], $activity['object'], &$obj])) { + throw new ClientException('Unsupported Object type.'); + } + break; + } + + DB::persist($obj); + $act->setObjectId($obj->getId()); + DB::persist($act); + } else { + $actor = Actor::getById($act->getActorId()); + switch ($activity['object']['type']) { + case 'Note': + $obj = Note::getWithPK(['id' => $act->getObjectId()]); + break; + default: + if (!Event::handle('ActivityPubObject', [$activity['object']['type'], $activity['object'], &$obj])) { + throw new ClientException('Unsupported Object type.'); + } + break; + } } - $obj = null; - switch ($activity['object']['type']) { - case 'Note': - $obj = AS2ToNote::translate($activity['object'], $source); - break; - default: - if (!Event::handle('ActivityPubObject', [$activity['object']['type'], $activity['object'], &$obj])) { - throw new ClientException('Unsupported Object type.'); - } - break; - } - - DB::persist($obj); - $act->setObjectId($obj->getId()); - DB::persist($act); - - return [$act, $obj]; + return [$actor, $act, $obj]; } } diff --git a/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToGSActor.php b/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToGSActor.php deleted file mode 100644 index 7dc7dec89b..0000000000 --- a/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToGSActor.php +++ /dev/null @@ -1,49 +0,0 @@ - false, - 'created' => new DateTime($args['published'] ?? 'now'), - 'content' => $args['content'] ?? null, - 'content_type' => 'text/html', - 'rendered' => null, - 'modified' => new DateTime(), - 'source' => $source, - ]; - if ($map['content'] !== null) { - Event::handle('RenderNoteContent', [ - $map['content'], - $map['content_type'], - &$map['rendered'], - Actor::getById(1), // just for testing - null, // reply to - ]); - } - - $obj = new Note(); - foreach ($map as $prop => $val) { - $set = Formatting::snakeCaseToCamelCase("set_{$prop}"); - $obj->{$set}($val); - } - return $obj; - } -} diff --git a/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToNote.php b/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToNote.php index 8382443857..dce843e59a 100644 --- a/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToNote.php +++ b/plugins/ActivityPub/Util/Model/AS2ToEntity/AS2ToNote.php @@ -8,19 +8,24 @@ use App\Core\Event; use App\Entity\Actor; use App\Entity\Note; use App\Util\Formatting; -use Component\FreeNetwork\Entity\FreenetworkActor; use DateTime; use Exception; +use Plugin\ActivityPub\ActivityPub; +use Plugin\ActivityPub\Entity\ActivitypubActivity; abstract class AS2ToNote { /** *@throws Exception */ - public static function translate(array $object, ?string $source = null): Note + public static function translate(array $object, ?string $source, ?string $actor_uri, ?ActivitypubActivity $act = null): Note { - $actor_id = FreenetworkActor::getOrCreateByRemoteUri(actor_uri: $object['attributedTo'])->getActorId(); - $map = [ + if (isset($actor_uri) && $actor_uri === $object['attributedTo']) { + $actor_id = $act->getActorId(); + } else { + $actor_id = ActivityPub::getActorByUri($object['attributedTo'])->getId(); + } + $map = [ 'is_local' => false, 'created' => new DateTime($object['published'] ?? 'now'), 'content' => $object['content'] ?? null, diff --git a/plugins/ActivityPub/composer.json b/plugins/ActivityPub/composer.json new file mode 100644 index 0000000000..d837ad3d68 --- /dev/null +++ b/plugins/ActivityPub/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "masterminds/html5": "^2.7", + "mf2/mf2": "^0.4.6" + } +} diff --git a/plugins/ImageEncoder/composer.json b/plugins/ImageEncoder/composer.json index f64071c66f..caef00e53f 100644 --- a/plugins/ImageEncoder/composer.json +++ b/plugins/ImageEncoder/composer.json @@ -1,5 +1,6 @@ { "require": { + "ext-vips": "*", "jcupitt/vips": "1.0.8" } } diff --git a/symfony.lock b/symfony.lock index 6e9d1f7eca..a5e69ee695 100644 --- a/symfony.lock +++ b/symfony.lock @@ -172,6 +172,9 @@ "masterminds/html5": { "version": "2.7.4" }, + "mf2/mf2": { + "version": "0.4.6" + }, "ml/iri": { "version": "1.1.4" }, @@ -578,6 +581,9 @@ "symfony/options-resolver": { "version": "v5.2.4" }, + "symfony/password-hasher": { + "version": "v5.3.8" + }, "symfony/phpunit-bridge": { "version": "5.1", "recipe": {