From 2094e3965bf26452ec20cad4dd6827b38379437a Mon Sep 17 00:00:00 2001 From: HeySora Date: Wed, 16 Jul 2025 12:52:38 +0200 Subject: [PATCH 01/17] Fix docker command syntax --- cli/release.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/release.php b/cli/release.php index aac915b02..847dd14e2 100755 --- a/cli/release.php +++ b/cli/release.php @@ -244,7 +244,7 @@ else if ($options['docker']) { $zip_filename = "snappymail-{$package->version}.zip"; copy($zip_destination, "./.docker/release/{$zip_filename}"); if ($docker) { - passthru("{$docker} build --pull " . ROOT_DIR . "/.docker/release/ --build-arg FILES_ZIP={$zip_filename} -t snappymail:{$package->version}"); + passthru("{$docker} build --pull -f " . ROOT_DIR . '/.docker/release/Dockerfile ' . ROOT_DIR . " --build-arg FILES_ZIP={$zip_filename} -t snappymail:{$package->version}"); } else { echo "Docker not installed!\n"; } From c95f5fcf9878619da4fcc1afea6a83c9df5747cb Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Wed, 8 Oct 2025 02:50:09 +0200 Subject: [PATCH 02/17] Nextcloud 32+ compatibility adds NC32+ compatibility workaround src & credits go to: https://github.com/the-djmaze/snappymail/issues/1994#issuecomment-3366086651 --- .../snappymail/lib/Controller/PageController.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/integrations/nextcloud/snappymail/lib/Controller/PageController.php b/integrations/nextcloud/snappymail/lib/Controller/PageController.php index 7744cb4fc..469d6fb26 100644 --- a/integrations/nextcloud/snappymail/lib/Controller/PageController.php +++ b/integrations/nextcloud/snappymail/lib/Controller/PageController.php @@ -8,6 +8,12 @@ use OCA\SnappyMail\ContentSecurityPolicy; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; +// SnappyMail Nextcoud 32 compatibility workaound - START +// src: https://github.com/the-djmaze/snappymail/issues/1994#issuecomment-3366086651 + +use OCP\INavigationManager; // <<<<<<<<<<<< add this +use OCP\Server; // <<<<<<<<<<<< add this + class PageController extends Controller { /** @@ -17,7 +23,7 @@ class PageController extends Controller public function index() { $config = \OC::$server->getConfig(); - + $nav = Server::get(INavigationManager::class); // <<<<<<<<<<<< add this $bAdmin = false; if (!empty($_SERVER['QUERY_STRING'])) { SnappyMailHelper::loadApp(); @@ -28,7 +34,8 @@ class PageController extends Controller } if (!$bAdmin && $config->getAppValue('snappymail', 'snappymail-no-embed')) { - \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); + // \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); // <<<<<<<<<<<< Comment this + $nav->setActiveEntry('snappymail'); // <<<<<<<<<<<< add this \OCP\Util::addScript('snappymail', 'snappymail'); \OCP\Util::addStyle('snappymail', 'style'); SnappyMailHelper::startApp(); @@ -43,7 +50,10 @@ class PageController extends Controller return $response; } - \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); + // \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); // <<<<<<<<<<<< Comment this + $nav->setActiveEntry('snappymail'); // <<<<<<<<<<<< add this + +// SnappyMail Nextcoud 32 compatibility workaound - END \OCP\Util::addStyle('snappymail', 'embed'); From 2548ed59da7ccc5aa9343e74b045e66974ad40f1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:11:52 +0000 Subject: [PATCH 03/17] Fix PHP 8.4 implicit nullable parameter deprecations in Predis library PHP 8.4 deprecates implicitly nullable parameter types. Parameters with a default value of `null` must now be explicitly marked as nullable (e.g., `?Type $var = null`). This change updates the vendored `Predis` library in `plugins/cache-redis/` to comply with this requirement. It scans for function signatures like `Type $var = null` and updates them to `?Type $var = null`. This ensures compatibility with PHP 8.4 and PHP 8.5 while maintaining backward compatibility. Affected files include `Predis` interfaces, connection classes, and command definitions. Also updated PHPDoc `@method` annotations to reflect nullable types. --- .../Predis/ClientContextInterface.php | 30 +++++++------- .../cache-redis/Predis/ClientInterface.php | 40 +++++++++---------- .../Predis/Cluster/PredisStrategy.php | 2 +- .../Predis/Cluster/RedisStrategy.php | 2 +- .../Redis/Container/FunctionContainer.php | 4 +- .../Strategy/SubcommandStrategyResolver.php | 2 +- .../Predis/CommunicationException.php | 2 +- .../Predis/Configuration/Options.php | 2 +- .../Connection/Cluster/PredisCluster.php | 2 +- .../Connection/Cluster/RedisCluster.php | 4 +- .../Connection/CompositeStreamConnection.php | 2 +- .../Predis/Connection/RelayMethods.php | 4 +- .../Replication/MasterSlaveReplication.php | 2 +- .../Replication/SentinelReplication.php | 2 +- .../Text/CompositeProtocolProcessor.php | 4 +- 15 files changed, 52 insertions(+), 52 deletions(-) diff --git a/plugins/cache-redis/Predis/ClientContextInterface.php b/plugins/cache-redis/Predis/ClientContextInterface.php index e443ee5d1..d6cb74512 100644 --- a/plugins/cache-redis/Predis/ClientContextInterface.php +++ b/plugins/cache-redis/Predis/ClientContextInterface.php @@ -65,8 +65,8 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this randomkey() * @method $this rename($key, $target) * @method $this renamenx($key, $target) - * @method $this scan($cursor, array $options = null) - * @method $this sort($key, array $options = null) + * @method $this scan($cursor, ?array $options = null) + * @method $this sort($key, ?array $options = null) * @method $this sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false) * @method $this ttl($key) * @method $this type($key) @@ -163,7 +163,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this hmget($key, array $fields) * @method $this hmset($key, array $dictionary) * @method $this hrandfield(string $key, int $count = 1, bool $withValues = false) - * @method $this hscan($key, $cursor, array $options = null) + * @method $this hscan($key, $cursor, ?array $options = null) * @method $this hset($key, $field, $value) * @method $this hsetnx($key, $field, $value) * @method $this hvals($key) @@ -225,7 +225,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this spop($key, $count = null) * @method $this srandmember($key, $count = null) * @method $this srem($key, $member) - * @method $this sscan($key, $cursor, array $options = null) + * @method $this sscan($key, $cursor, ?array $options = null) * @method $this sunion(array|string $keys) * @method $this sunionstore($destination, array|string $keys) * @method $this tdigestadd(string $key, float ...$value) @@ -255,7 +255,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null) * @method $this tsdel(string $key, int $fromTimestamp, int $toTimestamp) * @method $this tsdeleterule(string $sourceKey, string $destKey) - * @method $this tsget(string $key, GetArguments $arguments = null) + * @method $this tsget(string $key, ?GetArguments $arguments = null) * @method $this tsincrby(string $key, float $value, ?IncrByArguments $arguments = null) * @method $this tsinfo(string $key, ?InfoArguments $arguments = null) * @method $this tsmadd(mixed ...$keyTimestampValue) @@ -277,22 +277,22 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1) * @method $this zmscore(string $key, string ...$member) * @method $this zrandmember(string $key, int $count = 1, bool $withScores = false) - * @method $this zrange($key, $start, $stop, array $options = null) - * @method $this zrangebyscore($key, $min, $max, array $options = null) + * @method $this zrange($key, $start, $stop, ?array $options = null) + * @method $this zrangebyscore($key, $min, $max, ?array $options = null) * @method $this zrangestore(string $destination, string $source, int|string $min, string|int $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0) * @method $this zrank($key, $member) * @method $this zrem($key, $member) * @method $this zremrangebyrank($key, $start, $stop) * @method $this zremrangebyscore($key, $min, $max) - * @method $this zrevrange($key, $start, $stop, array $options = null) - * @method $this zrevrangebyscore($key, $max, $min, array $options = null) + * @method $this zrevrange($key, $start, $stop, ?array $options = null) + * @method $this zrevrangebyscore($key, $max, $min, ?array $options = null) * @method $this zrevrank($key, $member) * @method $this zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false) * @method $this zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum') * @method $this zscore($key, $member) - * @method $this zscan($key, $cursor, array $options = null) - * @method $this zrangebylex($key, $start, $stop, array $options = null) - * @method $this zrevrangebylex($key, $start, $stop, array $options = null) + * @method $this zscan($key, $cursor, ?array $options = null) + * @method $this zrangebylex($key, $start, $stop, ?array $options = null) + * @method $this zrevrangebylex($key, $start, $stop, ?array $options = null) * @method $this zremrangebylex($key, $min, $max) * @method $this zlexcount($key, $min, $max) * @method $this pexpiretime(string $key) @@ -312,7 +312,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) * @method $this evalsha_ro(string $sha1, array $keys, ...$argument) * @method $this script($subcommand, $argument = null) - * @method $this shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) + * @method $this shutdown(?bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) * @method $this auth($password) * @method $this echo($message) * @method $this ping($message = null) @@ -335,8 +335,8 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this geohash($key, array $members) * @method $this geopos($key, array $members) * @method $this geodist($key, $member1, $member2, $unit = null) - * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null) - * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null) + * @method $this georadius($key, $longitude, $latitude, $radius, $unit, ?array $options = null) + * @method $this georadiusbymember($key, $member, $radius, $unit, ?array $options = null) * @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false) * @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false) * diff --git a/plugins/cache-redis/Predis/ClientInterface.php b/plugins/cache-redis/Predis/ClientInterface.php index 2937c5804..e7edb9750 100644 --- a/plugins/cache-redis/Predis/ClientInterface.php +++ b/plugins/cache-redis/Predis/ClientInterface.php @@ -74,8 +74,8 @@ use Predis\Response\Status; * @method string|null randomkey() * @method mixed rename(string $key, string $target) * @method int renamenx(string $key, string $target) - * @method array scan($cursor, array $options = null) - * @method array sort(string $key, array $options = null) + * @method array scan($cursor, ?array $options = null) + * @method array sort(string $key, ?array $options = null) * @method array sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false) * @method int ttl(string $key) * @method mixed type(string $key) @@ -172,7 +172,7 @@ use Predis\Response\Status; * @method array hmget(string $key, array $fields) * @method mixed hmset(string $key, array $dictionary) * @method array hrandfield(string $key, int $count = 1, bool $withValues = false) - * @method array hscan(string $key, $cursor, array $options = null) + * @method array hscan(string $key, $cursor, ?array $options = null) * @method int hset(string $key, string $field, string $value) * @method int hsetnx(string $key, string $field, string $value) * @method array hvals(string $key) @@ -231,10 +231,10 @@ use Predis\Response\Status; * @method string[] smembers(string $key) * @method array smismember(string $key, string ...$members) * @method int smove(string $source, string $destination, string $member) - * @method string|array|null spop(string $key, int $count = null) - * @method string|null srandmember(string $key, int $count = null) + * @method string|array|null spop(string $key, ?int $count = null) + * @method string|null srandmember(string $key, ?int $count = null) * @method int srem(string $key, array|string $member) - * @method array sscan(string $key, int $cursor, array $options = null) + * @method array sscan(string $key, int $cursor, ?array $options = null) * @method string[] sunion(array|string $keys) * @method int sunionstore(string $destination, array|string $keys) * @method int touch(string[]|string $keyOrKeys, string ...$keys = null) @@ -265,7 +265,7 @@ use Predis\Response\Status; * @method int tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null) * @method int tsdel(string $key, int $fromTimestamp, int $toTimestamp) * @method Status tsdeleterule(string $sourceKey, string $destKey) - * @method array tsget(string $key, GetArguments $arguments = null) + * @method array tsget(string $key, ?GetArguments $arguments = null) * @method int tsincrby(string $key, float $value, ?IncrByArguments $arguments = null) * @method array tsinfo(string $key, ?InfoArguments $arguments = null) * @method array tsmadd(mixed ...$keyTimestampValue) @@ -275,12 +275,12 @@ use Predis\Response\Status; * @method array tsqueryindex(string ...$filterExpression) * @method array tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null) * @method array tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null) - * @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null) + * @method string xadd(string $key, array $dictionary, string $id = '*', ?array $options = null) * @method int xdel(string $key, string ...$id) * @method int xlen(string $key) * @method array xrevrange(string $key, string $end, string $start, ?int $count = null) * @method array xrange(string $key, string $start, string $end, ?int $count = null) - * @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null) + * @method string xtrim(string $key, array|string $strategy, string $threshold, ?array $options = null) * @method int zadd(string $key, array $membersAndScoresDictionary) * @method int zcard(string $key) * @method string zcount(string $key, int|string $min, int|string $max) @@ -295,22 +295,22 @@ use Predis\Response\Status; * @method array zpopmin(string $key, int $count = 1) * @method array zpopmax(string $key, int $count = 1) * @method mixed zrandmember(string $key, int $count = 1, bool $withScores = false) - * @method array zrange(string $key, int|string $start, int|string $stop, array $options = null) - * @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null) + * @method array zrange(string $key, int|string $start, int|string $stop, ?array $options = null) + * @method array zrangebyscore(string $key, int|string $min, int|string $max, ?array $options = null) * @method int zrangestore(string $destination, string $source, int|string $min, int|string $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0) * @method int|null zrank(string $key, string $member) * @method int zrem(string $key, string ...$member) * @method int zremrangebyrank(string $key, int|string $start, int|string $stop) * @method int zremrangebyscore(string $key, int|string $min, int|string $max) - * @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null) - * @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null) + * @method array zrevrange(string $key, int|string $start, int|string $stop, ?array $options = null) + * @method array zrevrangebyscore(string $key, int|string $max, int|string $min, ?array $options = null) * @method int|null zrevrank(string $key, string $member) * @method array zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false) * @method int zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum') * @method string|null zscore(string $key, string $member) - * @method array zscan(string $key, int $cursor, array $options = null) - * @method array zrangebylex(string $key, string $start, string $stop, array $options = null) - * @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null) + * @method array zscan(string $key, int $cursor, ?array $options = null) + * @method array zrangebylex(string $key, string $start, string $stop, ?array $options = null) + * @method array zrevrangebylex(string $key, string $start, string $stop, ?array $options = null) * @method int zremrangebylex(string $key, string $min, string $max) * @method int zlexcount(string $key, string $min, string $max) * @method int pexpiretime(string $key) @@ -330,10 +330,10 @@ use Predis\Response\Status; * @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null) * @method mixed evalsha_ro(string $sha1, array $keys, ...$argument) * @method mixed script($subcommand, $argument = null) - * @method Status shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) + * @method Status shutdown(?bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) * @method mixed auth(string $password) * @method string echo(string $message) - * @method mixed ping(string $message = null) + * @method mixed ping(?string $message = null) * @method mixed select(int $database) * @method mixed bgrewriteaof() * @method mixed bgsave() @@ -353,8 +353,8 @@ use Predis\Response\Status; * @method array geohash(string $key, array $members) * @method array geopos(string $key, array $members) * @method string|null geodist(string $key, $member1, $member2, $unit = null) - * @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null) - * @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null) + * @method array georadius(string $key, $longitude, $latitude, $radius, $unit, ?array $options = null) + * @method array georadiusbymember(string $key, $member, $radius, $unit, ?array $options = null) * @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false) * @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false) * diff --git a/plugins/cache-redis/Predis/Cluster/PredisStrategy.php b/plugins/cache-redis/Predis/Cluster/PredisStrategy.php index 574b86f9e..04f31b117 100644 --- a/plugins/cache-redis/Predis/Cluster/PredisStrategy.php +++ b/plugins/cache-redis/Predis/Cluster/PredisStrategy.php @@ -25,7 +25,7 @@ class PredisStrategy extends ClusterStrategy /** * @param DistributorInterface $distributor Optional distributor instance. */ - public function __construct(DistributorInterface $distributor = null) + public function __construct(?DistributorInterface $distributor = null) { parent::__construct(); diff --git a/plugins/cache-redis/Predis/Cluster/RedisStrategy.php b/plugins/cache-redis/Predis/Cluster/RedisStrategy.php index 8ae5c0f5e..dadf782ed 100644 --- a/plugins/cache-redis/Predis/Cluster/RedisStrategy.php +++ b/plugins/cache-redis/Predis/Cluster/RedisStrategy.php @@ -27,7 +27,7 @@ class RedisStrategy extends ClusterStrategy /** * @param HashGeneratorInterface $hashGenerator Hash generator instance. */ - public function __construct(HashGeneratorInterface $hashGenerator = null) + public function __construct(?HashGeneratorInterface $hashGenerator = null) { parent::__construct(); diff --git a/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php b/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php index 8f2a673d1..dfa309eea 100644 --- a/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php +++ b/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php @@ -19,9 +19,9 @@ use Predis\Response\Status; * @method string dump() * @method Status flush(?string $mode = null) * @method Status kill() - * @method array list(string $libraryNamePattern = null, bool $withCode = false) + * @method array list(?string $libraryNamePattern = null, bool $withCode = false) * @method string load(string $functionCode, bool $replace = 'false') - * @method Status restore(string $value, string $policy = null) + * @method Status restore(string $value, ?string $policy = null) * @method array stats() */ class FunctionContainer extends AbstractContainer diff --git a/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php b/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php index cda84268e..b714d5276 100644 --- a/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php +++ b/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php @@ -23,7 +23,7 @@ class SubcommandStrategyResolver implements StrategyResolverInterface */ private $separator; - public function __construct(string $separator = null) + public function __construct(?string $separator = null) { $this->separator = $separator; } diff --git a/plugins/cache-redis/Predis/CommunicationException.php b/plugins/cache-redis/Predis/CommunicationException.php index 0fc7c07a6..9fd69a021 100644 --- a/plugins/cache-redis/Predis/CommunicationException.php +++ b/plugins/cache-redis/Predis/CommunicationException.php @@ -32,7 +32,7 @@ abstract class CommunicationException extends PredisException NodeConnectionInterface $connection, $message = '', $code = 0, - Exception $innerException = null + ?Exception $innerException = null ) { parent::__construct( is_null($message) ? '' : $message, diff --git a/plugins/cache-redis/Predis/Configuration/Options.php b/plugins/cache-redis/Predis/Configuration/Options.php index 3fff04129..3cad8e951 100644 --- a/plugins/cache-redis/Predis/Configuration/Options.php +++ b/plugins/cache-redis/Predis/Configuration/Options.php @@ -43,7 +43,7 @@ class Options implements OptionsInterface /** * @param array $options Named array of client options */ - public function __construct(array $options = null) + public function __construct(?array $options = null) { $this->input = $options ?? []; } diff --git a/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php b/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php index c30fd09d3..a65e665f5 100644 --- a/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php +++ b/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php @@ -52,7 +52,7 @@ class PredisCluster implements ClusterInterface, IteratorAggregate, Countable /** * @param StrategyInterface $strategy Optional cluster strategy. */ - public function __construct(StrategyInterface $strategy = null) + public function __construct(?StrategyInterface $strategy = null) { $this->strategy = $strategy ?: new PredisStrategy(); $this->distributor = $this->strategy->getDistributor(); diff --git a/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php b/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php index 7f3013c17..6e7a90239 100644 --- a/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php +++ b/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php @@ -70,7 +70,7 @@ class RedisCluster implements ClusterInterface, IteratorAggregate, Countable */ public function __construct( FactoryInterface $connections, - StrategyInterface $strategy = null + ?StrategyInterface $strategy = null ) { $this->connections = $connections; $this->strategy = $strategy ?: new RedisClusterStrategy(); @@ -273,7 +273,7 @@ class RedisCluster implements ClusterInterface, IteratorAggregate, Countable * * @param NodeConnectionInterface $connection Optional connection instance. */ - public function askSlotMap(NodeConnectionInterface $connection = null) + public function askSlotMap(?NodeConnectionInterface $connection = null) { if (!$connection && !$connection = $this->getRandomConnection()) { return; diff --git a/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php b/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php index ad69cbc12..431663cae 100644 --- a/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php +++ b/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php @@ -31,7 +31,7 @@ class CompositeStreamConnection extends StreamConnection implements CompositeCon */ public function __construct( ParametersInterface $parameters, - ProtocolProcessorInterface $protocol = null + ?ProtocolProcessorInterface $protocol = null ) { $this->parameters = $this->assertParameters($parameters); $this->protocol = $protocol ?: new TextProtocolProcessor(); diff --git a/plugins/cache-redis/Predis/Connection/RelayMethods.php b/plugins/cache-redis/Predis/Connection/RelayMethods.php index a52c4a035..d59dd41f2 100644 --- a/plugins/cache-redis/Predis/Connection/RelayMethods.php +++ b/plugins/cache-redis/Predis/Connection/RelayMethods.php @@ -32,7 +32,7 @@ trait RelayMethods * @param string $pattern * @return bool */ - public function onInvalidated(?callable $callback, string $pattern = null) + public function onInvalidated(?callable $callback, ?string $pattern = null) { return $this->client->onInvalidated($callback, $pattern); } @@ -129,7 +129,7 @@ trait RelayMethods * @param ?int $db * @return bool */ - public function flushMemory(string $endpointId = null, int $db = null) + public function flushMemory(?string $endpointId = null, ?int $db = null) { return $this->client->flushMemory($endpointId, $db); } diff --git a/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php b/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php index 9d57a5ac3..31897ed3e 100644 --- a/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php +++ b/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php @@ -72,7 +72,7 @@ class MasterSlaveReplication implements ReplicationInterface /** * {@inheritdoc} */ - public function __construct(ReplicationStrategy $strategy = null) + public function __construct(?ReplicationStrategy $strategy = null) { $this->strategy = $strategy ?: new ReplicationStrategy(); } diff --git a/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php b/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php index 14dbe2758..146254e88 100644 --- a/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php +++ b/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php @@ -123,7 +123,7 @@ class SentinelReplication implements ReplicationInterface $service, array $sentinels, ConnectionFactoryInterface $connectionFactory, - ReplicationStrategy $strategy = null + ?ReplicationStrategy $strategy = null ) { $this->sentinels = $sentinels; $this->service = $service; diff --git a/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php b/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php index 3f7df02e5..19494c05d 100644 --- a/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php +++ b/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php @@ -41,8 +41,8 @@ class CompositeProtocolProcessor implements ProtocolProcessorInterface * @param ResponseReaderInterface $reader Response reader. */ public function __construct( - RequestSerializerInterface $serializer = null, - ResponseReaderInterface $reader = null + ?RequestSerializerInterface $serializer = null, + ?ResponseReaderInterface $reader = null ) { $this->setRequestSerializer($serializer ?: new RequestSerializer()); $this->setResponseReader($reader ?: new ResponseReader()); From 11ac1fe1abff0007eaa3130a9d4e387d74397c2a Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Fri, 16 Jan 2026 23:31:35 +0100 Subject: [PATCH 04/17] Update docker.yml modify repo --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b957e2fc8..4115ffce9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -26,7 +26,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - djmaze/snappymail + nextgen-networks/snappymail ghcr.io/${{ github.repository }} # type=ref,event=branch generates tag(s) on branch only. E.g. 'master', 'master-abc0123' # type=ref,event=tag generates tag(s) on tags only. E.g. 'v0.0.0', 'v0.0.0-abc0123', and 'latest' From dbf2a26760778801bd732467460ac78dd9925e5a Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Fri, 16 Jan 2026 23:33:14 +0100 Subject: [PATCH 05/17] Update docker-pr.yml modify repo --- .github/workflows/docker-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml index af069cdf6..0ba375b5c 100644 --- a/.github/workflows/docker-pr.yml +++ b/.github/workflows/docker-pr.yml @@ -21,7 +21,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - djmaze/snappymail + nextgen-networks/snappymail ghcr.io/${{ github.repository }} # type=ref,event=pr generates tag(s) on PRs only. E.g. 'pr-123', 'pr-123-abc0123' tags: | From 36ea69ca00a87816ea6819b9694bf028ec8aa41f Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Sat, 17 Jan 2026 02:32:31 +0100 Subject: [PATCH 06/17] Update docker.yml change permission setting --- .github/workflows/docker.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4115ffce9..a233327e1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,9 +6,7 @@ on: - 'v2.*' # This is needed to push to GitHub Container Registry. See https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry -permissions: - contents: read - packages: write +permissions: write-all jobs: build: From d22f8d521dd242e2d4a96318b8eece1954a8d2d5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:04:29 +0000 Subject: [PATCH 07/17] Nextcloud 32/33 Compatibility: Remove deprecated API usage This commit updates the SnappyMail integration app for Nextcloud to support version 32 and the upcoming version 33. Key changes: - Updated `info.xml` to allow `max-version="33"`. - Refactored `AdminSettings`, `Application`, `FetchController`, `PageController`, `Settings` (command), and `InstallStep` to use Dependency Injection instead of the deprecated `\OC::$server` service container. - Replaced deprecated `OC_User`, `OC_Util` (e.g. `addScript`), and `OC_App` calls with their `OCP` (public API) counterparts or modern equivalents. - Updated `SnappyMailHelper` to use `\OCP\Server::get()` for static context access where DI is not possible. - Moved script and style registration from Controllers (using deprecated `Util::addScript`) to templates using `script()` and `style()` helpers. - Updated `Settings` console command to extend `Symfony\Component\Console\Command\Command` instead of deprecated `OC\Core\Command\Base`. - Addressed PHP 8.4+ compatibility by adding explicit property types and nullable types where necessary in the integration code. Co-authored-by: nextgen-networks <21206978+nextgen-networks@users.noreply.github.com> --- .github/workflows/docker-pr.yml | 2 +- .github/workflows/docker.yml | 6 +- .../nextcloud/snappymail/appinfo/info.xml | 2 +- .../snappymail/lib/AppInfo/Application.php | 34 +- .../snappymail/lib/Command/Settings.php | 4 +- .../snappymail/lib/ContentSecurityPolicy.php | 5 +- .../lib/Controller/FetchController.php | 24 +- .../lib/Controller/PageController.php | 46 +- .../snappymail/lib/Migration/InstallStep.php | 23 +- .../snappymail/lib/Settings/AdminSettings.php | 38 +- .../snappymail/lib/Util/SnappyMailHelper.php | 484 +++++++++--------- .../snappymail/templates/admin-local.php | 1 + .../nextcloud/snappymail/templates/index.php | 6 +- .../snappymail/templates/index_embed.php | 2 + .../Predis/ClientContextInterface.php | 30 +- .../cache-redis/Predis/ClientInterface.php | 40 +- .../Predis/Cluster/PredisStrategy.php | 2 +- .../Predis/Cluster/RedisStrategy.php | 2 +- .../Redis/Container/FunctionContainer.php | 4 +- .../Strategy/SubcommandStrategyResolver.php | 2 +- .../Predis/CommunicationException.php | 2 +- .../Predis/Configuration/Options.php | 2 +- .../Connection/Cluster/PredisCluster.php | 2 +- .../Connection/Cluster/RedisCluster.php | 4 +- .../Connection/CompositeStreamConnection.php | 2 +- .../Predis/Connection/RelayMethods.php | 4 +- .../Replication/MasterSlaveReplication.php | 2 +- .../Replication/SentinelReplication.php | 2 +- .../Text/CompositeProtocolProcessor.php | 4 +- 29 files changed, 422 insertions(+), 359 deletions(-) diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml index 0ba375b5c..af069cdf6 100644 --- a/.github/workflows/docker-pr.yml +++ b/.github/workflows/docker-pr.yml @@ -21,7 +21,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - nextgen-networks/snappymail + djmaze/snappymail ghcr.io/${{ github.repository }} # type=ref,event=pr generates tag(s) on PRs only. E.g. 'pr-123', 'pr-123-abc0123' tags: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a233327e1..b957e2fc8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,7 +6,9 @@ on: - 'v2.*' # This is needed to push to GitHub Container Registry. See https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry -permissions: write-all +permissions: + contents: read + packages: write jobs: build: @@ -24,7 +26,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - nextgen-networks/snappymail + djmaze/snappymail ghcr.io/${{ github.repository }} # type=ref,event=branch generates tag(s) on branch only. E.g. 'master', 'master-abc0123' # type=ref,event=tag generates tag(s) on tags only. E.g. 'v0.0.0', 'v0.0.0-abc0123', and 'latest' diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index d73ef28a0..b1a67e0b1 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -64,7 +64,7 @@ There, click on the link to go to the SnappyMail admin panel. xxtea zip --> - + OCA\SnappyMail\Settings\AdminSettings diff --git a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php index d764520ca..d8e6b294b 100644 --- a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php +++ b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php @@ -5,7 +5,6 @@ namespace OCA\SnappyMail\AppInfo; use OCA\SnappyMail\Util\SnappyMailHelper; use OCA\SnappyMail\Controller\FetchController; use OCA\SnappyMail\Controller\PageController; -use OCA\SnappyMail\Dashboard\UnreadMailWidget; use OCA\SnappyMail\Search\Provider; use OCA\SnappyMail\Listeners\AccessTokenUpdatedListener; @@ -18,6 +17,10 @@ use OCP\IUser; use OCP\User\Events\PostLoginEvent; use OCP\User\Events\BeforeUserLoggedOutEvent; use OCA\OIDCLogin\Events\AccessTokenUpdatedEvent; +use OCP\IConfig; +use OCP\ISession; +use OCP\IUserSession; +use OCP\INavigationManager; class Application extends App implements IBootstrap { @@ -37,7 +40,9 @@ class Application extends App implements IBootstrap 'PageController', function($c) { return new PageController( $c->query('AppName'), - $c->query('Request') + $c->query('Request'), + $c->query(IConfig::class), + $c->query(INavigationManager::class) ); } ); @@ -47,9 +52,10 @@ class Application extends App implements IBootstrap return new FetchController( $c->query('AppName'), $c->query('Request'), - $c->getServer()->getAppManager(), + $c->query('ServerContainer')->getAppManager(), $c->query('ServerContainer')->getConfig(), - $c->query(IL10N::class) + $c->query(IL10N::class), + $c->query(IUserSession::class) ); } ); @@ -72,22 +78,24 @@ class Application extends App implements IBootstrap public function boot(IBootContext $context): void { - if (!\is_dir(\rtrim(\trim(\OC::$server->getSystemConfig()->getValue('datadirectory', '')), '\\/') . '/appdata_snappymail')) { + $config = $context->getServerContainer()->getConfig(); + if (!\is_dir(\rtrim(\trim($config->getSystemValue('datadirectory', '')), '\\/') . '/appdata_snappymail')) { return; } $dispatcher = $context->getAppContainer()->query('OCP\EventDispatcher\IEventDispatcher'); - $dispatcher->addListener(PostLoginEvent::class, function (PostLoginEvent $Event) { + $dispatcher->addListener(PostLoginEvent::class, function (PostLoginEvent $Event) use ($context) { /* - $config = \OC::$server->getConfig(); + $config = $context->getServerContainer()->getConfig(); // Only store the user's password in the current session if they have // enabled auto-login using Nextcloud username or email address. if ($config->getAppValue('snappymail', 'snappymail-autologin', false) || $config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { */ $sUID = $Event->getUser()->getUID(); - \OC::$server->getSession()['snappymail-nc-uid'] = $sUID; - \OC::$server->getSession()['snappymail-passphrase'] = SnappyMailHelper::encodePassword($Event->getPassword(), $sUID); + $session = $context->getServerContainer()->getSession(); + $session['snappymail-nc-uid'] = $sUID; + $session['snappymail-passphrase'] = SnappyMailHelper::encodePassword($Event->getPassword(), $sUID); /* } */ @@ -105,13 +113,13 @@ class Application extends App implements IBootstrap // https://github.com/nextcloud/impersonate/pull/180 $class = 'OCA\Impersonate\Events\BeginImpersonateEvent'; if (\class_exists($class)) { - $dispatcher->addListener($class, function ($Event) { - \OC::$server->getSession()['snappymail-passphrase'] = ''; + $dispatcher->addListener($class, function ($Event) use ($context) { + $context->getServerContainer()->getSession()['snappymail-passphrase'] = ''; SnappyMailHelper::loadApp(); \RainLoop\Api::Actions()->Logout(true); }); - $dispatcher->addListener('OCA\Impersonate\Events\EndImpersonateEvent', function ($Event) { - \OC::$server->getSession()['snappymail-passphrase'] = ''; + $dispatcher->addListener('OCA\Impersonate\Events\EndImpersonateEvent', function ($Event) use ($context) { + $context->getServerContainer()->getSession()['snappymail-passphrase'] = ''; SnappyMailHelper::loadApp(); \RainLoop\Api::Actions()->Logout(true); }); diff --git a/integrations/nextcloud/snappymail/lib/Command/Settings.php b/integrations/nextcloud/snappymail/lib/Command/Settings.php index 1e6cae25d..2843d9f3b 100644 --- a/integrations/nextcloud/snappymail/lib/Command/Settings.php +++ b/integrations/nextcloud/snappymail/lib/Command/Settings.php @@ -3,14 +3,14 @@ namespace OCA\SnappyMail\Command; use OCA\SnappyMail\Util\SnappyMailHelper; -use OC\Core\Command\Base; use OCP\IConfig; use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class Settings extends Base +class Settings extends Command { protected IUserManager $userManager; protected IConfig $config; diff --git a/integrations/nextcloud/snappymail/lib/ContentSecurityPolicy.php b/integrations/nextcloud/snappymail/lib/ContentSecurityPolicy.php index 27a9c44d1..fb8bea501 100644 --- a/integrations/nextcloud/snappymail/lib/ContentSecurityPolicy.php +++ b/integrations/nextcloud/snappymail/lib/ContentSecurityPolicy.php @@ -2,6 +2,9 @@ namespace OCA\SnappyMail; +use OCP\Server; +use OCP\Security\IContentSecurityPolicyNonceManager; + class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy { /** @var bool Whether inline JS snippets are allowed */ @@ -37,7 +40,7 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy public function getSnappyMailNonce() { static $sNonce; if (!$sNonce) { - $cspManager = \OC::$server->getContentSecurityPolicyNonceManager(); + $cspManager = Server::get(IContentSecurityPolicyNonceManager::class); $sNonce = $cspManager->getNonce() ?: \SnappyMail\UUID::generate(); if (\method_exists($cspManager, 'browserSupportsCspV3') && !$cspManager->browserSupportsCspV3()) { $this->addAllowedScriptDomain("'nonce-{$sNonce}'"); diff --git a/integrations/nextcloud/snappymail/lib/Controller/FetchController.php b/integrations/nextcloud/snappymail/lib/Controller/FetchController.php index 6ba8a7761..2091e6122 100644 --- a/integrations/nextcloud/snappymail/lib/Controller/FetchController.php +++ b/integrations/nextcloud/snappymail/lib/Controller/FetchController.php @@ -10,17 +10,21 @@ use OCP\AppFramework\Http\JSONResponse; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; +use OCP\IUserSession; +use Exception; class FetchController extends Controller { private IConfig $config; private IAppManager $appManager; private IL10N $l; + private IUserSession $userSession; - public function __construct(string $appName, IRequest $request, IAppManager $appManager, IConfig $config, IL10N $l) { + public function __construct(string $appName, IRequest $request, IAppManager $appManager, IConfig $config, IL10N $l, IUserSession $userSession) { parent::__construct($appName, $request); $this->config = $config; $this->appManager = $appManager; $this->l = $l; + $this->userSession = $userSession; } public function upgrade(): JSONResponse { @@ -104,15 +108,18 @@ class FetchController extends Controller { try { $sEmail = ''; if (isset($_POST['appname'], $_POST['snappymail-password'], $_POST['snappymail-email']) && 'snappymail' === $_POST['appname']) { - $sUser = \OC::$server->getUserSession()->getUser()->getUID(); + $user = $this->userSession->getUser(); + $sUser = $user ? $user->getUID() : ''; - $sEmail = $_POST['snappymail-email']; - $this->config->setUserValue($sUser, 'snappymail', 'snappymail-email', $sEmail); + if ($sUser) { + $sEmail = $_POST['snappymail-email']; + $this->config->setUserValue($sUser, 'snappymail', 'snappymail-email', $sEmail); - $sPass = $_POST['snappymail-password']; - if ('******' !== $sPass) { - $this->config->setUserValue($sUser, 'snappymail', 'passphrase', - $sPass ? SnappyMailHelper::encodePassword($sPass, \md5($sEmail)) : ''); + $sPass = $_POST['snappymail-password']; + if ('******' !== $sPass) { + $this->config->setUserValue($sUser, 'snappymail', 'passphrase', + $sPass ? SnappyMailHelper::encodePassword($sPass, \md5($sEmail)) : ''); + } } } else { return new JSONResponse([ @@ -144,4 +151,3 @@ class FetchController extends Controller { } } } - diff --git a/integrations/nextcloud/snappymail/lib/Controller/PageController.php b/integrations/nextcloud/snappymail/lib/Controller/PageController.php index 469d6fb26..ee11d74e7 100644 --- a/integrations/nextcloud/snappymail/lib/Controller/PageController.php +++ b/integrations/nextcloud/snappymail/lib/Controller/PageController.php @@ -7,23 +7,27 @@ use OCA\SnappyMail\ContentSecurityPolicy; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; - -// SnappyMail Nextcoud 32 compatibility workaound - START -// src: https://github.com/the-djmaze/snappymail/issues/1994#issuecomment-3366086651 - -use OCP\INavigationManager; // <<<<<<<<<<<< add this -use OCP\Server; // <<<<<<<<<<<< add this +use OCP\INavigationManager; +use OCP\IConfig; +use OCP\IRequest; class PageController extends Controller { + private IConfig $config; + private INavigationManager $navigationManager; + + public function __construct($appName, IRequest $request, IConfig $config, INavigationManager $navigationManager) { + parent::__construct($appName, $request); + $this->config = $config; + $this->navigationManager = $navigationManager; + } + /** * @NoAdminRequired * @NoCSRFRequired */ public function index() { - $config = \OC::$server->getConfig(); - $nav = Server::get(INavigationManager::class); // <<<<<<<<<<<< add this $bAdmin = false; if (!empty($_SERVER['QUERY_STRING'])) { SnappyMailHelper::loadApp(); @@ -33,11 +37,8 @@ class PageController extends Controller } } - if (!$bAdmin && $config->getAppValue('snappymail', 'snappymail-no-embed')) { - // \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); // <<<<<<<<<<<< Comment this - $nav->setActiveEntry('snappymail'); // <<<<<<<<<<<< add this - \OCP\Util::addScript('snappymail', 'snappymail'); - \OCP\Util::addStyle('snappymail', 'style'); + if (!$bAdmin && $this->config->getAppValue('snappymail', 'snappymail-no-embed')) { + $this->navigationManager->setActiveEntry('snappymail'); SnappyMailHelper::startApp(); $response = new TemplateResponse('snappymail', 'index', [ 'snappymail-iframe-url' => SnappyMailHelper::normalizeUrl(SnappyMailHelper::getAppUrl()) @@ -50,12 +51,7 @@ class PageController extends Controller return $response; } - // \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); // <<<<<<<<<<<< Comment this - $nav->setActiveEntry('snappymail'); // <<<<<<<<<<<< add this - -// SnappyMail Nextcoud 32 compatibility workaound - END - - \OCP\Util::addStyle('snappymail', 'embed'); + $this->navigationManager->setActiveEntry('snappymail'); SnappyMailHelper::startApp(); $oConfig = \RainLoop\Api::Config(); @@ -69,6 +65,8 @@ class PageController extends Controller $csp = new ContentSecurityPolicy(); $sNonce = $csp->getSnappyMailNonce(); + $cssLink = \RainLoop\Utils::WebStaticPath('css/'.($bAdmin?'admin':'app').$sAppCssMin.'.css'); + $params = [ 'Admin' => $bAdmin ? 1 : 0, 'LoadingDescriptionEsc' => \htmlspecialchars($oConfig->Get('webmail', 'loading_description', 'SnappyMail'), ENT_QUOTES|ENT_IGNORE, 'UTF-8'), @@ -81,16 +79,10 @@ class PageController extends Controller '/\\s*([:;{},]+)\\s*/s', '$1', $oActions->compileCss($oActions->GetTheme($bAdmin), $bAdmin) - ) + ), + 'CssLink' => $cssLink ]; -// \OCP\Util::addScript('snappymail', '../app/snappymail/v/'.APP_VERSION.'/static/js'.($sAppJsMin ? '/min' : '').'/boot'.$sAppJsMin); - - // Nextcloud html encodes, so addHeader('style') is not possible -// \OCP\Util::addHeader('style', ['id'=>'app-boot-css'], \file_get_contents(APP_VERSION_ROOT_PATH.'static/css/boot'.$sAppCssMin.'.css')); - \OCP\Util::addHeader('link', ['type'=>'text/css','rel'=>'stylesheet','href'=>\RainLoop\Utils::WebStaticPath('css/'.($bAdmin?'admin':'app').$sAppCssMin.'.css')], ''); -// \OCP\Util::addHeader('style', ['id'=>'app-theme-style','data-href'=>$params['BaseAppThemeCssLink']], $params['BaseAppThemeCss']); - $response = new TemplateResponse('snappymail', 'index_embed', $params); $response->setContentSecurityPolicy($csp); diff --git a/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php b/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php index 966ef2352..6f00cedfe 100644 --- a/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php +++ b/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php @@ -6,7 +6,8 @@ use OCA\SnappyMail\AppInfo\Application; use OCP\IConfig; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; -use OCP\ILogger; +use Psr\Log\LoggerInterface; +use OCP\App\IAppManager; /** * Note: this is not run at install, but when: @@ -15,6 +16,16 @@ use OCP\ILogger; */ class InstallStep implements IRepairStep { + private IAppManager $appManager; + private IConfig $config; + private LoggerInterface $logger; + + public function __construct(IAppManager $appManager, IConfig $config, LoggerInterface $logger) { + $this->appManager = $appManager; + $this->config = $config; + $this->logger = $logger; + } + public function getName() { return 'Setup SnappyMail'; } @@ -53,7 +64,7 @@ class InstallStep implements IRepairStep if (!$oConfig->Get('webmail', 'app_path')) { $output->info('Set config [webmail]app_path'); - $oConfig->Set('webmail', 'app_path', \OC::$server->getAppManager()->getAppWebPath('snappymail') . '/app/'); + $oConfig->Set('webmail', 'app_path', $this->appManager->getAppWebPath('snappymail') . '/app/'); $oConfig->Set('webmail', 'allow_languages_on_settings', false); $oConfig->Set('login', 'allow_languages_on_login', false); $bSave = true; @@ -116,9 +127,7 @@ class InstallStep implements IRepairStep // ex: php occ config:app:set snappymail custom_config_file --value="/path/to/config.php" // https://github.com/the-djmaze/snappymail/pull/1197 try { - /** @var IConfig $ncConfig */ - $ncConfig = \OC::$server->get(IConfig::class); - $customConfigFile = $ncConfig->getAppValue(Application::APP_ID, 'custom_config_file'); + $customConfigFile = $this->config->getAppValue(Application::APP_ID, 'custom_config_file'); if ($customConfigFile) { $output->info("Load custom config: {$customConfigFile}"); if (!\str_contains($customConfigFile, ':') && \is_readable($customConfigFile)) { @@ -129,9 +138,7 @@ class InstallStep implements IRepairStep } } catch (\Throwable $e) { $output->warning("custom config error: " . $e->getMessage()); - /** @var \Psr\Log\LoggerInterface $logger */ - $logger = \OC::$server->get(\Psr\Log\LoggerInterface::class); - $logger->error("custom config error: " . $e->getMessage()); + $this->logger->error("custom config error: " . $e->getMessage()); } } } diff --git a/integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php b/integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php index 7a69cb96f..4bf372eae 100644 --- a/integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php +++ b/integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php @@ -5,14 +5,32 @@ use OCA\SnappyMail\Util\SnappyMailHelper; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\Settings\ISettings; +use OCP\IUserSession; +use OCP\IGroupManager; +use OCP\IURLGenerator; +use OCP\App\IAppManager; +use OCP\Util; class AdminSettings implements ISettings { - private $config; + private IConfig $config; + private IUserSession $userSession; + private IGroupManager $groupManager; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; - public function __construct(IConfig $config) - { + public function __construct( + IConfig $config, + IUserSession $userSession, + IGroupManager $groupManager, + IURLGenerator $urlGenerator, + IAppManager $appManager + ) { $this->config = $config; + $this->userSession = $userSession; + $this->groupManager = $groupManager; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; } public function getForm() @@ -30,12 +48,15 @@ class AdminSettings implements ISettings $v = $this->config->getAppValue('snappymail', $k); $parameters[$k] = $v; } - $uid = \OC::$server->getUserSession()->getUser()->getUID(); - if (\OC_User::isAdminUser($uid)) { + + $user = $this->userSession->getUser(); + $uid = $user ? $user->getUID() : null; + + if ($uid && $this->groupManager->isAdmin($uid)) { // $parameters['snappymail-admin-panel-link'] = SnappyMailHelper::getAppUrl().'?admin'; SnappyMailHelper::loadApp(); $parameters['snappymail-admin-panel-link'] = - \OC::$server->getURLGenerator()->linkToRoute('snappymail.page.index') + $this->urlGenerator->linkToRoute('snappymail.page.index') . '?' . \RainLoop\Api::Config()->Get('security', 'admin_panel_key', 'admin'); } @@ -49,7 +70,7 @@ class AdminSettings implements ISettings $parameters['snappymail-admin-password'] = $sPassword; $parameters['can-import-rainloop'] = $sPassword && \is_dir( - \rtrim(\trim(\OC::$server->getSystemConfig()->getValue('datadirectory', '')), '\\/') + \rtrim(\trim($this->config->getSystemValue('datadirectory', '')), '\\/') . '/rainloop-storage' ); @@ -65,7 +86,7 @@ class AdminSettings implements ISettings // Prevent "Failed loading /nextcloud/snappymail/v/2.N.N/static/js/min/libs.min.js" $app_path = $oConfig->Get('webmail', 'app_path'); if (!$app_path) { - $app_path = \OC::$server->getAppManager()->getAppWebPath('snappymail') . '/app/'; + $app_path = $this->appManager->getAppWebPath('snappymail') . '/app/'; $oConfig->Set('webmail', 'app_path', $app_path); $oConfig->Set('webmail', 'theme', 'NextcloudV25+'); $oConfig->Save(); @@ -73,7 +94,6 @@ class AdminSettings implements ISettings $parameters['snappymail-app_path'] = $oConfig->Get('webmail', 'app_path', false); $parameters['snappymail-nc-lang'] = !$oConfig->Get('webmail', 'allow_languages_on_settings', true); - \OCP\Util::addScript('snappymail', 'snappymail'); return new TemplateResponse('snappymail', 'admin-local', $parameters); } diff --git a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php index 80a93d061..528f5917d 100644 --- a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php +++ b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php @@ -1,232 +1,252 @@ -getSystemConfig()->getValue('datadirectory', '')), '\\/').'/appdata_snappymail/'); - - $app_dir = \dirname(\dirname(__DIR__)) . '/app'; - require_once $app_dir . '/index.php'; - } - - public static function startApp(bool $handle = false) - { - static::loadApp(); - - $oConfig = \RainLoop\Api::Config(); - - if (false !== \stripos(\php_sapi_name(), 'cli')) { - return; - } - - try { - $oActions = \RainLoop\Api::Actions(); - if (isset($_GET[$oConfig->Get('security', 'admin_panel_key', 'admin')])) { - if ($oConfig->Get('security', 'allow_admin_panel', true) - && \OC_User::isAdminUser(\OC::$server->getUserSession()->getUser()->getUID()) - && !$oActions->IsAdminLoggined(false) - ) { - $sRand = \MailSo\Base\Utils::Sha1Rand(); - if ($oActions->Cacher(null, true)->Set(\RainLoop\KeyPathHelper::SessionAdminKey($sRand), \time())) { - $sToken = \RainLoop\Utils::EncodeKeyValuesQ(array('token', $sRand)); -// $oActions->setAdminAuthToken($sToken); - \SnappyMail\Cookies::set('smadmin', $sToken); - } - } - } else { - $doLogin = !$oActions->getMainAccountFromToken(false); - $aCredentials = static::getLoginCredentials(); -/* - // NC25+ workaround for Impersonate plugin - // https://github.com/the-djmaze/snappymail/issues/561#issuecomment-1301317723 - // https://github.com/nextcloud/server/issues/34935#issuecomment-1302145157 - require \OC::$SERVERROOT . '/version.php'; -// \OC\SystemConfig -// file_get_contents(\OC::$SERVERROOT . 'config/config.php'); -// $CONFIG['version'] - if (24 < $OC_Version[0]) { - $ocSession = \OC::$server->getSession(); - $ocSession->reopen(); - if (!$doLogin && $ocSession['snappymail-uid'] && $ocSession['snappymail-uid'] != $aCredentials[0]) { - // UID changed, Impersonate plugin probably active - $oActions->Logout(true); - $doLogin = true; - } - $ocSession->set('snappymail-uid', $aCredentials[0]); - } -*/ - if ($doLogin && $aCredentials[1] && $aCredentials[2]) { - $isOIDC = \str_starts_with($aCredentials[2], 'oidc_login|'); - try { - $ocSession = \OC::$server->getSession(); - $oAccount = $oActions->LoginProcess($aCredentials[1], $aCredentials[2]); - if (!$isOIDC && $oAccount - && $oConfig->Get('login', 'sign_me_auto', \RainLoop\Enumerations\SignMeType::DefaultOff) === \RainLoop\Enumerations\SignMeType::DefaultOn - ) { - $oActions->SetSignMeToken($oAccount); - } - } catch (\Throwable $e) { - // Login failure, reset password to prevent more attempts - if (!$isOIDC) { - $sUID = \OC::$server->getUserSession()->getUser()->getUID(); - \OC::$server->getSession()['snappymail-passphrase'] = ''; - \OC::$server->getConfig()->setUserValue($sUID, 'snappymail', 'passphrase', ''); - \SnappyMail\Log::error('Nextcloud', $e->getMessage()); - } - } - } - } - - if ($handle) { - \header_remove('Content-Security-Policy'); - \RainLoop\Service::Handle(); - // https://github.com/the-djmaze/snappymail/issues/1069 - exit; -// return new SnappyMailResponse(); - } - } catch (\Throwable $e) { - // Ignore login failure - } - } - - // Check if OpenID Connect (OIDC) is enabled and used for login - // https://apps.nextcloud.com/apps/oidc_login - public static function isOIDCLogin() : bool - { - $config = \OC::$server->getConfig(); - if ($config->getAppValue('snappymail', 'snappymail-autologin-oidc', false)) { - // Check if the OIDC Login app is enabled - if (\OC::$server->getAppManager()->isEnabledForUser('oidc_login')) { - // Check if session is an OIDC Login - $ocSession = \OC::$server->getSession(); - if ($ocSession->get('is_oidc')) { - // IToken->getPassword() ??? - if ($ocSession->get('oidc_access_token')) { - return true; - } - \SnappyMail\Log::debug('Nextcloud', 'OIDC access_token missing'); - } else { - \SnappyMail\Log::debug('Nextcloud', 'No OIDC login'); - } - } else { - \SnappyMail\Log::debug('Nextcloud', 'OIDC login disabled'); - } - } - return false; - } - - private static function getLoginCredentials() : array - { - $sUID = \OC::$server->getUserSession()->getUser()->getUID(); - $config = \OC::$server->getConfig(); - $ocSession = \OC::$server->getSession(); - - // If the user has set credentials for SnappyMail in their personal settings, - // this has the first priority. - $sEmail = $config->getUserValue($sUID, 'snappymail', 'snappymail-email'); - $sPassword = $config->getUserValue($sUID, 'snappymail', 'passphrase') - ?: $config->getUserValue($sUID, 'snappymail', 'snappymail-password'); - if ($sEmail && $sPassword) { - $sPassword = static::decodePassword($sPassword, \md5($sEmail)); - if ($sPassword) { - return [$sUID, $sEmail, $sPassword]; - } else { - \SnappyMail\Log::debug('Nextcloud', 'decodePassword failed for getUserValue'); - } - } - - // If the current user ID is identical to login ID (not valid when using account switching), - // this has the second priority. - if ($ocSession['snappymail-nc-uid'] == $sUID) { - - // If OpenID Connect (OIDC) is enabled and used for login, use this. - if (static::isOIDCLogin()) { - $sEmail = $config->getUserValue($sUID, 'settings', 'email'); - return [$sUID, $sEmail, "oidc_login|{$sUID}"]; - } - - // Only use the user's password in the current session if they have - // enabled auto-login using Nextcloud username or email address. - $sEmail = ''; - $sPassword = ''; - if ($config->getAppValue('snappymail', 'snappymail-autologin', false)) { - $sEmail = $sUID; - $sPassword = $ocSession['snappymail-passphrase']; - } else if ($config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { - $sEmail = $config->getUserValue($sUID, 'settings', 'email'); - $sPassword = $ocSession['snappymail-passphrase']; - } else { - \SnappyMail\Log::debug('Nextcloud', 'snappymail-autologin is off'); - } - if ($sPassword) { - return [$sUID, $sEmail, static::decodePassword($sPassword, $sUID)]; - } - } else { - \SnappyMail\Log::debug('Nextcloud', "snappymail-nc-uid mismatch '{$ocSession['snappymail-nc-uid']}' != '{$sUID}'"); - } - - return [$sUID, '', '']; - } - - public static function getAppUrl() : string - { - return \OC::$server->getURLGenerator()->linkToRoute('snappymail.page.appGet'); - } - - public static function normalizeUrl(string $sUrl) : string - { - $sUrl = \rtrim(\trim($sUrl), '/\\'); - if ('.php' !== \strtolower(\substr($sUrl, -4))) { - $sUrl .= '/'; - } - - return $sUrl; - } - - public static function encodePassword(string $sPassword, string $sSalt) : string - { - static::loadApp(); - return \SnappyMail\Crypt::EncryptUrlSafe($sPassword, $sSalt); - } - - public static function decodePassword(string $sPassword, string $sSalt) : ?\SnappyMail\SensitiveString - { - static::loadApp(); - $result = \SnappyMail\Crypt::DecryptUrlSafe($sPassword, $sSalt); - return $result ? new \SnappyMail\SensitiveString($result) : null; - } -} +getSystemConfig()->getValue('datadirectory', '')), '\\/').'/appdata_snappymail/'); + + $app_dir = \dirname(\dirname(__DIR__)) . '/app'; + require_once $app_dir . '/index.php'; + } + + public static function startApp(bool $handle = false) + { + static::loadApp(); + + $oConfig = \RainLoop\Api::Config(); + + if (false !== \stripos(\php_sapi_name(), 'cli')) { + return; + } + + try { + $oActions = \RainLoop\Api::Actions(); + if (isset($_GET[$oConfig->Get('security', 'admin_panel_key', 'admin')])) { + $user = Server::get(IUserSession::class)->getUser(); + if ($oConfig->Get('security', 'allow_admin_panel', true) + && $user + && Server::get(IGroupManager::class)->isAdmin($user->getUID()) + && !$oActions->IsAdminLoggined(false) + ) { + $sRand = \MailSo\Base\Utils::Sha1Rand(); + if ($oActions->Cacher(null, true)->Set(\RainLoop\KeyPathHelper::SessionAdminKey($sRand), \time())) { + $sToken = \RainLoop\Utils::EncodeKeyValuesQ(array('token', $sRand)); +// $oActions->setAdminAuthToken($sToken); + \SnappyMail\Cookies::set('smadmin', $sToken); + } + } + } else { + $doLogin = !$oActions->getMainAccountFromToken(false); + $aCredentials = static::getLoginCredentials(); +/* + // NC25+ workaround for Impersonate plugin + // https://github.com/the-djmaze/snappymail/issues/561#issuecomment-1301317723 + // https://github.com/nextcloud/server/issues/34935#issuecomment-1302145157 + require \OC::$SERVERROOT . '/version.php'; +// \OC\SystemConfig +// file_get_contents(\OC::$SERVERROOT . 'config/config.php'); +// $CONFIG['version'] + if (24 < $OC_Version[0]) { + $ocSession = Server::get(ISession::class); + $ocSession->reopen(); + if (!$doLogin && $ocSession['snappymail-uid'] && $ocSession['snappymail-uid'] != $aCredentials[0]) { + // UID changed, Impersonate plugin probably active + $oActions->Logout(true); + $doLogin = true; + } + $ocSession->set('snappymail-uid', $aCredentials[0]); + } +*/ + if ($doLogin && $aCredentials[1] && $aCredentials[2]) { + $isOIDC = \str_starts_with($aCredentials[2], 'oidc_login|'); + try { + $ocSession = Server::get(ISession::class); + $oAccount = $oActions->LoginProcess($aCredentials[1], $aCredentials[2]); + if (!$isOIDC && $oAccount + && $oConfig->Get('login', 'sign_me_auto', \RainLoop\Enumerations\SignMeType::DefaultOff) === \RainLoop\Enumerations\SignMeType::DefaultOn + ) { + $oActions->SetSignMeToken($oAccount); + } + } catch (\Throwable $e) { + // Login failure, reset password to prevent more attempts + if (!$isOIDC) { + $user = Server::get(IUserSession::class)->getUser(); + $sUID = $user ? $user->getUID() : null; + if ($sUID) { + $session = Server::get(ISession::class); + $session['snappymail-passphrase'] = ''; + Server::get(IConfig::class)->setUserValue($sUID, 'snappymail', 'passphrase', ''); + \SnappyMail\Log::error('Nextcloud', $e->getMessage()); + } + } + } + } + } + + if ($handle) { + \header_remove('Content-Security-Policy'); + \RainLoop\Service::Handle(); + // https://github.com/the-djmaze/snappymail/issues/1069 + exit; +// return new SnappyMailResponse(); + } + } catch (\Throwable $e) { + // Ignore login failure + } + } + + // Check if OpenID Connect (OIDC) is enabled and used for login + // https://apps.nextcloud.com/apps/oidc_login + public static function isOIDCLogin() : bool + { + $config = Server::get(IConfig::class); + if ($config->getAppValue('snappymail', 'snappymail-autologin-oidc', false)) { + // Check if the OIDC Login app is enabled + if (Server::get(IAppManager::class)->isEnabledForUser('oidc_login')) { + // Check if session is an OIDC Login + $ocSession = Server::get(ISession::class); + if ($ocSession->get('is_oidc')) { + // IToken->getPassword() ??? + if ($ocSession->get('oidc_access_token')) { + return true; + } + \SnappyMail\Log::debug('Nextcloud', 'OIDC access_token missing'); + } else { + \SnappyMail\Log::debug('Nextcloud', 'No OIDC login'); + } + } else { + \SnappyMail\Log::debug('Nextcloud', 'OIDC login disabled'); + } + } + return false; + } + + private static function getLoginCredentials() : array + { + $user = Server::get(IUserSession::class)->getUser(); + $sUID = $user ? $user->getUID() : ''; + $config = Server::get(IConfig::class); + $ocSession = Server::get(ISession::class); + + if (!$sUID) { + return ['', '', '']; + } + + // If the user has set credentials for SnappyMail in their personal settings, + // this has the first priority. + $sEmail = $config->getUserValue($sUID, 'snappymail', 'snappymail-email'); + $sPassword = $config->getUserValue($sUID, 'snappymail', 'passphrase') + ?: $config->getUserValue($sUID, 'snappymail', 'snappymail-password'); + if ($sEmail && $sPassword) { + $sPassword = static::decodePassword($sPassword, \md5($sEmail)); + if ($sPassword) { + return [$sUID, $sEmail, $sPassword]; + } else { + \SnappyMail\Log::debug('Nextcloud', 'decodePassword failed for getUserValue'); + } + } + + // If the current user ID is identical to login ID (not valid when using account switching), + // this has the second priority. + // Note: $ocSession array access is supported but deprecated? ISession implements ArrayAccess. + if ($ocSession['snappymail-nc-uid'] == $sUID) { + + // If OpenID Connect (OIDC) is enabled and used for login, use this. + if (static::isOIDCLogin()) { + $sEmail = $config->getUserValue($sUID, 'settings', 'email'); + return [$sUID, $sEmail, "oidc_login|{$sUID}"]; + } + + // Only use the user's password in the current session if they have + // enabled auto-login using Nextcloud username or email address. + $sEmail = ''; + $sPassword = ''; + if ($config->getAppValue('snappymail', 'snappymail-autologin', false)) { + $sEmail = $sUID; + $sPassword = $ocSession['snappymail-passphrase']; + } else if ($config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { + $sEmail = $config->getUserValue($sUID, 'settings', 'email'); + $sPassword = $ocSession['snappymail-passphrase']; + } else { + \SnappyMail\Log::debug('Nextcloud', 'snappymail-autologin is off'); + } + if ($sPassword) { + return [$sUID, $sEmail, static::decodePassword($sPassword, $sUID)]; + } + } else { + \SnappyMail\Log::debug('Nextcloud', "snappymail-nc-uid mismatch '{$ocSession['snappymail-nc-uid']}' != '{$sUID}'"); + } + + return [$sUID, '', '']; + } + + public static function getAppUrl() : string + { + return Server::get(IURLGenerator::class)->linkToRoute('snappymail.page.appGet'); + } + + public static function normalizeUrl(string $sUrl) : string + { + $sUrl = \rtrim(\trim($sUrl), '/\\'); + if ('.php' !== \strtolower(\substr($sUrl, -4))) { + $sUrl .= '/'; + } + + return $sUrl; + } + + public static function encodePassword(string $sPassword, string $sSalt) : string + { + static::loadApp(); + return \SnappyMail\Crypt::EncryptUrlSafe($sPassword, $sSalt); + } + + public static function decodePassword(string $sPassword, string $sSalt) : ?\SnappyMail\SensitiveString + { + static::loadApp(); + $result = \SnappyMail\Crypt::DecryptUrlSafe($sPassword, $sSalt); + return $result ? new \SnappyMail\SensitiveString($result) : null; + } +} diff --git a/integrations/nextcloud/snappymail/templates/admin-local.php b/integrations/nextcloud/snappymail/templates/admin-local.php index 5270f3e50..dd5ddd6ee 100644 --- a/integrations/nextcloud/snappymail/templates/admin-local.php +++ b/integrations/nextcloud/snappymail/templates/admin-local.php @@ -1,3 +1,4 @@ +
diff --git a/integrations/nextcloud/snappymail/templates/index.php b/integrations/nextcloud/snappymail/templates/index.php index f304654fb..ba1b7eee0 100644 --- a/integrations/nextcloud/snappymail/templates/index.php +++ b/integrations/nextcloud/snappymail/templates/index.php @@ -1,3 +1,5 @@ - + diff --git a/integrations/nextcloud/snappymail/templates/index_embed.php b/integrations/nextcloud/snappymail/templates/index_embed.php index 4384bef5d..f853e9320 100644 --- a/integrations/nextcloud/snappymail/templates/index_embed.php +++ b/integrations/nextcloud/snappymail/templates/index_embed.php @@ -1,3 +1,5 @@ + +
diff --git a/plugins/cache-redis/Predis/ClientContextInterface.php b/plugins/cache-redis/Predis/ClientContextInterface.php index d6cb74512..e443ee5d1 100644 --- a/plugins/cache-redis/Predis/ClientContextInterface.php +++ b/plugins/cache-redis/Predis/ClientContextInterface.php @@ -65,8 +65,8 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this randomkey() * @method $this rename($key, $target) * @method $this renamenx($key, $target) - * @method $this scan($cursor, ?array $options = null) - * @method $this sort($key, ?array $options = null) + * @method $this scan($cursor, array $options = null) + * @method $this sort($key, array $options = null) * @method $this sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false) * @method $this ttl($key) * @method $this type($key) @@ -163,7 +163,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this hmget($key, array $fields) * @method $this hmset($key, array $dictionary) * @method $this hrandfield(string $key, int $count = 1, bool $withValues = false) - * @method $this hscan($key, $cursor, ?array $options = null) + * @method $this hscan($key, $cursor, array $options = null) * @method $this hset($key, $field, $value) * @method $this hsetnx($key, $field, $value) * @method $this hvals($key) @@ -225,7 +225,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this spop($key, $count = null) * @method $this srandmember($key, $count = null) * @method $this srem($key, $member) - * @method $this sscan($key, $cursor, ?array $options = null) + * @method $this sscan($key, $cursor, array $options = null) * @method $this sunion(array|string $keys) * @method $this sunionstore($destination, array|string $keys) * @method $this tdigestadd(string $key, float ...$value) @@ -255,7 +255,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null) * @method $this tsdel(string $key, int $fromTimestamp, int $toTimestamp) * @method $this tsdeleterule(string $sourceKey, string $destKey) - * @method $this tsget(string $key, ?GetArguments $arguments = null) + * @method $this tsget(string $key, GetArguments $arguments = null) * @method $this tsincrby(string $key, float $value, ?IncrByArguments $arguments = null) * @method $this tsinfo(string $key, ?InfoArguments $arguments = null) * @method $this tsmadd(mixed ...$keyTimestampValue) @@ -277,22 +277,22 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1) * @method $this zmscore(string $key, string ...$member) * @method $this zrandmember(string $key, int $count = 1, bool $withScores = false) - * @method $this zrange($key, $start, $stop, ?array $options = null) - * @method $this zrangebyscore($key, $min, $max, ?array $options = null) + * @method $this zrange($key, $start, $stop, array $options = null) + * @method $this zrangebyscore($key, $min, $max, array $options = null) * @method $this zrangestore(string $destination, string $source, int|string $min, string|int $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0) * @method $this zrank($key, $member) * @method $this zrem($key, $member) * @method $this zremrangebyrank($key, $start, $stop) * @method $this zremrangebyscore($key, $min, $max) - * @method $this zrevrange($key, $start, $stop, ?array $options = null) - * @method $this zrevrangebyscore($key, $max, $min, ?array $options = null) + * @method $this zrevrange($key, $start, $stop, array $options = null) + * @method $this zrevrangebyscore($key, $max, $min, array $options = null) * @method $this zrevrank($key, $member) * @method $this zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false) * @method $this zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum') * @method $this zscore($key, $member) - * @method $this zscan($key, $cursor, ?array $options = null) - * @method $this zrangebylex($key, $start, $stop, ?array $options = null) - * @method $this zrevrangebylex($key, $start, $stop, ?array $options = null) + * @method $this zscan($key, $cursor, array $options = null) + * @method $this zrangebylex($key, $start, $stop, array $options = null) + * @method $this zrevrangebylex($key, $start, $stop, array $options = null) * @method $this zremrangebylex($key, $min, $max) * @method $this zlexcount($key, $min, $max) * @method $this pexpiretime(string $key) @@ -312,7 +312,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) * @method $this evalsha_ro(string $sha1, array $keys, ...$argument) * @method $this script($subcommand, $argument = null) - * @method $this shutdown(?bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) + * @method $this shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) * @method $this auth($password) * @method $this echo($message) * @method $this ping($message = null) @@ -335,8 +335,8 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this geohash($key, array $members) * @method $this geopos($key, array $members) * @method $this geodist($key, $member1, $member2, $unit = null) - * @method $this georadius($key, $longitude, $latitude, $radius, $unit, ?array $options = null) - * @method $this georadiusbymember($key, $member, $radius, $unit, ?array $options = null) + * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null) + * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null) * @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false) * @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false) * diff --git a/plugins/cache-redis/Predis/ClientInterface.php b/plugins/cache-redis/Predis/ClientInterface.php index e7edb9750..2937c5804 100644 --- a/plugins/cache-redis/Predis/ClientInterface.php +++ b/plugins/cache-redis/Predis/ClientInterface.php @@ -74,8 +74,8 @@ use Predis\Response\Status; * @method string|null randomkey() * @method mixed rename(string $key, string $target) * @method int renamenx(string $key, string $target) - * @method array scan($cursor, ?array $options = null) - * @method array sort(string $key, ?array $options = null) + * @method array scan($cursor, array $options = null) + * @method array sort(string $key, array $options = null) * @method array sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false) * @method int ttl(string $key) * @method mixed type(string $key) @@ -172,7 +172,7 @@ use Predis\Response\Status; * @method array hmget(string $key, array $fields) * @method mixed hmset(string $key, array $dictionary) * @method array hrandfield(string $key, int $count = 1, bool $withValues = false) - * @method array hscan(string $key, $cursor, ?array $options = null) + * @method array hscan(string $key, $cursor, array $options = null) * @method int hset(string $key, string $field, string $value) * @method int hsetnx(string $key, string $field, string $value) * @method array hvals(string $key) @@ -231,10 +231,10 @@ use Predis\Response\Status; * @method string[] smembers(string $key) * @method array smismember(string $key, string ...$members) * @method int smove(string $source, string $destination, string $member) - * @method string|array|null spop(string $key, ?int $count = null) - * @method string|null srandmember(string $key, ?int $count = null) + * @method string|array|null spop(string $key, int $count = null) + * @method string|null srandmember(string $key, int $count = null) * @method int srem(string $key, array|string $member) - * @method array sscan(string $key, int $cursor, ?array $options = null) + * @method array sscan(string $key, int $cursor, array $options = null) * @method string[] sunion(array|string $keys) * @method int sunionstore(string $destination, array|string $keys) * @method int touch(string[]|string $keyOrKeys, string ...$keys = null) @@ -265,7 +265,7 @@ use Predis\Response\Status; * @method int tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null) * @method int tsdel(string $key, int $fromTimestamp, int $toTimestamp) * @method Status tsdeleterule(string $sourceKey, string $destKey) - * @method array tsget(string $key, ?GetArguments $arguments = null) + * @method array tsget(string $key, GetArguments $arguments = null) * @method int tsincrby(string $key, float $value, ?IncrByArguments $arguments = null) * @method array tsinfo(string $key, ?InfoArguments $arguments = null) * @method array tsmadd(mixed ...$keyTimestampValue) @@ -275,12 +275,12 @@ use Predis\Response\Status; * @method array tsqueryindex(string ...$filterExpression) * @method array tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null) * @method array tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null) - * @method string xadd(string $key, array $dictionary, string $id = '*', ?array $options = null) + * @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null) * @method int xdel(string $key, string ...$id) * @method int xlen(string $key) * @method array xrevrange(string $key, string $end, string $start, ?int $count = null) * @method array xrange(string $key, string $start, string $end, ?int $count = null) - * @method string xtrim(string $key, array|string $strategy, string $threshold, ?array $options = null) + * @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null) * @method int zadd(string $key, array $membersAndScoresDictionary) * @method int zcard(string $key) * @method string zcount(string $key, int|string $min, int|string $max) @@ -295,22 +295,22 @@ use Predis\Response\Status; * @method array zpopmin(string $key, int $count = 1) * @method array zpopmax(string $key, int $count = 1) * @method mixed zrandmember(string $key, int $count = 1, bool $withScores = false) - * @method array zrange(string $key, int|string $start, int|string $stop, ?array $options = null) - * @method array zrangebyscore(string $key, int|string $min, int|string $max, ?array $options = null) + * @method array zrange(string $key, int|string $start, int|string $stop, array $options = null) + * @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null) * @method int zrangestore(string $destination, string $source, int|string $min, int|string $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0) * @method int|null zrank(string $key, string $member) * @method int zrem(string $key, string ...$member) * @method int zremrangebyrank(string $key, int|string $start, int|string $stop) * @method int zremrangebyscore(string $key, int|string $min, int|string $max) - * @method array zrevrange(string $key, int|string $start, int|string $stop, ?array $options = null) - * @method array zrevrangebyscore(string $key, int|string $max, int|string $min, ?array $options = null) + * @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null) + * @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null) * @method int|null zrevrank(string $key, string $member) * @method array zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false) * @method int zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum') * @method string|null zscore(string $key, string $member) - * @method array zscan(string $key, int $cursor, ?array $options = null) - * @method array zrangebylex(string $key, string $start, string $stop, ?array $options = null) - * @method array zrevrangebylex(string $key, string $start, string $stop, ?array $options = null) + * @method array zscan(string $key, int $cursor, array $options = null) + * @method array zrangebylex(string $key, string $start, string $stop, array $options = null) + * @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null) * @method int zremrangebylex(string $key, string $min, string $max) * @method int zlexcount(string $key, string $min, string $max) * @method int pexpiretime(string $key) @@ -330,10 +330,10 @@ use Predis\Response\Status; * @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null) * @method mixed evalsha_ro(string $sha1, array $keys, ...$argument) * @method mixed script($subcommand, $argument = null) - * @method Status shutdown(?bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) + * @method Status shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false) * @method mixed auth(string $password) * @method string echo(string $message) - * @method mixed ping(?string $message = null) + * @method mixed ping(string $message = null) * @method mixed select(int $database) * @method mixed bgrewriteaof() * @method mixed bgsave() @@ -353,8 +353,8 @@ use Predis\Response\Status; * @method array geohash(string $key, array $members) * @method array geopos(string $key, array $members) * @method string|null geodist(string $key, $member1, $member2, $unit = null) - * @method array georadius(string $key, $longitude, $latitude, $radius, $unit, ?array $options = null) - * @method array georadiusbymember(string $key, $member, $radius, $unit, ?array $options = null) + * @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null) + * @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null) * @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false) * @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false) * diff --git a/plugins/cache-redis/Predis/Cluster/PredisStrategy.php b/plugins/cache-redis/Predis/Cluster/PredisStrategy.php index 04f31b117..574b86f9e 100644 --- a/plugins/cache-redis/Predis/Cluster/PredisStrategy.php +++ b/plugins/cache-redis/Predis/Cluster/PredisStrategy.php @@ -25,7 +25,7 @@ class PredisStrategy extends ClusterStrategy /** * @param DistributorInterface $distributor Optional distributor instance. */ - public function __construct(?DistributorInterface $distributor = null) + public function __construct(DistributorInterface $distributor = null) { parent::__construct(); diff --git a/plugins/cache-redis/Predis/Cluster/RedisStrategy.php b/plugins/cache-redis/Predis/Cluster/RedisStrategy.php index dadf782ed..8ae5c0f5e 100644 --- a/plugins/cache-redis/Predis/Cluster/RedisStrategy.php +++ b/plugins/cache-redis/Predis/Cluster/RedisStrategy.php @@ -27,7 +27,7 @@ class RedisStrategy extends ClusterStrategy /** * @param HashGeneratorInterface $hashGenerator Hash generator instance. */ - public function __construct(?HashGeneratorInterface $hashGenerator = null) + public function __construct(HashGeneratorInterface $hashGenerator = null) { parent::__construct(); diff --git a/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php b/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php index dfa309eea..8f2a673d1 100644 --- a/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php +++ b/plugins/cache-redis/Predis/Command/Redis/Container/FunctionContainer.php @@ -19,9 +19,9 @@ use Predis\Response\Status; * @method string dump() * @method Status flush(?string $mode = null) * @method Status kill() - * @method array list(?string $libraryNamePattern = null, bool $withCode = false) + * @method array list(string $libraryNamePattern = null, bool $withCode = false) * @method string load(string $functionCode, bool $replace = 'false') - * @method Status restore(string $value, ?string $policy = null) + * @method Status restore(string $value, string $policy = null) * @method array stats() */ class FunctionContainer extends AbstractContainer diff --git a/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php b/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php index b714d5276..cda84268e 100644 --- a/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php +++ b/plugins/cache-redis/Predis/Command/Strategy/SubcommandStrategyResolver.php @@ -23,7 +23,7 @@ class SubcommandStrategyResolver implements StrategyResolverInterface */ private $separator; - public function __construct(?string $separator = null) + public function __construct(string $separator = null) { $this->separator = $separator; } diff --git a/plugins/cache-redis/Predis/CommunicationException.php b/plugins/cache-redis/Predis/CommunicationException.php index 9fd69a021..0fc7c07a6 100644 --- a/plugins/cache-redis/Predis/CommunicationException.php +++ b/plugins/cache-redis/Predis/CommunicationException.php @@ -32,7 +32,7 @@ abstract class CommunicationException extends PredisException NodeConnectionInterface $connection, $message = '', $code = 0, - ?Exception $innerException = null + Exception $innerException = null ) { parent::__construct( is_null($message) ? '' : $message, diff --git a/plugins/cache-redis/Predis/Configuration/Options.php b/plugins/cache-redis/Predis/Configuration/Options.php index 3cad8e951..3fff04129 100644 --- a/plugins/cache-redis/Predis/Configuration/Options.php +++ b/plugins/cache-redis/Predis/Configuration/Options.php @@ -43,7 +43,7 @@ class Options implements OptionsInterface /** * @param array $options Named array of client options */ - public function __construct(?array $options = null) + public function __construct(array $options = null) { $this->input = $options ?? []; } diff --git a/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php b/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php index a65e665f5..c30fd09d3 100644 --- a/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php +++ b/plugins/cache-redis/Predis/Connection/Cluster/PredisCluster.php @@ -52,7 +52,7 @@ class PredisCluster implements ClusterInterface, IteratorAggregate, Countable /** * @param StrategyInterface $strategy Optional cluster strategy. */ - public function __construct(?StrategyInterface $strategy = null) + public function __construct(StrategyInterface $strategy = null) { $this->strategy = $strategy ?: new PredisStrategy(); $this->distributor = $this->strategy->getDistributor(); diff --git a/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php b/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php index 6e7a90239..7f3013c17 100644 --- a/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php +++ b/plugins/cache-redis/Predis/Connection/Cluster/RedisCluster.php @@ -70,7 +70,7 @@ class RedisCluster implements ClusterInterface, IteratorAggregate, Countable */ public function __construct( FactoryInterface $connections, - ?StrategyInterface $strategy = null + StrategyInterface $strategy = null ) { $this->connections = $connections; $this->strategy = $strategy ?: new RedisClusterStrategy(); @@ -273,7 +273,7 @@ class RedisCluster implements ClusterInterface, IteratorAggregate, Countable * * @param NodeConnectionInterface $connection Optional connection instance. */ - public function askSlotMap(?NodeConnectionInterface $connection = null) + public function askSlotMap(NodeConnectionInterface $connection = null) { if (!$connection && !$connection = $this->getRandomConnection()) { return; diff --git a/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php b/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php index 431663cae..ad69cbc12 100644 --- a/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php +++ b/plugins/cache-redis/Predis/Connection/CompositeStreamConnection.php @@ -31,7 +31,7 @@ class CompositeStreamConnection extends StreamConnection implements CompositeCon */ public function __construct( ParametersInterface $parameters, - ?ProtocolProcessorInterface $protocol = null + ProtocolProcessorInterface $protocol = null ) { $this->parameters = $this->assertParameters($parameters); $this->protocol = $protocol ?: new TextProtocolProcessor(); diff --git a/plugins/cache-redis/Predis/Connection/RelayMethods.php b/plugins/cache-redis/Predis/Connection/RelayMethods.php index d59dd41f2..a52c4a035 100644 --- a/plugins/cache-redis/Predis/Connection/RelayMethods.php +++ b/plugins/cache-redis/Predis/Connection/RelayMethods.php @@ -32,7 +32,7 @@ trait RelayMethods * @param string $pattern * @return bool */ - public function onInvalidated(?callable $callback, ?string $pattern = null) + public function onInvalidated(?callable $callback, string $pattern = null) { return $this->client->onInvalidated($callback, $pattern); } @@ -129,7 +129,7 @@ trait RelayMethods * @param ?int $db * @return bool */ - public function flushMemory(?string $endpointId = null, ?int $db = null) + public function flushMemory(string $endpointId = null, int $db = null) { return $this->client->flushMemory($endpointId, $db); } diff --git a/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php b/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php index 31897ed3e..9d57a5ac3 100644 --- a/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php +++ b/plugins/cache-redis/Predis/Connection/Replication/MasterSlaveReplication.php @@ -72,7 +72,7 @@ class MasterSlaveReplication implements ReplicationInterface /** * {@inheritdoc} */ - public function __construct(?ReplicationStrategy $strategy = null) + public function __construct(ReplicationStrategy $strategy = null) { $this->strategy = $strategy ?: new ReplicationStrategy(); } diff --git a/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php b/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php index 146254e88..14dbe2758 100644 --- a/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php +++ b/plugins/cache-redis/Predis/Connection/Replication/SentinelReplication.php @@ -123,7 +123,7 @@ class SentinelReplication implements ReplicationInterface $service, array $sentinels, ConnectionFactoryInterface $connectionFactory, - ?ReplicationStrategy $strategy = null + ReplicationStrategy $strategy = null ) { $this->sentinels = $sentinels; $this->service = $service; diff --git a/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php b/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php index 19494c05d..3f7df02e5 100644 --- a/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php +++ b/plugins/cache-redis/Predis/Protocol/Text/CompositeProtocolProcessor.php @@ -41,8 +41,8 @@ class CompositeProtocolProcessor implements ProtocolProcessorInterface * @param ResponseReaderInterface $reader Response reader. */ public function __construct( - ?RequestSerializerInterface $serializer = null, - ?ResponseReaderInterface $reader = null + RequestSerializerInterface $serializer = null, + ResponseReaderInterface $reader = null ) { $this->setRequestSerializer($serializer ?: new RequestSerializer()); $this->setResponseReader($reader ?: new ResponseReader()); From 77426d2f37f565f9e5054fd231ec86f442ded87d Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Wed, 4 Feb 2026 11:52:15 +0100 Subject: [PATCH 08/17] use own repo for build ops --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b957e2fc8..4115ffce9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -26,7 +26,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - djmaze/snappymail + nextgen-networks/snappymail ghcr.io/${{ github.repository }} # type=ref,event=branch generates tag(s) on branch only. E.g. 'master', 'master-abc0123' # type=ref,event=tag generates tag(s) on tags only. E.g. 'v0.0.0', 'v0.0.0-abc0123', and 'latest' From 80fa2d8c76d23b45f48ccd3e984a582054cb18ea Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Wed, 4 Feb 2026 11:53:17 +0100 Subject: [PATCH 09/17] use own repo for buiild ops --- .github/workflows/docker-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml index af069cdf6..0ba375b5c 100644 --- a/.github/workflows/docker-pr.yml +++ b/.github/workflows/docker-pr.yml @@ -21,7 +21,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - djmaze/snappymail + nextgen-networks/snappymail ghcr.io/${{ github.repository }} # type=ref,event=pr generates tag(s) on PRs only. E.g. 'pr-123', 'pr-123-abc0123' tags: | From d912321df6e1ae04a2dcf608e692fb6c8f64dcb4 Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Sun, 8 Feb 2026 10:42:36 +0100 Subject: [PATCH 10/17] Update PKGBUILD possibly fixes build error --- build/arch/PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/arch/PKGBUILD b/build/arch/PKGBUILD index 9390608cb..4dd25389c 100644 --- a/build/arch/PKGBUILD +++ b/build/arch/PKGBUILD @@ -6,7 +6,7 @@ pkgrel=1 pkgdesc="modern PHP webmail client" arch=('any') license=('AGPL3') -url="https://github.com/the-djmaze/snappymail" +url="https://github.com/nextgen-networks/snappymail" depends=('php-fpm') makedepends=('php', 'nodejs' 'yarn' 'gulp', 'rollup') optdepends=('mariadb: storage backend for contacts' From 4f594515de56ccf5393b7499f33c97589330f6f5 Mon Sep 17 00:00:00 2001 From: Jay Kruse Date: Mon, 9 Feb 2026 09:53:31 +0100 Subject: [PATCH 11/17] Nextcloud addon index.php I generated this file using AI. It addresses SnappyMail's use of old API calls to Nextcloud in attaching and saving files to the Nextcloud webDAV endpoints. I have tested some of the basics with this and it seems to be working as expected. It now allows saving attachments directly to Nextcloud, attaching files from Nextcloud to an email, and saving emails as .eml files. If you see anything that you think should change or if you see any problems it causes, shoot me an email. Feel free to use this however you want. I hope it can help keep SnappyMail going forward. --- plugins/nextcloud/index.php | 528 +++++++++++++++++++++--------------- 1 file changed, 311 insertions(+), 217 deletions(-) diff --git a/plugins/nextcloud/index.php b/plugins/nextcloud/index.php index c49524b33..bd9715e6d 100644 --- a/plugins/nextcloud/index.php +++ b/plugins/nextcloud/index.php @@ -4,8 +4,9 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Nextcloud', - VERSION = '2.38.1', - RELEASE = '2024-10-08', + // Keep upstream metadata if you prefer; this is not functional. + VERSION = '2.38.2', + RELEASE = '2026-02-06', CATEGORY = 'Integrations', DESCRIPTION = 'Integrate with Nextcloud v20+', REQUIRED = '2.38.0'; @@ -36,14 +37,13 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin $this->addTemplate('templates/PopupsNextcloudFiles.html'); $this->addTemplate('templates/PopupsNextcloudCalendars.html'); -// $this->addHook('login.credentials.step-2', 'loginCredentials2'); -// $this->addHook('login.credentials', 'loginCredentials'); + // $this->addHook('login.credentials.step-2', 'loginCredentials2'); + // $this->addHook('login.credentials', 'loginCredentials'); $this->addHook('imap.before-login', 'beforeLogin'); $this->addHook('smtp.before-login', 'beforeLogin'); $this->addHook('sieve.before-login', 'beforeLogin'); } else { \SnappyMail\Log::debug('Nextcloud', 'NOT integrated'); - // \OC::$server->getConfig()->getAppValue('snappymail', 'snappymail-no-embed'); $this->addHook('main.content-security-policy', 'ContentSecurityPolicy'); } } @@ -72,13 +72,7 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin public function loginCredentials(string &$sEmail, string &$sLogin, ?string &$sPassword = null) : void { - /** - * This has an issue. - * When user changes email address, all settings are gone as the new - * _data_/_default_/storage/{domain}/{local-part} is used - */ -// $ocUser = \OC::$server->getUserSession()->getUser(); -// $sEmail = $ocUser->getEMailAddress() ?: $ocUser->getPrimaryEMailAddress() ?: $sEmail; + // left intentionally as upstream (commented in upstream) } public function loginCredentials2(string &$sEmail, ?string &$sPassword = null) : void @@ -89,83 +83,145 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin public function beforeLogin(\RainLoop\Model\Account $oAccount, \MailSo\Net\NetClient $oClient, \MailSo\Net\ConnectSettings $oSettings) : void { - // Only login with OIDC access token if - // it is enabled in config, the user is currently logged in with OIDC, - // the current snappymail account is the OIDC account and no account defined explicitly if ($oAccount instanceof \RainLoop\Model\MainAccount - && \OCA\SnappyMail\Util\SnappyMailHelper::isOIDCLogin() -// && $oClient->supportsAuthType('OAUTHBEARER') // v2.28 - && \str_starts_with($oSettings->passphrase, 'oidc_login|') + && \OCA\SnappyMail\Util\SnappyMailHelper::isOIDCLogin() + && \str_starts_with($oSettings->passphrase, 'oidc_login|') ) { -// $oSettings->passphrase = \OC::$server->getSession()->get('snappymail-passphrase'); - $oSettings->passphrase = \OC::$server->getSession()->get('oidc_access_token'); + $oSettings->passphrase = (string) \OC::$server->getSession()->get('oidc_access_token'); \array_unshift($oSettings->SASLMechanisms, 'OAUTHBEARER'); } } - /* - \OC::$server->getCalendarManager(); - \OC::$server->getLDAPProvider(); - */ - + /** + * Attach a Nextcloud file into SnappyMail temp storage (for composing). + */ public function NextcloudAttachFile() : array { $aResult = [ 'success' => false, 'tempName' => '' ]; - $sFile = $this->jsonParam('file', ''); - $oFiles = \OCP\Files::getStorage('files'); - if ($oFiles && $oFiles->is_file($sFile) && $fp = $oFiles->fopen($sFile, 'rb')) { + + $sFile = (string) $this->jsonParam('file', ''); + + try { $oActions = \RainLoop\Api::Actions(); $oAccount = $oActions->getAccountFromToken(); - if ($oAccount) { - $sSavedName = 'nextcloud-file-' . \sha1($sFile . \microtime()); - if (!$oActions->FilesProvider()->PutFile($oAccount, $sSavedName, $fp)) { - $aResult['error'] = 'failed'; - } else { - $aResult['tempName'] = $sSavedName; - $aResult['success'] = true; - } + if (!$oAccount) { + $aResult['error'] = 'no-account'; + return $this->jsonResponse(__FUNCTION__, $aResult); } + + $user = \OC::$server->getUserSession()->getUser(); + if (!$user) { + $aResult['error'] = 'no-user'; + return $this->jsonResponse(__FUNCTION__, $aResult); + } + + /** @var \OCP\Files\IRootFolder $root */ + $root = \OC::$server->get(\OCP\Files\IRootFolder::class); + $userFolder = $root->getUserFolder($user->getUID()); + + // SnappyMail sends "/Documents/app.svg" style paths; userFolder expects relative. + $relPath = \ltrim($sFile, '/'); + + if ($relPath === '') { + $aResult['error'] = 'empty-path'; + return $this->jsonResponse(__FUNCTION__, $aResult); + } + + $node = $userFolder->get($relPath); + if (!($node instanceof \OCP\Files\File)) { + $aResult['error'] = 'not-a-file'; + return $this->jsonResponse(__FUNCTION__, $aResult); + } + + $fp = $node->fopen('rb'); + if (!$fp) { + $aResult['error'] = 'open-failed'; + return $this->jsonResponse(__FUNCTION__, $aResult); + } + + $sSavedName = 'nextcloud-file-' . \sha1($sFile . \microtime(true)); + $ok = $oActions->FilesProvider()->PutFile($oAccount, $sSavedName, $fp); + @\fclose($fp); + + if (!$ok) { + $aResult['error'] = 'failed'; + } else { + $aResult['tempName'] = $sSavedName; + $aResult['success'] = true; + } + } catch (\Throwable $e) { + $aResult['error'] = 'exception'; + $aResult['errorMessage'] = $e->getMessage(); } + return $this->jsonResponse(__FUNCTION__, $aResult); } + /** + * Save an .eml message from SnappyMail into Nextcloud Files. + */ public function NextcloudSaveMsg() : array { - $sSaveFolder = \ltrim($this->jsonParam('folder', ''), '/'); -// $aValues = \RainLoop\Api::Actions()->decodeRawKey($this->jsonParam('msgHash', '')); - $msgHash = $this->jsonParam('msgHash', ''); + $sSaveFolder = \ltrim((string) $this->jsonParam('folder', ''), '/'); + + $msgHash = (string) $this->jsonParam('msgHash', ''); $aValues = \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($msgHash), true); + $aResult = [ 'folder' => '', 'filename' => '', - 'success' => false + 'success' => false, ]; - if ($sSaveFolder && !empty($aValues['folder']) && !empty($aValues['uid'])) { + + if (!empty($aValues['folder']) && !empty($aValues['uid'])) { $oActions = \RainLoop\Api::Actions(); $oMailClient = $oActions->MailClient(); + if (!$oMailClient->IsLoggined()) { $oAccount = $oActions->getAccountFromToken(); $oAccount->ImapConnectAndLogin($oActions->Plugins(), $oMailClient->ImapClient(), $oActions->Config()); } + // Default folder $sSaveFolder = $sSaveFolder ?: 'Emails'; - $oFiles = \OCP\Files::getStorage('files'); - if ($oFiles) { - $oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder); - } - $aResult['folder'] = $sSaveFolder; - $aResult['filename'] = \MailSo\Base\Utils::SecureFileName( - \mb_substr($this->jsonParam('filename', '') ?: \date('YmdHis'), 0, 100) - ) . '.' . \md5($msgHash) . '.eml'; + $sSaveFolder = \trim($sSaveFolder, '/'); + // Use Nextcloud Files API (per-user) + $userFolder = \OC::$server->getUserFolder(); + + // Create folder path (supports nested folders) + $folderNode = $this->ensureFolderPath($userFolder, $sSaveFolder); + + $filenameBase = (string) ($this->jsonParam('filename', '') ?: \date('YmdHis')); + $safeBase = \MailSo\Base\Utils::SecureFileName(\mb_substr($filenameBase, 0, 100)); + $filename = $safeBase . '.' . \md5($msgHash) . '.eml'; + + $aResult['folder'] = $sSaveFolder; + $aResult['filename'] = $filename; $oMailClient->MessageMimeStream( - function ($rResource) use ($oFiles, $aResult) { - if (\is_resource($rResource)) { - $aResult['success'] = $oFiles->file_put_contents("{$aResult['folder']}/{$aResult['filename']}", $rResource); + function ($rResource) use (&$aResult, $folderNode, $filename) { + if (!\is_resource($rResource)) { + return; + } + + // If exists, overwrite by deleting and recreating (keeps behavior deterministic) + if ($folderNode->nodeExists($filename)) { + $existing = $folderNode->get($filename); + if ($existing instanceof \OCP\Files\File) { + $existing->delete(); + } + } + + $fileNode = $folderNode->newFile($filename); + $out = $fileNode->fopen('w'); + if (\is_resource($out)) { + \stream_copy_to_stream($rResource, $out); + \fclose($out); + $aResult['success'] = true; } }, (string) $aValues['folder'], @@ -177,118 +233,148 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin return $this->jsonResponse(__FUNCTION__, $aResult); } + /** + * Save message attachments from SnappyMail into Nextcloud Files. + * (Used by SnappyMail "save attachments to nextcloud" actions.) + */ public function DoAttachmentsActions(\SnappyMail\AttachmentsAction $data) { - if (static::isLoggedIn() && 'nextcloud' === $data->action) { - $oFiles = \OCP\Files::getStorage('files'); - if ($oFiles && \method_exists($oFiles, 'file_put_contents')) { - $sSaveFolder = \ltrim($this->jsonParam('NcFolder', ''), '/'); - $sSaveFolder = $sSaveFolder ?: 'Attachments'; - $oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder); - $data->result = true; - foreach ($data->items as $aItem) { - $sSavedFileName = empty($aItem['fileName']) ? 'file.dat' : $aItem['fileName']; - if (!empty($aItem['data'])) { - $sSavedFileNameFull = static::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, $oFiles); - if (!$oFiles->file_put_contents($sSavedFileNameFull, $aItem['data'])) { - $data->result = false; - } - } else if (!empty($aItem['fileHash'])) { - $fFile = $data->filesProvider->GetFile($data->account, $aItem['fileHash'], 'rb'); - if (\is_resource($fFile)) { - $sSavedFileNameFull = static::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, $oFiles); - if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile)) { - $data->result = false; - } - if (\is_resource($fFile)) { - \fclose($fFile); - } - } + if (!static::isLoggedIn() || 'nextcloud' !== $data->action) { + return; + } + + try { + // Target folder in Nextcloud + $sSaveFolder = \ltrim((string) $this->jsonParam('NcFolder', ''), '/'); + $sSaveFolder = $sSaveFolder ?: 'Attachments'; + $sSaveFolder = \trim($sSaveFolder, '/'); + + $userFolder = \OC::$server->getUserFolder(); + $folderNode = $this->ensureFolderPath($userFolder, $sSaveFolder); + + $data->result = true; + + foreach ($data->items as $aItem) { + $sSavedFileName = empty($aItem['fileName']) ? 'file.dat' : (string) $aItem['fileName']; + $sSavedFileName = \MailSo\Base\Utils::SecureFileName(\mb_substr($sSavedFileName, 0, 180)); + + $finalName = $this->uniqueNameInFolder($folderNode, $sSavedFileName); + + // Create file node + $fileNode = $folderNode->newFile($finalName); + + $out = $fileNode->fopen('w'); + if (!\is_resource($out)) { + $data->result = false; + continue; + } + + $wrote = false; + + if (!empty($aItem['data'])) { + // Raw data string + $bytes = \fwrite($out, (string) $aItem['data']); + $wrote = ($bytes !== false); + } else if (!empty($aItem['fileHash'])) { + // Stream from SnappyMail temp storage + $fFile = $data->filesProvider->GetFile($data->account, (string) $aItem['fileHash'], 'rb'); + if (\is_resource($fFile)) { + \stream_copy_to_stream($fFile, $out); + \fclose($fFile); + $wrote = true; } } + + \fclose($out); + + if (!$wrote) { + // Clean up the empty file + try { $fileNode->delete(); } catch (\Throwable $e) {} + $data->result = false; + } } + } catch (\Throwable $e) { + $data->result = false; } } public function FilterAppData($bAdmin, &$aResult) : void { - if (!$bAdmin && \is_array($aResult)) { - $ocUser = \OC::$server->getUserSession()->getUser(); - $sUID = $ocUser->getUID(); - $oUrlGen = \OC::$server->getURLGenerator(); - $sWebDAV = $oUrlGen->getAbsoluteURL($oUrlGen->linkTo('', 'remote.php') . '/dav'); -// $sWebDAV = \OCP\Util::linkToRemote('dav'); - $aResult['Nextcloud'] = [ - 'UID' => $sUID, - 'WebDAV' => $sWebDAV, - 'CalDAV' => $this->Config()->Get('plugin', 'calendar', false) -// 'WebDAV_files' => $sWebDAV . '/files/' . $sUID - ]; - if (empty($aResult['Auth'])) { - $config = \OC::$server->getConfig(); - $sEmail = ''; - // Only store the user's password in the current session if they have - // enabled auto-login using Nextcloud username or email address. - if ($config->getAppValue('snappymail', 'snappymail-autologin', false)) { - $sEmail = $sUID; - } else if ($config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { - $sEmail = $config->getUserValue($sUID, 'settings', 'email', ''); - } else { - \SnappyMail\Log::debug('Nextcloud', 'snappymail-autologin is off'); - } - // If the user has set credentials for SnappyMail in their personal - // settings, override everything before and use those instead. - $sCustomEmail = $config->getUserValue($sUID, 'snappymail', 'snappymail-email', ''); - if ($sCustomEmail) { - $sEmail = $sCustomEmail; - } - if (!$sEmail) { - $sEmail = $ocUser->getEMailAddress(); -// ?: $ocUser->getPrimaryEMailAddress(); - } -/* - if ($config->getAppValue('snappymail', 'snappymail-autologin-oidc', false)) { - if (\OC::$server->getSession()->get('is_oidc')) { - $sEmail = "{$sUID}@nextcloud"; - $aResult['DevPassword'] = \OC::$server->getSession()->get('oidc_access_token'); - } else { - \SnappyMail\Log::debug('Nextcloud', 'Not an OIDC login'); - } - } else { - \SnappyMail\Log::debug('Nextcloud', 'OIDC is off'); - } -*/ - $aResult['DevEmail'] = $sEmail ?: ''; - } else if (!empty($aResult['ContactsSync'])) { - $bSave = false; - if (empty($aResult['ContactsSync']['Url'])) { - $aResult['ContactsSync']['Url'] = "{$sWebDAV}/addressbooks/users/{$sUID}/contacts/"; + if ($bAdmin || !\is_array($aResult)) { + return; + } + + $ocUser = \OC::$server->getUserSession()->getUser(); + if (!$ocUser) { + return; + } + + $sUID = $ocUser->getUID(); + $oUrlGen = \OC::$server->getURLGenerator(); + $sWebDAV = $oUrlGen->getAbsoluteURL($oUrlGen->linkTo('', 'remote.php') . '/dav'); + + $aResult['Nextcloud'] = [ + 'UID' => $sUID, + 'WebDAV' => $sWebDAV, + 'CalDAV' => $this->Config()->Get('plugin', 'calendar', false) + ]; + + if (empty($aResult['Auth'])) { + $config = \OC::$server->getConfig(); + $sEmail = ''; + + if ($config->getAppValue('snappymail', 'snappymail-autologin', false)) { + $sEmail = $sUID; + } else if ($config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { + $sEmail = $config->getUserValue($sUID, 'settings', 'email', ''); + } else { + \SnappyMail\Log::debug('Nextcloud', 'snappymail-autologin is off'); + } + + // User-set SnappyMail credentials override + $sCustomEmail = $config->getUserValue($sUID, 'snappymail', 'snappymail-email', ''); + if ($sCustomEmail) { + $sEmail = $sCustomEmail; + } + + if (!$sEmail) { + $sEmail = $ocUser->getEMailAddress(); + } + + $aResult['DevEmail'] = $sEmail ?: ''; + } else if (!empty($aResult['ContactsSync'])) { + $bSave = false; + + if (empty($aResult['ContactsSync']['Url'])) { + $aResult['ContactsSync']['Url'] = "{$sWebDAV}/addressbooks/users/{$sUID}/contacts/"; + $bSave = true; + } + if (empty($aResult['ContactsSync']['User'])) { + $aResult['ContactsSync']['User'] = $sUID; + $bSave = true; + } + + // FIX: do not array-access the session; use ->get() + $pass = (string) \OC::$server->getSession()->get('snappymail-passphrase'); + if ($pass) { + $pass = \SnappyMail\Crypt::DecryptUrlSafe($pass, $sUID); + if ($pass) { + $aResult['ContactsSync']['Password'] = $pass; $bSave = true; } - if (empty($aResult['ContactsSync']['User'])) { - $aResult['ContactsSync']['User'] = $sUID; - $bSave = true; - } - $pass = \OC::$server->getSession()['snappymail-passphrase']; - if ($pass/* && empty($aResult['ContactsSync']['Password'])*/) { - $pass = \SnappyMail\Crypt::DecryptUrlSafe($pass, $sUID); - if ($pass) { - $aResult['ContactsSync']['Password'] = $pass; - $bSave = true; - } - } - if ($bSave) { - $oActions = \RainLoop\Api::Actions(); - $oActions->setContactsSyncData( - $oActions->getAccountFromToken(), - array( - 'Mode' => $aResult['ContactsSync']['Mode'], - 'User' => $aResult['ContactsSync']['User'], - 'Password' => $aResult['ContactsSync']['Password'], - 'Url' => $aResult['ContactsSync']['Url'] - ) - ); - } + } + + if ($bSave) { + $oActions = \RainLoop\Api::Actions(); + $oActions->setContactsSyncData( + $oActions->getAccountFromToken(), + [ + 'Mode' => $aResult['ContactsSync']['Mode'], + 'User' => $aResult['ContactsSync']['User'], + 'Password' => $aResult['ContactsSync']['Password'], + 'Url' => $aResult['ContactsSync']['Url'] + ] + ); } } } @@ -301,116 +387,124 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin $userLang = \OC::$server->getConfig()->getUserValue($userId, 'core', 'lang', 'en'); $userLang = \strtr($userLang, '_', '-'); $sLanguage = $this->determineLocale($userLang, $aResultLang); - // Check if $sLanguage is null if (!$sLanguage) { - $sLanguage = 'en'; // Assign 'en' if $sLanguage is null + $sLanguage = 'en'; } } } - /** - * Determine locale from user language. - * - * @param string $langCode The name of the input. - * @param array $languagesArray The value of the array. - * - * @return string return locale - */ private function determineLocale(string $langCode, array $languagesArray) : ?string { - // Direct check for the language code - if (\in_array($langCode, $languagesArray)) { + if (\in_array($langCode, $languagesArray, true)) { return $langCode; } - // Check without country code if (\str_contains($langCode, '-')) { $langCode = \explode('-', $langCode)[0]; - if (\in_array($langCode, $languagesArray)) { + if (\in_array($langCode, $languagesArray, true)) { return $langCode; } } - // Check with uppercase country code $langCodeWithUpperCase = $langCode . '-' . \strtoupper($langCode); - if (\in_array($langCodeWithUpperCase, $languagesArray)) { + if (\in_array($langCodeWithUpperCase, $languagesArray, true)) { return $langCodeWithUpperCase; } - // If no match is found return null; } - /** - * @param mixed $mResult - */ public function MainFabrica(string $sName, &$mResult) { - if (static::isLoggedIn()) { - if ('suggestions' === $sName && $this->Config()->Get('plugin', 'suggestions', true)) { - if (!\is_array($mResult)) { - $mResult = array(); - } - include_once __DIR__ . '/NextcloudContactsSuggestions.php'; - $mResult[] = new NextcloudContactsSuggestions( - $this->Config()->Get('plugin', 'ignoreSystemAddressbook', true) - ); - } -/* - if ($this->Config()->Get('plugin', 'storage', false) && ('storage' === $sName || 'storage-local' === $sName)) { - require_once __DIR__ . '/storage.php'; - $oDriver = new \NextcloudStorage(APP_PRIVATE_DATA.'storage', $sName === 'storage-local'); - } -*/ + if (!static::isLoggedIn()) { + return; } + + if ('suggestions' === $sName && $this->Config()->Get('plugin', 'suggestions', true)) { + if (!\is_array($mResult)) { + $mResult = []; + } + include_once __DIR__ . '/NextcloudContactsSuggestions.php'; + $mResult[] = new NextcloudContactsSuggestions( + $this->Config()->Get('plugin', 'ignoreSystemAddressbook', true) + ); + } + + // storage hook left as upstream (commented) } protected function configMapping() : array { - return array( + return [ \RainLoop\Plugins\Property::NewInstance('suggestions')->SetLabel('Suggestions') ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) ->SetDefaultValue(true), + \RainLoop\Plugins\Property::NewInstance('ignoreSystemAddressbook')->SetLabel('Ignore system addressbook') ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) ->SetDefaultValue(true), -/* - \RainLoop\Plugins\Property::NewInstance('storage')->SetLabel('Use Nextcloud user ID in config storage path') - ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) - ->SetDefaultValue(false) -*/ + \RainLoop\Plugins\Property::NewInstance('calendar')->SetLabel('Enable "Put ICS in calendar"') ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) ->SetDefaultValue(false) - ); + ]; } - private static function SmartFileExists(string $sFilePath, $oFiles) : string + /** + * Ensure a (possibly nested) folder path exists under the given base folder. + */ + private function ensureFolderPath(\OCP\Files\Folder $baseFolder, string $path) : \OCP\Files\Folder { - $sFilePath = \str_replace('\\', '/', \trim($sFilePath)); - - if (!$oFiles->file_exists($sFilePath)) { - return $sFilePath; + $path = \trim($path, '/'); + if ($path === '') { + return $baseFolder; } - $aFileInfo = \pathinfo($sFilePath); - - $iIndex = 0; - - while (true) { - ++$iIndex; - $sFilePathNew = $aFileInfo['dirname'].'/'. - \preg_replace('/\(\d{1,2}\)$/', '', $aFileInfo['filename']). - ' ('.$iIndex.')'. - (empty($aFileInfo['extension']) ? '' : '.'.$aFileInfo['extension']) - ; - if (!$oFiles->file_exists($sFilePathNew)) { - return $sFilePathNew; - } - if (10 < $iIndex) { - break; + $folderNode = $baseFolder; + foreach (\array_filter(\explode('/', $path), 'strlen') as $part) { + $part = (string) $part; + if ($folderNode->nodeExists($part)) { + $node = $folderNode->get($part); + if ($node instanceof \OCP\Files\Folder) { + $folderNode = $node; + } else { + // name collision: file exists where folder needed; create a suffixed folder + $folderNode = $folderNode->newFolder($part . '-folder'); + } + } else { + $folderNode = $folderNode->newFolder($part); } } - return $sFilePath; + + return $folderNode; + } + + /** + * Return a unique filename inside a Nextcloud folder (keeps original extension). + */ + private function uniqueNameInFolder(\OCP\Files\Folder $folderNode, string $fileName) : string + { + $fileName = \trim($fileName); + if ($fileName === '') { + $fileName = 'file.dat'; + } + + if (!$folderNode->nodeExists($fileName)) { + return $fileName; + } + + $info = \pathinfo($fileName); + $base = $info['filename'] ?? 'file'; + $ext = isset($info['extension']) && $info['extension'] !== '' ? ('.' . $info['extension']) : ''; + + for ($i = 1; $i <= 50; $i++) { + $try = $base . ' (' . $i . ')' . $ext; + if (!$folderNode->nodeExists($try)) { + return $try; + } + } + + // fallback + return $fileName; } } From 901d0efd6f9ff85f94168e64a10f84725138d1e4 Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Thu, 19 Feb 2026 17:43:35 +0100 Subject: [PATCH 12/17] Update docker-pr.yml correct repo name for uploads --- .github/workflows/docker-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml index 0ba375b5c..55a777acd 100644 --- a/.github/workflows/docker-pr.yml +++ b/.github/workflows/docker-pr.yml @@ -21,7 +21,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - nextgen-networks/snappymail + nextgennetworks/snappymail ghcr.io/${{ github.repository }} # type=ref,event=pr generates tag(s) on PRs only. E.g. 'pr-123', 'pr-123-abc0123' tags: | From f748a686c95c2aed3566c50ba92817215f4db692 Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Thu, 19 Feb 2026 19:06:14 +0100 Subject: [PATCH 13/17] Update docker.yml fix wrong docker hub repo name --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4115ffce9..28fb6f31e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -26,7 +26,7 @@ jobs: DOCKER_METADATA_PR_HEAD_SHA: 'true' with: images: | - nextgen-networks/snappymail + nextgennetworks/snappymail ghcr.io/${{ github.repository }} # type=ref,event=branch generates tag(s) on branch only. E.g. 'master', 'master-abc0123' # type=ref,event=tag generates tag(s) on tags only. E.g. 'v0.0.0', 'v0.0.0-abc0123', and 'latest' From a88d24c38a243471a57530933103a8920acd090e Mon Sep 17 00:00:00 2001 From: Nextgen-Networks Date: Fri, 20 Feb 2026 11:30:14 +0100 Subject: [PATCH 14/17] Update release.php change repo --- cli/release.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/release.php b/cli/release.php index 847dd14e2..2e4e12c46 100755 --- a/cli/release.php +++ b/cli/release.php @@ -207,7 +207,7 @@ if ($options['aur']) { pkgdesc = modern PHP webmail client pkgver = '.$package->version.' pkgrel = 1 - url = https://github.com/the-djmaze/snappymail + url = https://github.com/nextgen-networks/snappymail arch = any license = AGPL3 makedepends = php @@ -218,7 +218,7 @@ if ($options['aur']) { optdepends = mariadb: storage backend for contacts optdepends = php-pgsql: storage backend for contacts optdepends = php-sqlite: storage backend for contacts - source = snappymail-'.$package->version.'.tar.gz::https://github.com/the-djmaze/snappymail/archive/v'.$package->version.'.tar.gz + source = snappymail-'.$package->version.'.tar.gz::https://github.com/nextgen-networks/snappymail/archive/v'.$package->version.'.tar.gz source = snappymail.sysusers source = snappymail.tmpfiles b2sums = '.implode("\n b2sums = ", $b2sums).' @@ -269,7 +269,7 @@ if (isset($options['sign'])) { if (isset($options['debian'])) { passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/" . basename(DEB_DEST_DIR.'.deb')), $return_var); - // https://github.com/the-djmaze/snappymail/issues/185#issuecomment-1059420588 + // https://github.com/nextgen-networks/snappymail/issues/185#issuecomment-1059420588 passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --digest-algo SHA512 --clearsign --output ' . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/InRelease") . ' ' . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/Release"), $return_var); From 0b86edb1679eea0ccedac0ba619fd3a5a05346ff Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:18:25 +0000 Subject: [PATCH 15/17] Update SnappyMail repository references to nextgen-networks Replaced references to https://github.com/the-djmaze/snappymail with https://github.com/nextgen-networks/snappymail/ across the repository, excluding README files as requested. The replacement handles trailing slashes carefully to ensure bare URLs include the slash while sub-paths (like /issues/ or .git) do not result in double slashes. Files that already used the new domain were also updated to ensure consistent trailing slash usage. Co-authored-by: nextgen-networks <21206978+nextgen-networks@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- CHANGELOG.md | 1124 ++++++++--------- CONTRIBUTING.md | 2 +- build/arch/PKGBUILD | 2 +- build/deb.php | 2 +- build/deb/DEBIAN/control | 4 +- cli/release.php | 2 +- dev/Common/Html.js | 16 +- dev/Common/UtilsUser.js | 4 +- dev/Component/EmailAddresses.js | 2 +- dev/External/SquireUI.js | 6 +- dev/Model/FolderCollection.js | 4 +- dev/Remote/AbstractFetch.js | 2 +- dev/Settings/User/Security.js | 2 +- dev/Stores/Theme.js | 2 +- dev/Stores/User/Messagelist.js | 2 +- dev/Styles/Main.less | 2 +- dev/Styles/User/FolderList.less | 2 +- dev/View/Popup/Compose.js | 2 +- dev/View/Popup/Domain.js | 2 +- examples/eml/tnef.eml | 2 +- integrations/cloudron/Dockerfile | 2 +- integrations/nextcloud/snappymail/INSTALL | 2 +- .../nextcloud/snappymail/appinfo/info.xml | 16 +- .../nextcloud/snappymail/css/embed.css | 4 +- .../snappymail/lib/Migration/InstallStep.php | 4 +- .../snappymail/lib/Util/SnappyMailHelper.php | 4 +- integrations/owncloud/snappymail/CHANGELOG.md | 296 ++--- .../owncloud/snappymail/appinfo/info.xml | 12 +- .../owncloud/snappymail/css/embed.css | 4 +- .../snappymail/lib/Util/SnappyMailHelper.php | 4 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 4 +- plugins/attachments-force-open/index.php | 2 +- plugins/auto-domain-grab/index.php | 2 +- plugins/avatars/index.php | 2 +- plugins/compact-composer/index.php | 2 +- .../compact-composer/js/CompactComposer.js | 6 +- plugins/demo-account/index.php | 2 +- .../ldap-mail-accounts/LdapMailAccounts.php | 4 +- plugins/ldap-mail-accounts/index.php | 2 +- plugins/login-cpanel/index.php | 2 +- plugins/login-gmail/index.php | 2 +- plugins/login-o365/index.php | 2 +- plugins/login-remote/index.php | 2 +- plugins/login-virtuser/index.php | 2 +- plugins/nextcloud/js/composer.js | 2 +- plugins/nextcloud/js/message.js | 2 +- plugins/nextcloud/js/webdav.js | 4 +- plugins/search-filters/index.php | 2 +- .../two-factor-auth/js/TwoFactorAuthLogin.js | 2 +- .../libraries/MailSo/Base/DateTimeHelper.php | 4 +- .../libraries/MailSo/Imap/BodyStructure.php | 2 +- .../MailSo/Imap/Commands/Folders.php | 2 +- .../libraries/MailSo/Imap/SearchCriterias.php | 4 +- .../MailSo/Imap/Traits/ResponseParser.php | 4 +- .../libraries/MailSo/Imap/Traits/Status.php | 4 +- .../app/libraries/MailSo/Mail/Message.php | 2 +- .../libraries/MailSo/Net/ConnectSettings.php | 2 +- .../libraries/MailSo/Sieve/SieveClient.php | 2 +- .../app/libraries/MailSo/Smtp/Settings.php | 2 +- .../app/libraries/MailSo/Smtp/SmtpClient.php | 2 +- .../libraries/RainLoop/Actions/Accounts.php | 2 +- .../libraries/RainLoop/Actions/Folders.php | 4 +- .../libraries/RainLoop/Actions/Messages.php | 6 +- .../app/libraries/RainLoop/Actions/Raw.php | 4 +- .../libraries/RainLoop/Actions/UserAuth.php | 2 +- .../0.0.0/app/libraries/RainLoop/Pdo/Base.php | 2 +- .../0.0.0/app/libraries/RainLoop/Service.php | 6 +- .../v/0.0.0/app/libraries/RainLoop/Utils.php | 2 +- .../app/libraries/snappymail/cookies.php | 2 +- .../app/libraries/snappymail/gpg/base.php | 4 +- .../app/libraries/snappymail/gpg/pgp.php | 6 +- .../app/libraries/snappymail/gpg/smime.php | 6 +- .../libraries/snappymail/image/imagick.php | 2 +- .../app/libraries/snappymail/integrity.php | 4 +- .../app/libraries/snappymail/sasl/login.php | 2 +- .../app/libraries/snappymail/sasl/scram.php | 2 +- .../Views/Admin/AdminSettingsAbout.html | 6 +- .../Views/Admin/AdminSettingsGeneral.html | 4 +- .../templates/Views/Admin/PopupsDomain.html | 2 +- snappymail/v/0.0.0/cpanel.php | 2 +- snappymail/v/0.0.0/include.php | 2 +- 83 files changed, 844 insertions(+), 844 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 998692964..9c16abc7e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -31,7 +31,7 @@ If applicable, add screenshots to help explain your problem. - Mode: [e.g. standalone, nextcloud, cyberpanel, docker] **Debug/logging information** -[Read here how to log](https://github.com/the-djmaze/snappymail/wiki/FAQ#how-do-i-enable-logging) +[Read here how to log](https://github.com/nextgen-networks/snappymail/wiki/FAQ#how-do-i-enable-logging) - [ ] I've placed them here (few lines) or as attachments (many lines) **Additional context** diff --git a/CHANGELOG.md b/CHANGELOG.md index d31a1b771..6d11b12ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## 2.38.2 – 2024-10-09 ### Fixed - error '$index is not defined' in Settings > Accounts - [#1797](https://github.com/the-djmaze/snappymail/issues/1797) + [#1797](https://github.com/nextgen-networks/snappymail/issues/1797) ## 2.38.1 – 2024-10-08 @@ -9,15 +9,15 @@ ### Added - Admin - Extensions search filter - Options to unset current font family and size - [#1726](https://github.com/the-djmaze/snappymail/issues/1726) + [#1726](https://github.com/nextgen-networks/snappymail/issues/1726) - Option to allow insecure cryptkey - [#1746](https://github.com/the-djmaze/snappymail/issues/1746) + [#1746](https://github.com/nextgen-networks/snappymail/issues/1746) - Save vCard FN property - [#1761](https://github.com/the-djmaze/snappymail/issues/1761) + [#1761](https://github.com/nextgen-networks/snappymail/issues/1761) - Docker Nginx should listen on IPv6 addresses in addition to IPv4 - [#1770](https://github.com/the-djmaze/snappymail/issues/1770) + [#1770](https://github.com/nextgen-networks/snappymail/issues/1770) - Full-screen Message View on Double-Click - [#1787](https://github.com/the-djmaze/snappymail/issues/1787) + [#1787](https://github.com/nextgen-networks/snappymail/issues/1787) ### Changed - Use the custom Squire as submodule vendors/squire2 @@ -25,11 +25,11 @@ - Keep 1 space between HTML elements in compressed templates - Squire: sanitizeToDOMFragment now uses cleanHTML() - Squire: improved handling of BR elements - [#1389](https://github.com/the-djmaze/snappymail/issues/1389) + [#1389](https://github.com/nextgen-networks/snappymail/issues/1389) - Nextcloud border-radius by @gnilebein - [#1790](https://github.com/the-djmaze/snappymail/pull/1790) + [#1790](https://github.com/nextgen-networks/snappymail/pull/1790) - Compose window sentFolder handling for - [#1793](https://github.com/the-djmaze/snappymail/pull/1793) + [#1793](https://github.com/nextgen-networks/snappymail/pull/1793) - Update Portuguese by @ner00 - Update Polish by @tinola - Update French by @hguilbert @@ -40,18 +40,18 @@ - Composer dialog "from" triangle button wrong position due to font changes - Admin - Config `search` should be ko.observable() not ko.observableArray() - Squire: paste images - [#1389](https://github.com/the-djmaze/snappymail/issues/1389) + [#1389](https://github.com/nextgen-networks/snappymail/issues/1389) - Domain whitelist failures on login - [#1706](https://github.com/the-djmaze/snappymail/issues/1706) + [#1706](https://github.com/nextgen-networks/snappymail/issues/1706) - Sieve parse errors - Sieve support `index` was not optional for - [#1709](https://github.com/the-djmaze/snappymail/issues/1709) + [#1709](https://github.com/nextgen-networks/snappymail/issues/1709) - Pagination problem for large mailbox after 2.36.1 - [#1716](https://github.com/the-djmaze/snappymail/issues/1716) + [#1716](https://github.com/nextgen-networks/snappymail/issues/1716) - Undefined constant "LOG_ERROR" - [#1754](https://github.com/the-djmaze/snappymail/issues/1754) + [#1754](https://github.com/nextgen-networks/snappymail/issues/1754) - Search Filter Capital "B" not working - [#1780](https://github.com/the-djmaze/snappymail/issues/1780) + [#1780](https://github.com/nextgen-networks/snappymail/issues/1780) - PHP 8.4: Implicitly nullable parameter declarations deprecated @@ -59,11 +59,11 @@ ### Added - Remove `tel:` links when converting HTML to plain - [#1724](https://github.com/the-djmaze/snappymail/issues/1724) + [#1724](https://github.com/nextgen-networks/snappymail/issues/1724) - Convert HTML to Markdown instead of plain, by using [Turndown](https://github.com/mixmark-io/turndown) - [#1604](https://github.com/the-djmaze/snappymail/issues/1604) + [#1604](https://github.com/nextgen-networks/snappymail/issues/1604) - Check HTMLInputElement.validity() for - [#1733](https://github.com/the-djmaze/snappymail/issues/1733) + [#1733](https://github.com/nextgen-networks/snappymail/issues/1733) ### Changed - Use a modified [Squire 2.3.2](https://github.com/the-djmaze/Squire/commits/snappymail/) @@ -72,13 +72,13 @@ ### Fixed - mXSS exploit found by SonarSource - [CVE-2024-45800](https://github.com/the-djmaze/snappymail/security/advisories/GHSA-2rq7-79vp-ffxm) + [CVE-2024-45800](https://github.com/nextgen-networks/snappymail/security/advisories/GHSA-2rq7-79vp-ffxm) - Call to a member function Email() on null - [#1706](https://github.com/the-djmaze/snappymail/issues/1706) + [#1706](https://github.com/nextgen-networks/snappymail/issues/1706) - IMAP capabilities via IMAP Proxy - [#1725](https://github.com/the-djmaze/snappymail/issues/1725) + [#1725](https://github.com/nextgen-networks/snappymail/issues/1725) - Messages on page setting is not validated against 999 max - [#1733](https://github.com/the-djmaze/snappymail/issues/1733) + [#1733](https://github.com/nextgen-networks/snappymail/issues/1733) ## 2.37.3 – 2024-08-27 @@ -87,38 +87,38 @@ - Mark images with width=1 as tracking pixel - Show warning in Admin -> About when PHP runs in 32bit - Edit ACL rules - [#157](https://github.com/the-djmaze/snappymail/issues/157) + [#157](https://github.com/nextgen-networks/snappymail/issues/157) - Show GnuPG version for - [#1560](https://github.com/the-djmaze/snappymail/issues/1560) + [#1560](https://github.com/nextgen-networks/snappymail/issues/1560) - Make sure only scalar values are allowed in $_ENV for - [#1560](https://github.com/the-djmaze/snappymail/issues/1560) + [#1560](https://github.com/nextgen-networks/snappymail/issues/1560) - Change minimum new mail check interval - [#1678](https://github.com/the-djmaze/snappymail/issues/1678) + [#1678](https://github.com/nextgen-networks/snappymail/issues/1678) - Sieve editor does not support "index" extension - [#1709](https://github.com/the-djmaze/snappymail/issues/1709) + [#1709](https://github.com/nextgen-networks/snappymail/issues/1709) ### Changed - Improved domain autoconfig interaction - MS autodiscover priorities DNS over subdomain - Simplify sieve scripts list - [#1675](https://github.com/the-djmaze/snappymail/issues/1675) + [#1675](https://github.com/nextgen-networks/snappymail/issues/1675) - Handling of (token) errors due to - [#1706](https://github.com/the-djmaze/snappymail/issues/1706) + [#1706](https://github.com/nextgen-networks/snappymail/issues/1706) - Sabre/Xml to v4.0.5 - Update Chinese by @Artoria2e5 - Update French by @hguilbert ### Fixed - Thread sorting visible after disabling the imap capability - [#1574](https://github.com/the-djmaze/snappymail/issues/1574) + [#1574](https://github.com/nextgen-networks/snappymail/issues/1574) - Creating new message impossible as long as a draft exists? - [#1710](https://github.com/the-djmaze/snappymail/issues/1710) + [#1710](https://github.com/nextgen-networks/snappymail/issues/1710) - InvalidToken error at login - [#1706](https://github.com/the-djmaze/snappymail/issues/1706) + [#1706](https://github.com/nextgen-networks/snappymail/issues/1706) ### Nextcloud - Force Nextcloud personal language by default - [#1428](https://github.com/the-djmaze/snappymail/issues/1428) + [#1428](https://github.com/nextgen-networks/snappymail/issues/1428) ## 2.37.2 – 2024-08-13 @@ -132,7 +132,7 @@ ### Nextcloud - Failed loading due to Office365 OAuth2 attempt - [#1703](https://github.com/the-djmaze/snappymail/issues/1703) + [#1703](https://github.com/nextgen-networks/snappymail/issues/1703) ## 2.37.1 – 2024-08-12 @@ -149,15 +149,15 @@ ### Added - JavaScript event `rl-vm-visible` - Detailed error message on account switch failure for - [#1594](https://github.com/the-djmaze/snappymail/issues/1594) + [#1594](https://github.com/nextgen-networks/snappymail/issues/1594) - Workarounds for Microsoft OAuth2 (currently requires Apache AcceptPathInfo) - [#1645](https://github.com/the-djmaze/snappymail/issues/1645) + [#1645](https://github.com/nextgen-networks/snappymail/issues/1645) - Support "mark for deletion" - [#1657](https://github.com/the-djmaze/snappymail/issues/1657) by @smsoft-ru + [#1657](https://github.com/nextgen-networks/snappymail/issues/1657) by @smsoft-ru - Invoke "Update Identity" pop up right after login (when not initialized) - [#1689](https://github.com/the-djmaze/snappymail/issues/1689) + [#1689](https://github.com/nextgen-networks/snappymail/issues/1689) - Keyboard shortcut for "Swap default (background) color" - [#1690](https://github.com/the-djmaze/snappymail/issues/1690) + [#1690](https://github.com/nextgen-networks/snappymail/issues/1690) ### Changed - Updated gulp to v5 @@ -166,7 +166,7 @@ - Align save button in admin security settings - Made registerProtocolHandler('mailto') optional by activating at Settings -> General - Improved InvalidToken handling for - [#1653](https://github.com/the-djmaze/snappymail/issues/1653) + [#1653](https://github.com/nextgen-networks/snappymail/issues/1653) - Cleanup localizations - Update French by @hguilbert - Update German by @tkasch @@ -177,41 +177,41 @@ - prevent multiple afterShow() and afterHide() due to `transitionend` on multiple CSS properties - Attempt to read property "smimeSigned" on null - Refreshing mail list doesn't update current message - [#1654](https://github.com/the-djmaze/snappymail/issues/1654) + [#1654](https://github.com/nextgen-networks/snappymail/issues/1654) - Deletion of CACHE folder causing error - [#1660](https://github.com/the-djmaze/snappymail/issues/1660) + [#1660](https://github.com/nextgen-networks/snappymail/issues/1660) - Multiple line breaks are not displayed - [#1666](https://github.com/the-djmaze/snappymail/issues/1666) + [#1666](https://github.com/nextgen-networks/snappymail/issues/1666) - RainLoop\Exceptions\ClientException::__construct(): Argument #2 ($oPrevious) must be of type ?Throwable, string given - [#1686](https://github.com/the-djmaze/snappymail/issues/1686) + [#1686](https://github.com/nextgen-networks/snappymail/issues/1686) - SpamAssassin Division by zero - [#1694](https://github.com/the-djmaze/snappymail/issues/1694) + [#1694](https://github.com/nextgen-networks/snappymail/issues/1694) - Failed to parse RFC 2822 date '6 Jul 2024 16:42:09 +0200' - [#1694](https://github.com/the-djmaze/snappymail/issues/1694) + [#1694](https://github.com/nextgen-networks/snappymail/issues/1694) - Fix capabilities when THREAD is disabled - [#1698](https://github.com/the-djmaze/snappymail/pull/1698) by @akhil1508 + [#1698](https://github.com/nextgen-networks/snappymail/pull/1698) by @akhil1508 ### Nextcloud - Failed loading due to incorrect `app_path` - Bugfix language detection - Allow multi-account in nc with oauth login - [#1699](https://github.com/the-djmaze/snappymail/pull/1699) by @akhil1508 + [#1699](https://github.com/nextgen-networks/snappymail/pull/1699) by @akhil1508 ## 2.36.4 – 2024-06-25 ### Added - Customize private key passphrase expiration interval - [#1545](https://github.com/the-djmaze/snappymail/discussions/1545) + [#1545](https://github.com/nextgen-networks/snappymail/discussions/1545) - AdvancedSearch support for filtering mails before a given date - [#1606](https://github.com/the-djmaze/snappymail/pull/1606) by @codiflow + [#1606](https://github.com/nextgen-networks/snappymail/pull/1606) by @codiflow - Control valid spam and virus headers - [#1607](https://github.com/the-djmaze/snappymail/issues/1607) + [#1607](https://github.com/nextgen-networks/snappymail/issues/1607) - Remember S/MIME private Key without function - [#1611](https://github.com/the-djmaze/snappymail/issues/1611) + [#1611](https://github.com/nextgen-networks/snappymail/issues/1611) - Resize compose dialog - Magnetic theme - [#1637](https://github.com/the-djmaze/snappymail/pull/1637) by @TheCuteFoxxy + [#1637](https://github.com/nextgen-networks/snappymail/pull/1637) by @TheCuteFoxxy ### Changed - Improved signing messages by allowing to choose between the options @@ -226,17 +226,17 @@ - Default language error - Undefined $sEmail in DoAdminDomainMatch - Handling Autocrypt header failed on `=` - [#1608](https://github.com/the-djmaze/snappymail/issues/1608) + [#1608](https://github.com/nextgen-networks/snappymail/issues/1608) - Blank lines are inserted when editing draft - [#1609](https://github.com/the-djmaze/snappymail/issues/1609) + [#1609](https://github.com/nextgen-networks/snappymail/issues/1609) - Workaround Cyrus MAILBOXID bug (disable OBJECTID capability by default due to impact) - [#1640](https://github.com/the-djmaze/snappymail/issues/1640) + [#1640](https://github.com/nextgen-networks/snappymail/issues/1640) - Workaround HTML with multiple body elements or MIME with multiple text/html - [#1641](https://github.com/the-djmaze/snappymail/issues/1641) + [#1641](https://github.com/nextgen-networks/snappymail/issues/1641) ### Nextcloud - OIDC stay logged in - [#1620](https://github.com/the-djmaze/snappymail/pull/1620) by @avinash-0007 + [#1620](https://github.com/nextgen-networks/snappymail/pull/1620) by @avinash-0007 ## 2.36.3 – 2024-05-27 @@ -246,16 +246,16 @@ ### Fixed - Undefined variable $aTokenData - [#1567](https://github.com/the-djmaze/snappymail/issues/1567) + [#1567](https://github.com/nextgen-networks/snappymail/issues/1567) ## 2.36.2 – 2024-05-26 ### Added - "copy to" action in menu's for - [#1559](https://github.com/the-djmaze/snappymail/issues/1559) + [#1559](https://github.com/nextgen-networks/snappymail/issues/1559) - Log signal info for - [#1569](https://github.com/the-djmaze/snappymail/issues/1569) + [#1569](https://github.com/nextgen-networks/snappymail/issues/1569) - OpenPGP.js automatically import backup keys from server ### Changed @@ -265,23 +265,23 @@ ### Fixed - "Account already exists" - [#1561](https://github.com/the-djmaze/snappymail/issues/1561) + [#1561](https://github.com/nextgen-networks/snappymail/issues/1561) - Properly escape path separator in tar.php file list regex - [#1562](https://github.com/the-djmaze/snappymail/pull/1562) by @sevmonster + [#1562](https://github.com/nextgen-networks/snappymail/pull/1562) by @sevmonster - Prevent mkdir() error - [#1565](https://github.com/the-djmaze/snappymail/issues/1565) + [#1565](https://github.com/nextgen-networks/snappymail/issues/1565) - SCRAM Exception when trying to log in to SMTP - [#1575](https://github.com/the-djmaze/snappymail/issues/1575) + [#1575](https://github.com/nextgen-networks/snappymail/issues/1575) - Error when redirected back to instance after Gmail OAuth - [#1580](https://github.com/the-djmaze/snappymail/issues/1580) + [#1580](https://github.com/nextgen-networks/snappymail/issues/1580) - Uncaught TypeError: hasPublicKeyForEmails(...).then is not a function - [#1589](https://github.com/the-djmaze/snappymail/issues/1589) + [#1589](https://github.com/nextgen-networks/snappymail/issues/1589) - Undefined variable $sFilename - GPG/PGP exec() return false handling ### Nextcloud - OIDC login active again - [#1572](https://github.com/the-djmaze/snappymail/pull/1572) by @avinash-0007 + [#1572](https://github.com/nextgen-networks/snappymail/pull/1572) by @avinash-0007 ## 2.36.1 – 2024-04-23 @@ -292,7 +292,7 @@ - Handle RFC 5987 in Content-Disposition header - Ignore text/x-amp-html - Show SMTP error to user - [#1521](https://github.com/the-djmaze/snappymail/issues/1521) + [#1521](https://github.com/nextgen-networks/snappymail/issues/1521) - OAuth2 for login using gmail (and others) ### Changed @@ -315,19 +315,19 @@ - Content encoding and type detection in JavaScript could fail due to case-sensitivity. - Extensions set logger failed - GnuPG check open_basedir and if shell_exec is disabled - [#1385](https://github.com/the-djmaze/snappymail/issues/1385) - [#1496](https://github.com/the-djmaze/snappymail/issues/1496) - [#1555](https://github.com/the-djmaze/snappymail/issues/1555) + [#1385](https://github.com/nextgen-networks/snappymail/issues/1385) + [#1496](https://github.com/nextgen-networks/snappymail/issues/1496) + [#1555](https://github.com/nextgen-networks/snappymail/issues/1555) - Hide pagination when search result has no messages - Prevent mbstring error before setup.php - Prevent MessagesPerPage Infinity - [#1540](https://github.com/the-djmaze/snappymail/issues/1540) + [#1540](https://github.com/nextgen-networks/snappymail/issues/1540) - Reseal CryptKey failed - [#1543](https://github.com/the-djmaze/snappymail/issues/1543) + [#1543](https://github.com/nextgen-networks/snappymail/issues/1543) ### Nextcloud - Add an occ command to set up the login settings - [#1552](https://github.com/the-djmaze/snappymail/issues/1552) + [#1552](https://github.com/nextgen-networks/snappymail/issues/1552) ## 2.36.0 – 2024-03-18 @@ -336,24 +336,24 @@ - Allow setting the supported THREAD algorithm - Icon to system folders - Remove remembered password after 15 minutes of inactivity - [#1142](https://github.com/the-djmaze/snappymail/issues/1142) + [#1142](https://github.com/nextgen-networks/snappymail/issues/1142) - Swap background and text color for unreadable text on dark background - [#1486](https://github.com/the-djmaze/snappymail/issues/1486) + [#1486](https://github.com/nextgen-networks/snappymail/issues/1486) - Generate TOTP code at ?Admin -> Security - [#1501](https://github.com/the-djmaze/snappymail/issues/1501) + [#1501](https://github.com/nextgen-networks/snappymail/issues/1501) - Button to change S/MIME private key passphrase - [#1505](https://github.com/the-djmaze/snappymail/issues/1505) + [#1505](https://github.com/nextgen-networks/snappymail/issues/1505) - Belarusian - [#1512](https://github.com/the-djmaze/snappymail/pull/1512) by @spoooyders + [#1512](https://github.com/nextgen-networks/snappymail/pull/1512) by @spoooyders - Log some domain idn_to_ascii issues - [#1513](https://github.com/the-djmaze/snappymail/issues/1513) + [#1513](https://github.com/nextgen-networks/snappymail/issues/1513) ### Changed - On folder/mailbox rename, also rename all children instead of reloading all - Seal MainAccount CryptKey and on error ask old login passphrase to reseal key. - Moved cache drivers outside core into extensions - Sieve always allow fileinto INBOX - [#1510](https://github.com/the-djmaze/snappymail/issues/1510) + [#1510](https://github.com/nextgen-networks/snappymail/issues/1510) - Moved application.ini `sieve_auth_plain_initial` to per domain config - Languages use rfc5646, by using the shortest ISO 639 code by default - Update French by @hguilbert @@ -364,15 +364,15 @@ - On folder/mailbox rename, the checkable option was not renamed - Sort accounts drag & drop - S/MIME encrypted and opaque signed not visible - [#1450](https://github.com/the-djmaze/snappymail/issues/1450) + [#1450](https://github.com/nextgen-networks/snappymail/issues/1450) - Wrong last UID of thread - [#1507](https://github.com/the-djmaze/snappymail/issues/1507) + [#1507](https://github.com/nextgen-networks/snappymail/issues/1507) - Creation of dynamic property SnappyMail\DAV\Client::$HTTP - [#1509](https://github.com/the-djmaze/snappymail/issues/1509) + [#1509](https://github.com/nextgen-networks/snappymail/issues/1509) - "Download as ZIP" fails for messages - [#1514](https://github.com/the-djmaze/snappymail/issues/1514) + [#1514](https://github.com/nextgen-networks/snappymail/issues/1514) - SMTP "Authentication failed" when IMAP uses `shortLogin` and SMTP not - [#1517](https://github.com/the-djmaze/snappymail/issues/1517) + [#1517](https://github.com/nextgen-networks/snappymail/issues/1517) ## 2.35.4 – 2024-03-16 @@ -388,19 +388,19 @@ ### Fixed - Raise JS TypeEroor "toLowerCase" after update - [#1491](https://github.com/the-djmaze/snappymail/issues/1491) + [#1491](https://github.com/nextgen-networks/snappymail/issues/1491) - Call to undefined function shell_exec - [#1496](https://github.com/the-djmaze/snappymail/issues/1496) + [#1496](https://github.com/nextgen-networks/snappymail/issues/1496) - Download attachments as ZIP doesn't work for PGP encrypted mail - [#1499](https://github.com/the-djmaze/snappymail/issues/1499) + [#1499](https://github.com/nextgen-networks/snappymail/issues/1499) - Importing or downloading a PGP public key attachment from a PGP encrypted message doesn't work - [#1500](https://github.com/the-djmaze/snappymail/issues/1500) + [#1500](https://github.com/nextgen-networks/snappymail/issues/1500) - VCard PHP Notice: Undefined index: ENCODING ### Nextcloud - Changed stored password handling - Can't login from nextcloud with 2.35.3 bug Nextcloud - [#1490](https://github.com/the-djmaze/snappymail/issues/1490) + [#1490](https://github.com/nextgen-networks/snappymail/issues/1490) ## 2.35.3 – 2024-03-12 @@ -408,14 +408,14 @@ ### Added - GnuPG can be disabled - Missing strings for localization inside identity popup (Cryptography > S/MIME) - [#1458](https://github.com/the-djmaze/snappymail/issues/1458) + [#1458](https://github.com/nextgen-networks/snappymail/issues/1458) - Automatically verify PGP and S/MIME signed messages - TNEFDecoder for - [#1012](https://github.com/the-djmaze/snappymail/discussions/1012) + [#1012](https://github.com/nextgen-networks/snappymail/discussions/1012) - RTF to HTML converter for - [#1012](https://github.com/the-djmaze/snappymail/discussions/1012) + [#1012](https://github.com/nextgen-networks/snappymail/discussions/1012) - Polyfill for PHP ctype - [#1250](https://github.com/the-djmaze/snappymail/issues/1250) + [#1250](https://github.com/nextgen-networks/snappymail/issues/1250) ### Changed - `new Error()` to `Error()` @@ -438,20 +438,20 @@ - Handling of Internationalized Domain Names in several areas - Decrypt error message - Stalwart ManageSieve Error 352 when getting Filters - [#1455](https://github.com/the-djmaze/snappymail/issues/1455) + [#1455](https://github.com/nextgen-networks/snappymail/issues/1455) - Nextcloud V25+ theme slightly broken - [#1463](https://github.com/the-djmaze/snappymail/issues/1463) + [#1463](https://github.com/nextgen-networks/snappymail/issues/1463) - PGP decryption fails with "Not armored text" - [#1462](https://github.com/the-djmaze/snappymail/issues/1462) + [#1462](https://github.com/nextgen-networks/snappymail/issues/1462) - AUTH_BASIC falling through as AUTH_BEARER; change AUTH_BEARER to a different value - [#1461](https://github.com/the-djmaze/snappymail/issues/1461) + [#1461](https://github.com/nextgen-networks/snappymail/issues/1461) - SetPassword expects \SnappyMail\SensitiveString - Crash on importing corrupt OpenPGP keys - Crash on old browsers instead of showing error - Ignore popups on logoutReload() - [#1467](https://github.com/the-djmaze/snappymail/issues/1467) + [#1467](https://github.com/nextgen-networks/snappymail/issues/1467) - Custom SASLMechanisms fail in IMAP when the connection is secure - [#1484](https://github.com/the-djmaze/snappymail/pull/1484) by @botsarenthuman + [#1484](https://github.com/nextgen-networks/snappymail/pull/1484) by @botsarenthuman ## 2.35.2 – 2024-02-27 @@ -459,7 +459,7 @@ ### Added - GnuPG error handling - Missing strings for localization inside identity popup (Cryptography > S/MIME) - [#1458](https://github.com/the-djmaze/snappymail/issues/1458) + [#1458](https://github.com/nextgen-networks/snappymail/issues/1458) ### Changed - Update Portuguese by @ner00 @@ -467,11 +467,11 @@ ### Fixed - Drop support for gnupg PECL extension as it fails with "no passphrase" issues - Error 352 when getting Filters - [#1455](https://github.com/the-djmaze/snappymail/issues/1455) + [#1455](https://github.com/nextgen-networks/snappymail/issues/1455) ### Nextcloud - SetPassword(): Argument #1 must be of type SensitiveString, string given - [#1456](https://github.com/the-djmaze/snappymail/issues/1456) + [#1456](https://github.com/nextgen-networks/snappymail/issues/1456) ## 2.35.1 – 2024-02-26 @@ -507,7 +507,7 @@ ### Added - S/MIME support - [#259](https://github.com/the-djmaze/snappymail/issues/259) + [#259](https://github.com/nextgen-networks/snappymail/issues/259) ### Changed - Disable IMAP METADATA by default (hardly used) @@ -522,49 +522,49 @@ - Sign messages using PGP - Check for CONDSTORE or QRESYNC to get the HIGHESTMODSEQ - Unable to login on certain IMAP server since 2.34.2 - [#1438](https://github.com/the-djmaze/snappymail/issues/1438) + [#1438](https://github.com/nextgen-networks/snappymail/issues/1438) ### Nextcloud - Save as .eml - [#1425](https://github.com/the-djmaze/snappymail/issues/1425) + [#1425](https://github.com/nextgen-networks/snappymail/issues/1425) ## 2.34.2 – 2024-02-14 ### Fixed - Message was sent but not saved to sent items folder - [#1432](https://github.com/the-djmaze/snappymail/issues/1432) + [#1432](https://github.com/nextgen-networks/snappymail/issues/1432) - Login with scram failed - [#1433](https://github.com/the-djmaze/snappymail/issues/1433) + [#1433](https://github.com/nextgen-networks/snappymail/issues/1433) ## 2.34.1 – 2024-02-13 ### Added - Autocrypt support - [#342](https://github.com/the-djmaze/snappymail/issues/342) + [#342](https://github.com/nextgen-networks/snappymail/issues/342) - Load the mailboxes/folders of all namespaces (other users, global, shared, etc.) - Load keys from server into OpenPGP.js - [#973](https://github.com/the-djmaze/snappymail/issues/973) + [#973](https://github.com/nextgen-networks/snappymail/issues/973) - Import PGP Keys from remote key servers - Sort Inbox Folders with Unread Messages First - [#1427](https://github.com/the-djmaze/snappymail/issues/1427) + [#1427](https://github.com/nextgen-networks/snappymail/issues/1427) - Define JMAP FolderModel.myRights - Identity Management: add identity display name - [#1405](https://github.com/the-djmaze/snappymail/issues/1405) + [#1405](https://github.com/nextgen-networks/snappymail/issues/1405) - Identity Management: add per-identity "sent" folder - [#1404](https://github.com/the-djmaze/snappymail/issues/1404) + [#1404](https://github.com/nextgen-networks/snappymail/issues/1404) - Some support for JSON-LD / Structured Email - [#1422](https://github.com/the-djmaze/snappymail/issues/1422) + [#1422](https://github.com/nextgen-networks/snappymail/issues/1422) - Domain Autoconfig and Microsoft's autodiscover (and also as extension/plugin) - View MMS messages that are received via email - [#1294](https://github.com/the-djmaze/snappymail/issues/1294) + [#1294](https://github.com/nextgen-networks/snappymail/issues/1294) - Draft code for S/MIME - [#259](https://github.com/the-djmaze/snappymail/issues/259) + [#259](https://github.com/nextgen-networks/snappymail/issues/259) ### Changed - Many OpenPGP improvements - [#89](https://github.com/the-djmaze/snappymail/issues/89) + [#89](https://github.com/nextgen-networks/snappymail/issues/89) - Allow CSP connect-src CORS for keys.openpgp.org to directly fetch PGP keys - Improved handling of visible folders - KnockoutJS Replace some ko.exportSymbol('*') in favour of ko['*'] @@ -572,10 +572,10 @@ - Simplify generating folderListVisible - Drop the bSearchSecretWords param from logger - Transparent background for text - [#1412](https://github.com/the-djmaze/snappymail/issues/1412) + [#1412](https://github.com/nextgen-networks/snappymail/issues/1412) - Enable OpenPGP.js by default at install - Added folder edit popup for improved IMAP ACL Support - [#157](https://github.com/the-djmaze/snappymail/issues/157) + [#157](https://github.com/nextgen-networks/snappymail/issues/157) - Process all IMAP namespaces - Update Polish by @tinola - Update Portuguese by @ner00 @@ -583,20 +583,20 @@ ### Fixed - Make time_zone a select list due to PEBKAC - Workaround Outlook generated double spacing - [#1415](https://github.com/the-djmaze/snappymail/issues/1415) + [#1415](https://github.com/nextgen-networks/snappymail/issues/1415) - HTML Parser is not picking up the full Unsubscribe URL in the attached text file - [#1225](https://github.com/the-djmaze/snappymail/issues/1225) + [#1225](https://github.com/nextgen-networks/snappymail/issues/1225) - Contacts - it auto "Select All", after entry delete - [#1411](https://github.com/the-djmaze/snappymail/issues/1411) + [#1411](https://github.com/nextgen-networks/snappymail/issues/1411) - Message header parsing issue - [#1403](https://github.com/the-djmaze/snappymail/issues/1403) + [#1403](https://github.com/nextgen-networks/snappymail/issues/1403) - apple-touch-icon should not be transparent - [#1408](https://github.com/the-djmaze/snappymail/issues/1408) + [#1408](https://github.com/nextgen-networks/snappymail/issues/1408) - Creation of dynamic property is deprecated - [#1409](https://github.com/the-djmaze/snappymail/issues/1409) + [#1409](https://github.com/nextgen-networks/snappymail/issues/1409) - Ask/send readReceipt was broken - OpenPGP public key can not be removed anymore after importing private key of subkey - [#1384](https://github.com/the-djmaze/snappymail/issues/1384) + [#1384](https://github.com/nextgen-networks/snappymail/issues/1384) - KnockoutJS failed to output text '0' - JavaScript friendlySize() failed on 0 - Workaround Dovecot `PREAUTH [CAPABILITY (null)]` issue @@ -604,9 +604,9 @@ ### Nextcloud - Save multiple as .eml - [#1425](https://github.com/the-djmaze/snappymail/issues/1425) + [#1425](https://github.com/nextgen-networks/snappymail/issues/1425) - Disabled support for Nextcloud OpenID Connect - [#1420](https://github.com/the-djmaze/snappymail/issues/1420) + [#1420](https://github.com/nextgen-networks/snappymail/issues/1420) ## 2.33.0 – 2024-01-22 @@ -630,18 +630,18 @@ ### Fixed - Sorting not supported since 2.32.0 - [#1373](https://github.com/the-djmaze/snappymail/issues/1373) + [#1373](https://github.com/nextgen-networks/snappymail/issues/1373) - FILE_ON_SAVING_ERROR is not defined - [#1379](https://github.com/the-djmaze/snappymail/issues/1379) + [#1379](https://github.com/nextgen-networks/snappymail/issues/1379) - Saving EML files with same subject result in only saving latest email - [#1381](https://github.com/the-djmaze/snappymail/issues/1381) + [#1381](https://github.com/nextgen-networks/snappymail/issues/1381) - Some Sieve parser issues - Handling of RainLoop Sieve script - Sieve rfc5429 RejectCommand and ErejectCommand - KnockoutJS title:value was removed, use attr:{title:value} - dataBaseUpgrade() always runs on sqlite and pgsql - Message was sent but not saved to sent items folder - [#1397](https://github.com/the-djmaze/snappymail/issues/1397) + [#1397](https://github.com/nextgen-networks/snappymail/issues/1397) - DKIM `pass` detection sometimes failed @@ -652,22 +652,22 @@ - MessageListThreadsMap as background task when message_list_limit is set - Properly set CACHEDIR.TAG - Sending group email to all contact addresses - [#1286](https://github.com/the-djmaze/snappymail/pull/1286) by @rezaei92 + [#1286](https://github.com/nextgen-networks/snappymail/pull/1286) by @rezaei92 ### Changed - Default IMAP message_list_limit to 10000 - DoMessageCopy() return toFolder hash/etag - Improved Squire WYSIWYG - Sort real attachments and inline attachments for - [#1360](https://github.com/the-djmaze/snappymail/issues/1360) + [#1360](https://github.com/nextgen-networks/snappymail/issues/1360) - Nextcloud Theme fixes and improvements - [#1363](https://github.com/the-djmaze/snappymail/pull/1363) by @hampoelz + [#1363](https://github.com/nextgen-networks/snappymail/pull/1363) by @hampoelz - Improve display of attachments - [#1361](https://github.com/the-djmaze/snappymail/issues/1361) + [#1361](https://github.com/nextgen-networks/snappymail/issues/1361) - Rename messageVisibility to messageVisible - All CSS font-size to % instead of px - Flip source code view of .eml attachments - [#1332](https://github.com/the-djmaze/snappymail/issues/1332) + [#1332](https://github.com/nextgen-networks/snappymail/issues/1332) ### Fixed - Folders array_filter(): Argument 1 must be of type array, null given @@ -676,7 +676,7 @@ ### Nextcloud - Improved language handling - [#1362](https://github.com/the-djmaze/snappymail/pull/1362) by @avinash-0007 + [#1362](https://github.com/nextgen-networks/snappymail/pull/1362) by @avinash-0007 - FilterLanguage had wrong parameter order - Use NextcloudV25+ theme by default @@ -694,18 +694,18 @@ ### Fixed - Call to undefined method FolderMyRights() - [#1344](https://github.com/the-djmaze/snappymail/issues/1344) + [#1344](https://github.com/nextgen-networks/snappymail/issues/1344) - NO Mailbox does not exist, or must be subscribed to") - [#1354](https://github.com/the-djmaze/snappymail/issues/1354) + [#1354](https://github.com/nextgen-networks/snappymail/issues/1354) - Flag indicators are added to wrong message - [#1347](https://github.com/the-djmaze/snappymail/pull/1347) by @SergeyMosin + [#1347](https://github.com/nextgen-networks/snappymail/pull/1347) by @SergeyMosin - Squire: issue when using the enter key in a reply window - [#1296](https://github.com/the-djmaze/snappymail/issues/1296) + [#1296](https://github.com/nextgen-networks/snappymail/issues/1296) - Squire: crash on cut/delete range ### Nextcloud - Use language as defined in Nextcloud settings - [#1293](https://github.com/the-djmaze/snappymail/issues/1293) + [#1293](https://github.com/nextgen-networks/snappymail/issues/1293) - Plugin Call to undefined method RainLoop\Model\MainAccount::ImapConnectAndLoginHelper() - SnappyMail failed due to Nextcloud Symfony polyfill @@ -716,22 +716,22 @@ - SnappyMail\SensitiveString class to secure passwords - Allow to disable all IMAP features through Admin -> Domain - Setting to open mails in a tab or new window - [#951](https://github.com/the-djmaze/snappymail/issues/951) + [#951](https://github.com/nextgen-networks/snappymail/issues/951) - Fully support IMAP PREVIEW - [#1338](https://github.com/the-djmaze/snappymail/issues/1338) + [#1338](https://github.com/nextgen-networks/snappymail/issues/1338) - Disable "Mark message as read after", offer manual toggle - [#1289](https://github.com/the-djmaze/snappymail/issues/1289) + [#1289](https://github.com/nextgen-networks/snappymail/issues/1289) - A "Move to" button inside message view as an icon/button and in the drop down menu. - [#1295](https://github.com/the-djmaze/snappymail/issues/1295) + [#1295](https://github.com/nextgen-networks/snappymail/issues/1295) - Support for IMAP WITHIN - Support \noinferiors to disallow creating subfolders - A test due to Failed loading libs.min.js - [#358](https://github.com/the-djmaze/snappymail/issues/358), - [#862](https://github.com/the-djmaze/snappymail/issues/862), - [#890](https://github.com/the-djmaze/snappymail/issues/890), - [#895](https://github.com/the-djmaze/snappymail/issues/895), - [#1238](https://github.com/the-djmaze/snappymail/issues/1238), - [#1320](https://github.com/the-djmaze/snappymail/issues/1320) + [#358](https://github.com/nextgen-networks/snappymail/issues/358), + [#862](https://github.com/nextgen-networks/snappymail/issues/862), + [#890](https://github.com/nextgen-networks/snappymail/issues/890), + [#895](https://github.com/nextgen-networks/snappymail/issues/895), + [#1238](https://github.com/nextgen-networks/snappymail/issues/1238), + [#1320](https://github.com/nextgen-networks/snappymail/issues/1320) ### Changed - Split PHP 8 polyfills from include.php @@ -745,75 +745,75 @@ ### Fixed - Move to button does not work - [#1328](https://github.com/the-djmaze/snappymail/issues/1328) + [#1328](https://github.com/nextgen-networks/snappymail/issues/1328) - Mark passwords as sensitive information - [#1343](https://github.com/the-djmaze/snappymail/issues/1343) + [#1343](https://github.com/nextgen-networks/snappymail/issues/1343) - Account sSmtpPassword wrong value - SCRAM sign-in failed - [#1245](https://github.com/the-djmaze/snappymail/issues/1245) + [#1245](https://github.com/nextgen-networks/snappymail/issues/1245) - Squire generates to many `

` - [#1339](https://github.com/the-djmaze/snappymail/issues/1339) + [#1339](https://github.com/nextgen-networks/snappymail/issues/1339) - Creation of dynamic property SnappyMail\Stream\ZipEntry::$compression is deprecated - `json.after-*` hooks didn't send $aResponse as recursive array - Sieve: Move to folder with trailing space does not work - [#1329](https://github.com/the-djmaze/snappymail/issues/1329) + [#1329](https://github.com/nextgen-networks/snappymail/issues/1329) - Squire: cantFocusEmptyTextNodes var is always undefined - [#1337](https://github.com/the-djmaze/snappymail/issues/1337) + [#1337](https://github.com/nextgen-networks/snappymail/issues/1337) - Squire: Remove redundant after replacing styles - Squire: Handle empty nodes in moveRangeBoundariesDownTree - Theme "Nextcloud V25+" can't be translated - [#1331](https://github.com/the-djmaze/snappymail/issues/1331) + [#1331](https://github.com/nextgen-networks/snappymail/issues/1331) ## 2.29.4 – 2023-11-21 ### Fixed - Contacts not work - [#1319](https://github.com/the-djmaze/snappymail/issues/1319) + [#1319](https://github.com/nextgen-networks/snappymail/issues/1319) ## 2.29.3 – 2023-11-21 ### Added - Docker Hub image - [#965](https://github.com/the-djmaze/snappymail/pull/965) by @leojonathanoh + [#965](https://github.com/nextgen-networks/snappymail/pull/965) by @leojonathanoh ### Changed - Sabre/VObject 4.5.4 and Sabre/Xml 4.0.4 - [#1311](https://github.com/the-djmaze/snappymail/issues/1311) + [#1311](https://github.com/nextgen-networks/snappymail/issues/1311) ### Fixed - '#/mailbox/folder/mUID/search' uri/route handling - [#1301](https://github.com/the-djmaze/snappymail/pull/1301) by @SergeyMosin + [#1301](https://github.com/nextgen-networks/snappymail/pull/1301) by @SergeyMosin - "Remember me" doesn't work when browser is closed - [#1313](https://github.com/the-djmaze/snappymail/issues/1313) + [#1313](https://github.com/nextgen-networks/snappymail/issues/1313) - Blank email displayed when "Prefer HTML to plain text" is unchecked and the message is html only - [#1302](https://github.com/the-djmaze/snappymail/issues/1302) + [#1302](https://github.com/nextgen-networks/snappymail/issues/1302) - Parent folder of Sub folder not useable. - [#1008](https://github.com/the-djmaze/snappymail/issues/1008) + [#1008](https://github.com/nextgen-networks/snappymail/issues/1008) - Large detailed header don't display body - [#1284](https://github.com/the-djmaze/snappymail/issues/1284) + [#1284](https://github.com/nextgen-networks/snappymail/issues/1284) ### Nextcloud - Improvements for Install / update issues #929 - [#929](https://github.com/the-djmaze/snappymail/issues/929) + [#929](https://github.com/nextgen-networks/snappymail/issues/929) - Should use language as defined in cloud settings #1293 - [#1293](https://github.com/the-djmaze/snappymail/issues/1293) + [#1293](https://github.com/nextgen-networks/snappymail/issues/1293) ## 2.29.2 – 2023-11-14 ### Added - Show size of folders in folders list #1303 - [#1303](https://github.com/the-djmaze/snappymail/issues/1303) + [#1303](https://github.com/nextgen-networks/snappymail/issues/1303) ### Fixed - Configuration failed when using special chars in MySQL password #1308 - [#1308](https://github.com/the-djmaze/snappymail/issues/1308) + [#1308](https://github.com/nextgen-networks/snappymail/issues/1308) - With email open, "delete" doesn't delete #1274 - [#1274](https://github.com/the-djmaze/snappymail/issues/1274) + [#1274](https://github.com/nextgen-networks/snappymail/issues/1274) - Fix threading view in Thunderbird (others?) - [#1304](https://github.com/the-djmaze/snappymail/pull/1304) by @tkasch + [#1304](https://github.com/nextgen-networks/snappymail/pull/1304) by @tkasch ## 2.29.1 – 2023-10-02 @@ -826,102 +826,102 @@ ### Added - Modern UI / Nextcloud Theme - [#629](https://github.com/the-djmaze/snappymail/pull/629) by @hampoelz + [#629](https://github.com/nextgen-networks/snappymail/pull/629) by @hampoelz - "Add/Edit signature" label to PopupsIdentity.html - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - use calendar icon in message list for messages with '.ics' or 'text/calendar' attachments by @SergeyMosin - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) - Show unseen message count when the message list is threaded - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - in mobile mode hide folders(left) panel when a folder is clicked - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - spellcheck the subject when 'allowSpellcheck' setting is true - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - 'collapse_blockquotes', 'allow_spellcheck' and 'mail_list_grouped' to admin settings ('defaults' section) - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - Browser support for autocompleting TOTP code - [#1251](https://github.com/the-djmaze/snappymail/issues/1251) + [#1251](https://github.com/nextgen-networks/snappymail/issues/1251) ### Changed - URL strip tracking for - [#1225](https://github.com/the-djmaze/snappymail/issues/1225) + [#1225](https://github.com/nextgen-networks/snappymail/issues/1225) - Color picker use color blind palette "Tableau 10" by Maureen Stone by default - [#1199](https://github.com/the-djmaze/snappymail/issues/1199) + [#1199](https://github.com/nextgen-networks/snappymail/issues/1199) - Draft code to improve mobile breakpoints - [#1150](https://github.com/the-djmaze/snappymail/issues/1150) + [#1150](https://github.com/nextgen-networks/snappymail/issues/1150) - address input: space character can trigger '_parseValue' if the email address looks complete - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - if applicable set '\\answered' or '$forwarded' flag after a message is sent so the proper icon is shown in the message list view - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin ### Fixed - CHARSET is not valid in UTF8 mode - [#1230](https://github.com/the-djmaze/snappymail/issues/1230) + [#1230](https://github.com/nextgen-networks/snappymail/issues/1230) - Spam score is always "acceptable" - [#1228](https://github.com/the-djmaze/snappymail/issues/1228) + [#1228](https://github.com/nextgen-networks/snappymail/issues/1228) - Undefined constant PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT - [#1205](https://github.com/the-djmaze/snappymail/issues/1205) + [#1205](https://github.com/nextgen-networks/snappymail/issues/1205) - Fetch controller.abort(reason) handling - [#1220](https://github.com/the-djmaze/snappymail/issues/1220) + [#1220](https://github.com/nextgen-networks/snappymail/issues/1220) - "Request failed" on message move - [#1220](https://github.com/the-djmaze/snappymail/issues/1220) + [#1220](https://github.com/nextgen-networks/snappymail/issues/1220) - Unwrapped text nodes attached to squire._root - [#1234](https://github.com/the-djmaze/snappymail/pull/1234) by @SergeyMosin + [#1234](https://github.com/nextgen-networks/snappymail/pull/1234) by @SergeyMosin - Extra wrapper div is added in Squire every time a Draft is open (or closed) after save. - [#1208](https://github.com/the-djmaze/snappymail/issues/1208) + [#1208](https://github.com/nextgen-networks/snappymail/issues/1208) - foreach() argument must be of type array|object - [#1237](https://github.com/the-djmaze/snappymail/issues/1237) + [#1237](https://github.com/nextgen-networks/snappymail/issues/1237) - `` tag 'style' is lost in replies - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - unseen indicator is not shown in thread view when 'listGrouped' settings is false - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - TOTP plugin is dependent on ctype - [#1250](https://github.com/the-djmaze/snappymail/issues/1250) + [#1250](https://github.com/nextgen-networks/snappymail/issues/1250) ### Nextcloud - iFrame mode: click on unified search result opens inner iFrame - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - set 'smremember' cookie if 'sign_me_auto' is set to 'DefaultOn' when using 'snappymail-autologin*', otherwise nextcloud users need to re-login when the browser is re-opened - [#1248](https://github.com/the-djmaze/snappymail/pull/1248) by @SergeyMosin + [#1248](https://github.com/nextgen-networks/snappymail/pull/1248) by @SergeyMosin - Improve UX of "Put in Calendar" option in plugin - [#1259](https://github.com/the-djmaze/snappymail/pull/1259) by @theronakpatel + [#1259](https://github.com/nextgen-networks/snappymail/pull/1259) by @theronakpatel ## 2.28.4 – 2023-07-10 ### Added - application.ini msg_default_action - [#1204](https://github.com/the-djmaze/snappymail/pull/1204) by @SergeyMosin + [#1204](https://github.com/nextgen-networks/snappymail/pull/1204) by @SergeyMosin - application.ini view_show_next_message - [#1204](https://github.com/the-djmaze/snappymail/pull/1204) by @SergeyMosin + [#1204](https://github.com/nextgen-networks/snappymail/pull/1204) by @SergeyMosin - application.ini view_images - [#1204](https://github.com/the-djmaze/snappymail/pull/1204) by @SergeyMosin + [#1204](https://github.com/nextgen-networks/snappymail/pull/1204) by @SergeyMosin - nextcloud add ability to include custom php file in InstallStep migration - [#1197](https://github.com/the-djmaze/snappymail/pull/1197) by @SergeyMosin + [#1197](https://github.com/nextgen-networks/snappymail/pull/1197) by @SergeyMosin - Support plugin for Squire editor - [#1192](https://github.com/the-djmaze/snappymail/issues/1192) + [#1192](https://github.com/nextgen-networks/snappymail/issues/1192) ### Changed - only show 'Add "domain.tld" as an application for mailto links?' message after login (firefox shows the message on every reload otherwise). - [#1204](https://github.com/the-djmaze/snappymail/issues/1204) + [#1204](https://github.com/nextgen-networks/snappymail/issues/1204) - Convert getPdoAccessData() : array to a RainLoop\Pdo\Settings object instance - New bidi buttons to Squire editor - [#1200](https://github.com/the-djmaze/snappymail/pull/1200) by @rezaei92 + [#1200](https://github.com/nextgen-networks/snappymail/pull/1200) by @rezaei92 ### Fixed - Undefined constant PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT - [#1205](https://github.com/the-djmaze/snappymail/issues/1205) + [#1205](https://github.com/nextgen-networks/snappymail/issues/1205) - 'reloadTime' function result is passed into 'setInterval' instead of the function - [#1204](https://github.com/the-djmaze/snappymail/pull/1204) by @SergeyMosin + [#1204](https://github.com/nextgen-networks/snappymail/pull/1204) by @SergeyMosin - UNKNOWN-CTE Invalid data in MIME part - [#1186](https://github.com/the-djmaze/snappymail/issues/1186) + [#1186](https://github.com/nextgen-networks/snappymail/issues/1186) ## 2.28.3 – 2023-06-22 ### Added - Attachments in "new window" view - [#1166](https://github.com/the-djmaze/snappymail/issues/1166) + [#1166](https://github.com/nextgen-networks/snappymail/issues/1166) ### Changed - Update Portuguese by @ner00 @@ -929,22 +929,22 @@ ### Fixed - Some emails with inline CSS break the UI - [#1187](https://github.com/the-djmaze/snappymail/issues/1187) + [#1187](https://github.com/nextgen-networks/snappymail/issues/1187) - Remote.get() Promise broken by previous change - [#1185](https://github.com/the-djmaze/snappymail/issues/1185) + [#1185](https://github.com/nextgen-networks/snappymail/issues/1185) - Class "MailSo\Base\Exceptions\InvalidArgumentException" not found - [#1182](https://github.com/the-djmaze/snappymail/issues/1182) + [#1182](https://github.com/nextgen-networks/snappymail/issues/1182) - First account not showed in the right list (dropbox) - [#1180](https://github.com/the-djmaze/snappymail/issues/1180) + [#1180](https://github.com/nextgen-networks/snappymail/issues/1180) ## 2.28.2 – 2023-06-19 ### Added - Detailed error for "Cannot access the repository at the moment" - [#1164](https://github.com/the-djmaze/snappymail/issues/1164) + [#1164](https://github.com/nextgen-networks/snappymail/issues/1164) - Bidi in Squire editor - [#1158](https://github.com/the-djmaze/snappymail/issues/1158) + [#1158](https://github.com/nextgen-networks/snappymail/issues/1158) - Translate Squire UI - Nextcloud 27 compatibility by @LarsBel - JWT class for handling JSON Web Tokens @@ -954,19 +954,19 @@ - Update French by @hguilbert - Update Polish by @tinola - Merge handling of local Account Settings. Found while investigating - [#1170](https://github.com/the-djmaze/snappymail/issues/1170) + [#1170](https://github.com/nextgen-networks/snappymail/issues/1170) - Image max-width now 100% instead of 90vw ### Fixed - Cannot modify header information - [#929](https://github.com/the-djmaze/snappymail/issues/929) (comment) + [#929](https://github.com/nextgen-networks/snappymail/issues/929) (comment) - Admin Panel broken when admin_panel_host is set - [#1169](https://github.com/the-djmaze/snappymail/issues/1169) + [#1169](https://github.com/nextgen-networks/snappymail/issues/1169) - Invalid CSP report-uri - Prevent MessageList multiple request at the same time - [#1071](https://github.com/the-djmaze/snappymail/issues/1071) + [#1071](https://github.com/nextgen-networks/snappymail/issues/1071) - Error in Addressbook Sync - [#1179](https://github.com/the-djmaze/snappymail/issues/1179) + [#1179](https://github.com/nextgen-networks/snappymail/issues/1179) - base64_decode() second parameter must be true @@ -974,91 +974,91 @@ ### Changed - Optical issue with input fields for mail and folder search - [#1149](https://github.com/the-djmaze/snappymail/issues/1149) + [#1149](https://github.com/nextgen-networks/snappymail/issues/1149) - Update Chinese translation - [#1157](https://github.com/the-djmaze/snappymail/pull/1157) by @mayswind + [#1157](https://github.com/nextgen-networks/snappymail/pull/1157) by @mayswind - Update Polish translation - [#1156](https://github.com/the-djmaze/snappymail/pull/1156) by @tinola + [#1156](https://github.com/nextgen-networks/snappymail/pull/1156) by @tinola ### Fixed - Undefined SIG constants - [#1147](https://github.com/the-djmaze/snappymail/issues/1147) + [#1147](https://github.com/nextgen-networks/snappymail/issues/1147) ## 2.28.0 – 2023-05-30 ### Added - Threaded view make number orange when unread sub-messages - [#1028](https://github.com/the-djmaze/snappymail/issues/1028) + [#1028](https://github.com/nextgen-networks/snappymail/issues/1028) - Handle PHP pctnl messages - addEventListener('rl-view-model') missing for Settings - [#1013](https://github.com/the-djmaze/snappymail/issues/1013) + [#1013](https://github.com/nextgen-networks/snappymail/issues/1013) - CSS `--btn-border-radius` ### Changed - Improved RTL languages support - [#1056](https://github.com/the-djmaze/snappymail/issues/1056) + [#1056](https://github.com/nextgen-networks/snappymail/issues/1056) - Composer text/attachments as tabs - [#1119](https://github.com/the-djmaze/snappymail/issues/1119) + [#1119](https://github.com/nextgen-networks/snappymail/issues/1119) - Filter dialog doesn't refer to folder names consistently - [#1111](https://github.com/the-djmaze/snappymail/issues/1111) + [#1111](https://github.com/nextgen-networks/snappymail/issues/1111) - TLS connection for MYSQL contact db - [#1078](https://github.com/the-djmaze/snappymail/issues/1078) + [#1078](https://github.com/nextgen-networks/snappymail/issues/1078) - Allow empty message body when there are attachments - [#1052](https://github.com/the-djmaze/snappymail/issues/1052) + [#1052](https://github.com/nextgen-networks/snappymail/issues/1052) - PHP inherit logger as Trait - Update Portuguese - [#1124](https://github.com/the-djmaze/snappymail/pull/1124) by @ner00 + [#1124](https://github.com/nextgen-networks/snappymail/pull/1124) by @ner00 - Update Traditional Chinese (Taiwan) - [#1107](https://github.com/the-djmaze/snappymail/pull/1107) by @chiyi4488 + [#1107](https://github.com/nextgen-networks/snappymail/pull/1107) by @chiyi4488 - Update Russian - [#1108](https://github.com/the-djmaze/snappymail/pull/1108) by @konkere + [#1108](https://github.com/nextgen-networks/snappymail/pull/1108) by @konkere - Update Italian - [#1094](https://github.com/the-djmaze/snappymail/pull/1094) by @cm-schl + [#1094](https://github.com/nextgen-networks/snappymail/pull/1094) by @cm-schl - Update French - [#1102](https://github.com/the-djmaze/snappymail/pull/1102) by @hguilbert + [#1102](https://github.com/nextgen-networks/snappymail/pull/1102) by @hguilbert - Update German - [#1087](https://github.com/the-djmaze/snappymail/pull/1087) by @cm-schl + [#1087](https://github.com/nextgen-networks/snappymail/pull/1087) by @cm-schl ### Fixed - Show messagelist timeout/abort error for - [#1071](https://github.com/the-djmaze/snappymail/issues/1071) + [#1071](https://github.com/nextgen-networks/snappymail/issues/1071) - DesktopNotifications setting not saved - [#1137](https://github.com/the-djmaze/snappymail/issues/1137) + [#1137](https://github.com/nextgen-networks/snappymail/issues/1137) - PHP Deprecation warning for $_openPipes - [#1141](https://github.com/the-djmaze/snappymail/issues/1141) + [#1141](https://github.com/nextgen-networks/snappymail/issues/1141) - Images size wrong - [#1134](https://github.com/the-djmaze/snappymail/issues/1134) + [#1134](https://github.com/nextgen-networks/snappymail/issues/1134) - Unable to preview body of encrypted mail in mailvelope reply-to - [#1130](https://github.com/the-djmaze/snappymail/issues/1130) + [#1130](https://github.com/nextgen-networks/snappymail/issues/1130) - Replace `