From 977d28353c44cc8e2e15e390da99ea38b94827fa Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 20:52:46 +0000 Subject: [PATCH 01/12] Transmit push subscriptions --- composer.json | 5 +- composer.lock | 703 +++++++++++++++++++++++++++++++- src/Model/Subscription.php | 2 + src/Worker/PushSubscription.php | 59 +++ 4 files changed, 765 insertions(+), 4 deletions(-) create mode 100644 src/Worker/PushSubscription.php diff --git a/composer.json b/composer.json index 4e485e4e9..1e3fcfcff 100644 --- a/composer.json +++ b/composer.json @@ -67,7 +67,8 @@ "npm-asset/moment": "^2.24", "npm-asset/perfect-scrollbar": "0.6.16", "npm-asset/textcomplete": "^0.18.2", - "npm-asset/typeahead.js": "^0.11.1" + "npm-asset/typeahead.js": "^0.11.1", + "minishlink/web-push": "^6.0" }, "repositories": [ { @@ -94,7 +95,7 @@ }, "config": { "platform": { - "php": "7.0" + "php": "7.2" }, "autoloader-suffix": "Friendica", "optimize-autoloader": true, diff --git a/composer.lock b/composer.lock index bf85afb79..fdfe37bbb 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": "5cf680863afa011d3c2c9f4e0b817690", + "content-hash": "9560ab7f3a3b33c673672c11813ae52e", "packages": [ { "name": "asika/simple-console", @@ -253,6 +253,58 @@ }, "type": "bower-asset-library" }, + { + "name": "brick/math", + "version": "0.9.2", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "dff976c2f3487d42c1db75a3b180e2b9f0e72ce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/dff976c2f3487d42c1db75a3b180e2b9f0e72ce0", + "reference": "dff976c2f3487d42c1db75a3b180e2b9f0e72ce0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", + "vimeo/psalm": "4.3.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/brick/math", + "type": "tidelift" + } + ], + "time": "2021-01-20T22:51:39+00:00" + }, { "name": "composer/ca-bundle", "version": "1.2.9", @@ -513,6 +565,77 @@ ], "time": "2020-06-29T00:56:53+00:00" }, + { + "name": "fgrosse/phpasn1", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/fgrosse/PHPASN1.git", + "reference": "20299033c35f4300eb656e7e8e88cf52d1d6694e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/20299033c35f4300eb656e7e8e88cf52d1d6694e", + "reference": "20299033c35f4300eb656e7e8e88cf52d1d6694e", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.3", + "satooshi/php-coveralls": "~2.0" + }, + "suggest": { + "ext-bcmath": "BCmath is the fallback extension for big integer calculations", + "ext-curl": "For loading OID information from the web if they have not bee defined statically", + "ext-gmp": "GMP is the preferred extension for big integer calculations", + "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "FG\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Friedrich Große", + "email": "friedrich.grosse@gmail.com", + "homepage": "https://github.com/FGrosse", + "role": "Author" + }, + { + "name": "All contributors", + "homepage": "https://github.com/FGrosse/PHPASN1/contributors" + } + ], + "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", + "homepage": "https://github.com/FGrosse/PHPASN1", + "keywords": [ + "DER", + "asn.1", + "asn1", + "ber", + "binary", + "decoding", + "encoding", + "x.509", + "x.690", + "x509", + "x690" + ], + "time": "2021-04-24T19:01:55+00:00" + }, { "name": "friendica/json-ld", "version": "1.1.1", @@ -1059,6 +1182,65 @@ ], "time": "2019-12-02T02:32:27+00:00" }, + { + "name": "minishlink/web-push", + "version": "v6.0.5", + "source": { + "type": "git", + "url": "https://github.com/web-push-libs/web-push-php.git", + "reference": "d87e9e3034ca2b95b1822b1b335e7761c14b89f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-push-libs/web-push-php/zipball/d87e9e3034ca2b95b1822b1b335e7761c14b89f6", + "reference": "d87e9e3034ca2b95b1822b1b335e7761c14b89f6", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^7.0.1|^6.2", + "php": ">=7.2", + "web-token/jwt-key-mgmt": "^2.0", + "web-token/jwt-signature": "^2.0", + "web-token/jwt-signature-algorithm-ecdsa": "^2.0", + "web-token/jwt-util-ecc": "^2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.14", + "phpstan/phpstan": "^0.11|^0.12", + "phpunit/phpunit": "^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Minishlink\\WebPush\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Louis Lagrange", + "email": "lagrange.louis@gmail.com", + "homepage": "https://github.com/Minishlink" + } + ], + "description": "Web Push library for PHP", + "homepage": "https://github.com/web-push-libs/web-push-php", + "keywords": [ + "Push API", + "WebPush", + "notifications", + "push", + "web" + ], + "time": "2021-04-08T15:22:50+00:00" + }, { "name": "mobiledetect/mobiledetectlib", "version": "2.8.37", @@ -3003,6 +3185,107 @@ ], "time": "2017-02-14T16:28:37+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "time": "2019-04-30T12:38:16+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -3248,6 +3531,67 @@ ], "time": "2021-02-17T21:57:51+00:00" }, + { + "name": "spomky-labs/base64url", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/base64url.git", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/base64url/zipball/7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.11|^0.12", + "phpstan/phpstan-beberlei-assert": "^0.11|^0.12", + "phpstan/phpstan-deprecation-rules": "^0.11|^0.12", + "phpstan/phpstan-phpunit": "^0.11|^0.12", + "phpstan/phpstan-strict-rules": "^0.11|^0.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Base64Url\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky-Labs/base64url/contributors" + } + ], + "description": "Base 64 URL Safe Encoding/Decoding PHP Library", + "homepage": "https://github.com/Spomky-Labs/base64url", + "keywords": [ + "base64", + "rfc4648", + "safe", + "url" + ], + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-11-03T09:10:25+00:00" + }, { "name": "symfony/polyfill-intl-idn", "version": "v1.19.0", @@ -3765,6 +4109,361 @@ "description": "A multi-language port of Browserscope's user agent parser.", "time": "2020-02-21T09:54:14+00:00" }, + { + "name": "web-token/jwt-core", + "version": "v2.2.10", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-core.git", + "reference": "53beb6f6c1eec4fa93c1c3e5d9e5701e71fa1678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-core/zipball/53beb6f6c1eec4fa93c1c3e5d9e5701e71fa1678", + "reference": "53beb6f6c1eec4fa93c1c3e5d9e5701e71fa1678", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.17|^0.9", + "ext-json": "*", + "ext-mbstring": "*", + "fgrosse/phpasn1": "^2.0", + "php": ">=7.2", + "spomky-labs/base64url": "^1.0|^2.0" + }, + "conflict": { + "spomky-labs/jose": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jose\\Component\\Core\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "Core component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2021-03-17T14:55:52+00:00" + }, + { + "name": "web-token/jwt-key-mgmt", + "version": "v2.2.10", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-key-mgmt.git", + "reference": "0b116379515700d237b4e5de86879078ccb09d8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-key-mgmt/zipball/0b116379515700d237b4e5de86879078ccb09d8a", + "reference": "0b116379515700d237b4e5de86879078ccb09d8a", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "web-token/jwt-core": "^2.0" + }, + "suggest": { + "ext-sodium": "Sodium is required for OKP key creation, EdDSA signature algorithm and ECDH-ES key encryption with OKP keys", + "php-http/httplug": "To enable JKU/X5U support.", + "php-http/message-factory": "To enable JKU/X5U support.", + "web-token/jwt-util-ecc": "To use EC key analyzers." + }, + "type": "library", + "autoload": { + "psr-4": { + "Jose\\Component\\KeyManagement\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-key-mgmt/contributors" + } + ], + "description": "Key Management component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2021-03-17T14:55:52+00:00" + }, + { + "name": "web-token/jwt-signature", + "version": "v2.2.10", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature.git", + "reference": "015b59aaf3b6e8fb9f5bd1338845b7464c7d8103" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature/zipball/015b59aaf3b6e8fb9f5bd1338845b7464c7d8103", + "reference": "015b59aaf3b6e8fb9f5bd1338845b7464c7d8103", + "shasum": "" + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "suggest": { + "web-token/jwt-signature-algorithm-ecdsa": "ECDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-eddsa": "EdDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-experimental": "Experimental Signature Algorithms", + "web-token/jwt-signature-algorithm-hmac": "HMAC Based Signature Algorithms", + "web-token/jwt-signature-algorithm-none": "None Signature Algorithm", + "web-token/jwt-signature-algorithm-rsa": "RSA Based Signature Algorithms" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-signature/contributors" + } + ], + "description": "Signature component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2021-03-01T19:55:28+00:00" + }, + { + "name": "web-token/jwt-signature-algorithm-ecdsa", + "version": "v2.2.10", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-ecdsa.git", + "reference": "44cbbb4374c51f1cf48b82ae761efbf24e1a8591" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-ecdsa/zipball/44cbbb4374c51f1cf48b82ae761efbf24e1a8591", + "reference": "44cbbb4374c51f1cf48b82ae761efbf24e1a8591", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "web-token/jwt-signature": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "ECDSA Based Signature Algorithms the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2021-01-21T19:18:03+00:00" + }, + { + "name": "web-token/jwt-util-ecc", + "version": "v2.2.10", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-util-ecc.git", + "reference": "915f3fde86f5236c205620d61177b9ef43863deb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-util-ecc/zipball/915f3fde86f5236c205620d61177b9ef43863deb", + "reference": "915f3fde86f5236c205620d61177b9ef43863deb", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.17|^0.9" + }, + "suggest": { + "ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance", + "ext-gmp": "GMP or BCMath is highly recommended to improve the library performance" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jose\\Component\\Core\\Util\\Ecc\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "ECC Tools for the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2021-03-24T13:35:17+00:00" + }, { "name": "xemlock/htmlpurifier-html5", "version": "v0.1.11", @@ -4001,7 +4700,7 @@ }, "platform-dev": [], "platform-overrides": { - "php": "7.0" + "php": "7.2" }, "plugin-api-version": "1.1.0" } diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index aaaa7db7a..dd81ee2d6 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -27,6 +27,7 @@ namespace Friendica\Model; use Friendica\Core\Logger; +use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Util\Crypto; @@ -131,6 +132,7 @@ class Subscription $subscriptions = DBA::select('subscription', [], ['uid' => $notification['uid'], $type => true]); while ($subscription = DBA::fetch($subscriptions)) { Logger::info('Push notification', ['id' => $subscription['id'], 'uid' => $subscription['uid'], 'type' => $type]); + Worker::add(PRIORITY_HIGH, 'PushSubscription', $subscription['id']); } DBA::close($subscriptions); } diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php new file mode 100644 index 000000000..7095c6794 --- /dev/null +++ b/src/Worker/PushSubscription.php @@ -0,0 +1,59 @@ +. + * + */ + +namespace Friendica\Worker; + +use Friendica\Core\Logger; +use Friendica\Database\DBA; +use Minishlink\WebPush\WebPush; +use Minishlink\WebPush\Subscription; + +Class PushSubscription +{ + public static function execute(int $sid) + { + $subscription = DBA::selectFirst('subscription', [], ['id' => $sid]); + + $notification = [ + 'subscription' => Subscription::create([ + 'endpoint' => $subscription['endpoint'], + 'publicKey' => $subscription['pubkey'], + 'authToken' => $subscription['secret'], + ]), + 'payload' => null, + ]; + + $webPush = new WebPush(); + + $report = $webPush->sendOneNotification( + $notification['subscription'], + $notification['payload'] + ); + + $endpoint = $report->getRequest()->getUri()->__toString(); + + if ($report->isSuccess()) { + Logger::info('Message sent successfully for subscription', ['endpoint' => $endpoint]); + } else { + Logger::info('Message failed to sent for subscription', ['endpoint' => $endpoint, 'reason' => $report->getReason()]); + } + } +} From 5cf0da4140ec4d7a3ba502b945b01e7c36a98025 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 21:01:58 +0000 Subject: [PATCH 02/12] Coding standards --- src/Worker/PushSubscription.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index 7095c6794..ab323105b 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -34,7 +34,7 @@ Class PushSubscription $notification = [ 'subscription' => Subscription::create([ - 'endpoint' => $subscription['endpoint'], + 'endpoint' => $subscription['endpoint'], 'publicKey' => $subscription['pubkey'], 'authToken' => $subscription['secret'], ]), @@ -47,7 +47,7 @@ Class PushSubscription $notification['subscription'], $notification['payload'] ); - + $endpoint = $report->getRequest()->getUri()->__toString(); if ($report->isSuccess()) { From d5e9253adbca138fb52631037640dcb0972f6382 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 21:03:43 +0000 Subject: [PATCH 03/12] Standards again --- src/Worker/PushSubscription.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index ab323105b..3f209af2c 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -26,7 +26,7 @@ use Friendica\Database\DBA; use Minishlink\WebPush\WebPush; use Minishlink\WebPush\Subscription; -Class PushSubscription +class PushSubscription { public static function execute(int $sid) { From 69f11c4a8461bd148b9d5e6223046e57994bb4cc Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 21:24:23 +0000 Subject: [PATCH 04/12] Adding the VAPID keys --- src/Factory/Api/Mastodon/Subscription.php | 2 +- src/Model/Subscription.php | 28 ++++++++++++++++++++--- src/Worker/PushSubscription.php | 12 +++++++++- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/Factory/Api/Mastodon/Subscription.php b/src/Factory/Api/Mastodon/Subscription.php index 08dbcf0d1..76034708a 100644 --- a/src/Factory/Api/Mastodon/Subscription.php +++ b/src/Factory/Api/Mastodon/Subscription.php @@ -36,6 +36,6 @@ class Subscription extends BaseFactory public function createForApplicationIdAndUserId(int $applicationid, int $uid): \Friendica\Object\Api\Mastodon\Subscription { $subscription = DBA::selectFirst('subscription', [], ['application-id' => $applicationid, 'uid' => $uid]); - return new \Friendica\Object\Api\Mastodon\Subscription($subscription, ModelSubscription::getVapidKey()); + return new \Friendica\Object\Api\Mastodon\Subscription($subscription, ModelSubscription::getPublicVapidKey()); } } diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index dd81ee2d6..43bf2ae55 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -100,20 +100,42 @@ class Subscription } /** - * Fetch a VAPID key + * Fetch a VAPID keypair * - * @return string + * @return array */ - public static function getVapidKey(): string + private static function getKeyPair(): array { $keypair = DI::config()->get('system', 'ec_keypair'); if (empty($keypair)) { $keypair = Crypto::newECKeypair(); DI::config()->set('system', 'ec_keypair', $keypair); } + return $keypair; + } + + /** + * Fetch the public VAPID key + * + * @return string + */ + public static function getPublicVapidKey(): string + { + $keypair = self::getKeyPair(); return $keypair['vapid-public']; } + /** + * Fetch the public VAPID key + * + * @return string + */ + public static function getPrivateVapidKey(): string + { + $keypair = self::getKeyPair(); + return $keypair['vapid-private']; + } + /** * Prepare push notification * diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index 3f209af2c..aef82103b 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -23,6 +23,8 @@ namespace Friendica\Worker; use Friendica\Core\Logger; use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Subscription as ModelSubscription; use Minishlink\WebPush\WebPush; use Minishlink\WebPush\Subscription; @@ -41,7 +43,15 @@ class PushSubscription 'payload' => null, ]; - $webPush = new WebPush(); + $auth = [ + 'VAPID' => [ + 'subject' => DI::baseUrl()->getHostname(), + 'publicKey' => ModelSubscription::getPublicVapidKey(), + 'privateKey' => ModelSubscription::getPrivateVapidKey(), + ], + ]; + + $webPush = new WebPush($auth); $report = $webPush->sendOneNotification( $notification['subscription'], From 2c1b33af87256054cc561aba1f95b3347e483f2d Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 21:30:27 +0000 Subject: [PATCH 05/12] Standards --- src/Worker/PushSubscription.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index aef82103b..de385afa1 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -45,12 +45,12 @@ class PushSubscription $auth = [ 'VAPID' => [ - 'subject' => DI::baseUrl()->getHostname(), - 'publicKey' => ModelSubscription::getPublicVapidKey(), + 'subject' => DI::baseUrl()->getHostname(), + 'publicKey' => ModelSubscription::getPublicVapidKey(), 'privateKey' => ModelSubscription::getPrivateVapidKey(), ], ]; - + $webPush = new WebPush($auth); $report = $webPush->sendOneNotification( From e3f8f371a7ab5e89139822a16f563b977196fefe Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 21:54:24 +0000 Subject: [PATCH 06/12] Use the existing VAPID key generation --- src/Model/Subscription.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index 43bf2ae55..e83bb919c 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -31,6 +31,7 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Util\Crypto; +use Minishlink\WebPush\VAPID; class Subscription { @@ -107,8 +108,8 @@ class Subscription private static function getKeyPair(): array { $keypair = DI::config()->get('system', 'ec_keypair'); - if (empty($keypair)) { - $keypair = Crypto::newECKeypair(); + if (empty($keypair['publicKey']) || empty($keypair['privateKey'])) { + $keypair = VAPID::createVapidKeys(); DI::config()->set('system', 'ec_keypair', $keypair); } return $keypair; @@ -122,7 +123,7 @@ class Subscription public static function getPublicVapidKey(): string { $keypair = self::getKeyPair(); - return $keypair['vapid-public']; + return $keypair['publicKey']; } /** @@ -133,7 +134,7 @@ class Subscription public static function getPrivateVapidKey(): string { $keypair = self::getKeyPair(); - return $keypair['vapid-private']; + return $keypair['privateKey']; } /** From 69c816eb17cab27daa50779a1818f70e88caac4a Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 22:09:32 +0000 Subject: [PATCH 07/12] Trim whitespace --- src/Model/Subscription.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index e83bb919c..71d26248f 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -109,7 +109,7 @@ class Subscription { $keypair = DI::config()->get('system', 'ec_keypair'); if (empty($keypair['publicKey']) || empty($keypair['privateKey'])) { - $keypair = VAPID::createVapidKeys(); + $keypair = VAPID::createVapidKeys(); DI::config()->set('system', 'ec_keypair', $keypair); } return $keypair; From b37a9a5624ff2d5179b51fa204fc4731e29233fc Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 22:19:57 +0000 Subject: [PATCH 08/12] Require PHP >= 7.2 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1e3fcfcff..179770fbe 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "issues": "https://github.com/friendica/friendica/issues" }, "require": { - "php": ">=7.0", + "php": ">=7.2", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", From 41545e8808afc9565e68228ff2237b49afacdc92 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 15 Aug 2021 23:21:18 +0000 Subject: [PATCH 09/12] Fixing composer hash --- composer.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.lock b/composer.lock index fdfe37bbb..fde685617 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": "9560ab7f3a3b33c673672c11813ae52e", + "content-hash": "67d9153800314dadee1f838ad722c148", "packages": [ { "name": "asika/simple-console", @@ -4684,7 +4684,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.0", + "php": ">=7.2", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", From c85ce2f6b078dfd5d25195d56d97542a41822a15 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 16 Aug 2021 06:11:26 +0000 Subject: [PATCH 10/12] Add a payload --- src/Model/Subscription.php | 2 +- src/Worker/PushSubscription.php | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index 71d26248f..5fb88911a 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -155,7 +155,7 @@ class Subscription $subscriptions = DBA::select('subscription', [], ['uid' => $notification['uid'], $type => true]); while ($subscription = DBA::fetch($subscriptions)) { Logger::info('Push notification', ['id' => $subscription['id'], 'uid' => $subscription['uid'], 'type' => $type]); - Worker::add(PRIORITY_HIGH, 'PushSubscription', $subscription['id']); + Worker::add(PRIORITY_HIGH, 'PushSubscription', $subscription['id'], $nid); } DBA::close($subscriptions); } diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index de385afa1..f3c3a4046 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -24,23 +24,43 @@ namespace Friendica\Worker; use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Model\Subscription as ModelSubscription; +use Friendica\Util\DateTimeFormat; use Minishlink\WebPush\WebPush; use Minishlink\WebPush\Subscription; class PushSubscription { - public static function execute(int $sid) + public static function execute(int $sid, int $nid) { $subscription = DBA::selectFirst('subscription', [], ['id' => $sid]); + $notification = DBA::selectFirst('notification', [], ['id' => $nid]); - $notification = [ + if (!empty($notification['uri-id'])) { + $notify = DBA::selectFirst('notify', ['msg'], ['uri-id' => $notification['target-uri-id']]); + } + + if (!empty($notification['actor-id'])) { + $actor = Contact::getById($notification['actor-id']); + } + + $push = [ 'subscription' => Subscription::create([ 'endpoint' => $subscription['endpoint'], 'publicKey' => $subscription['pubkey'], 'authToken' => $subscription['secret'], ]), - 'payload' => null, + // @todo Check if we are supposed to transmit a payload at all + 'payload' => json_encode([ + 'title' => 'Friendica', + 'body' => $notify['msg'] ?? '', + 'icon' => $actor['thumb'] ?? '', + 'image' => '', + 'badge' => DI::baseUrl()->get() . '/images/friendica-192.png', + 'tag' => $notification['parent-uri-id'] ?? '', + 'timestamp' => DateTimeFormat::utc($notification['created'], DateTimeFormat::JSON), + ]), ]; $auth = [ @@ -54,8 +74,8 @@ class PushSubscription $webPush = new WebPush($auth); $report = $webPush->sendOneNotification( - $notification['subscription'], - $notification['payload'] + $push['subscription'], + $push['payload'] ); $endpoint = $report->getRequest()->getUri()->__toString(); From 7158b35f58a3269e57350c2cde394921de2109cc Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 16 Aug 2021 15:23:34 +0000 Subject: [PATCH 11/12] Added logging, removed superfluous comments --- src/Model/Subscription.php | 6 ------ src/Worker/PushSubscription.php | 17 ++++++++++++++--- static/defaults.config.php | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index 5fb88911a..e3bbadcbf 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -19,18 +19,12 @@ * */ - /** - * @see https://github.com/web-push-libs/web-push-php - * Possibly we should simply use this. - */ - namespace Friendica\Model; use Friendica\Core\Logger; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Util\Crypto; use Minishlink\WebPush\VAPID; class Subscription diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index f3c3a4046..f740410e0 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -34,8 +34,19 @@ class PushSubscription { public static function execute(int $sid, int $nid) { + Logger::info('Start', ['subscription' => $sid, 'notification' => $nid]); + $subscription = DBA::selectFirst('subscription', [], ['id' => $sid]); + if (empty($subscription)) { + Logger::info('Subscription not found', ['subscription' => $sid]); + return; + } + $notification = DBA::selectFirst('notification', [], ['id' => $nid]); + if (empty($notification)) { + Logger::info('Notification not found', ['notification' => $nid]); + return; + } if (!empty($notification['uri-id'])) { $notify = DBA::selectFirst('notify', ['msg'], ['uri-id' => $notification['target-uri-id']]); @@ -71,7 +82,7 @@ class PushSubscription ], ]; - $webPush = new WebPush($auth); + $webPush = new WebPush($auth, [], DI::config()->get('system', 'xrd_timeout')); $report = $webPush->sendOneNotification( $push['subscription'], @@ -81,9 +92,9 @@ class PushSubscription $endpoint = $report->getRequest()->getUri()->__toString(); if ($report->isSuccess()) { - Logger::info('Message sent successfully for subscription', ['endpoint' => $endpoint]); + Logger::info('Message sent successfully for subscription', ['subscription' => $sid, 'notification' => $nid, 'endpoint' => $endpoint]); } else { - Logger::info('Message failed to sent for subscription', ['endpoint' => $endpoint, 'reason' => $report->getReason()]); + Logger::info('Message failed to sent for subscription', ['subscription' => $sid, 'notification' => $nid, 'endpoint' => $endpoint, 'reason' => $report->getReason()]); } } } diff --git a/static/defaults.config.php b/static/defaults.config.php index 20f351ab0..b405d5f5e 100644 --- a/static/defaults.config.php +++ b/static/defaults.config.php @@ -568,7 +568,7 @@ return [ 'worker_defer_limit' => 15, // xrd_timeout (Integer) - // Timeout in seconds for fetching the XRD links. + // Timeout in seconds for fetching the XRD links and other requests with an expected shorter timeout 'xrd_timeout' => 20, ], 'experimental' => [ From e8b7d125f2cb631020b338828476bf636e4d64d9 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 16 Aug 2021 17:21:58 +0000 Subject: [PATCH 12/12] Updated changelog --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 2c7e4fc43..02935eac4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,8 @@ Version 2021.09 (unreleased) Simplified the proxy mechanism. The proxy cache directory (/proxy) can now be removed [annando] DFRN is now always handled with the Diaspora transport layer. The legacy DFRN transport layer is removed [annando] Legacy OAuth server is removed [annando] + Scheduled posts are now possible [annando] + The minimal PHP version in the composer is increased to version 7.2 [annando] Version 2021.07 (2021-07-04) Friendica Core