')
.replace(/<(\/?)body(\s[^>]*)?>/gi, '<$1div$2>')
// .replace(/<\/?(html|head)[^>]*>/gi, '')
@@ -361,18 +363,6 @@ export const
});
*/
- // https://github.com/the-djmaze/snappymail/issues/1641
- let body = tmpl.content.querySelector('.mail-body');
- [...tmpl.content.querySelectorAll('.mail-body + .mail-body')]
- .forEach(oElement => body.append(...oElement.childNodes));
-/*
- .forEach(oElement => {
- let bq = createElement('blockquote');
- bq.append(...oElement.childNodes);
- body.replaceWith(bq);
- });
-*/
-
[...tmpl.content.querySelectorAll('*')].forEach(oElement => {
const name = oElement.tagName,
oStyle = oElement.style;
From 2de4ebac5476b0b3402a01b07ce9e9d359275498 Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 00:54:36 +0100
Subject: [PATCH 171/181] Use custom TAR builder instead of PharData
---
cli/release.php | 16 +-
.../v/0.0.0/app/libraries/snappymail/tar.php | 271 ++++++++++++++++++
2 files changed, 282 insertions(+), 5 deletions(-)
diff --git a/cli/release.php b/cli/release.php
index acafcc02b..fd17657b0 100755
--- a/cli/release.php
+++ b/cli/release.php
@@ -2,6 +2,7 @@
open($zip_destination, ZIPARCHIVE::CREATE)) {
exit("Failed to create {$zip_destination}");
}
-$tar = new PharData($tar_destination);
+$tar_destination .= '.gz';
+$tar = new \SnappyMail\TAR($tar_destination);
+//$tar = new PharData($tar_destination);
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST);
foreach ($files as $file) {
@@ -158,10 +161,13 @@ $zip->addFile('README.md');
$tar->addFile('README.md');
$zip->close();
-
-$tar->compress(Phar::GZ);
-unlink($tar_destination);
-$tar_destination .= '.gz';
+if ($tar instanceof PharData) {
+ $tar->compress(Phar::GZ);
+ unlink($tar_destination);
+ $tar_destination .= '.gz';
+} else {
+ $tar->close();
+}
echo "{$zip_destination} created\n{$tar_destination} created\n";
diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/tar.php b/snappymail/v/0.0.0/app/libraries/snappymail/tar.php
index 80ae166a1..0098c69b0 100644
--- a/snappymail/v/0.0.0/app/libraries/snappymail/tar.php
+++ b/snappymail/v/0.0.0/app/libraries/snappymail/tar.php
@@ -98,4 +98,275 @@ class TAR
\gzclose($fp);
return true;
}
+
+ const
+ TYPE_FILE = 0,
+ TYPE_LINK = 2,
+ TYPE_DIR = 5;
+
+ protected
+ $gzip = array('w' => null, 'h' => null, 'l' => 0),
+ $stream = null,
+ $stream_info = null;
+
+ public function start(): void
+ {
+ if ($this->stream) {
+ return;
+ }
+ $this->stream = \fopen($this->filename, 'wb');
+ if (\str_ends_with($this->filename, '.gz')) {
+ // Write gzip header, see http://www.zlib.org/rfc-gzip.html#member-format
+ if (!\fwrite($this->stream, "\x1F\x8B\x08\x00".\pack('V', \time())."\0\x03", 10)) {
+ throw new \Exception('Failed to write to stream');
+ }
+ // Start compression
+ $this->gzip['w'] = \stream_filter_append($this->stream, 'zlib.deflate', STREAM_FILTER_WRITE, array(
+ 'level' => 9,
+// 'window' => 32768,
+ 'memory' => 9,
+ ));
+ // Start CRC32 hashing
+ $this->gzip['h'] = \hash_init('crc32b');
+ }
+ $this->stream_info = new \SplFileInfo($this->filename);
+ }
+
+ function __destruct()
+ {
+ $this->close();
+ }
+
+ public function close(): bool
+ {
+ if (!$this->stream) {
+ return false;
+ }
+ // Write tar footer
+ $this->write(\pack('a1024', ''));
+ // Stop compression
+ if ($this->gzip['w']) {
+ \stream_filter_remove($this->gzip['w']);
+ // hash_final is a string, not an integer
+ $crc = \hash_final($this->gzip['h'], 1);
+ // write the little endian CRC32 and uncompressed file size
+ \fwrite($this->stream, $crc[3].$crc[2].$crc[1].$crc[0].\pack('V', $this->gzip['l']), 8);
+ }
+ \fclose($this->stream);
+ $this->stream = $this->stream_info = null;
+ return true;
+ }
+
+ public function addFile(string $name, ?string $localName = null, ?\SplFileInfo $fileinfo = null): void
+ {
+ $this->start();
+ if (!$fileinfo) {
+ $fileinfo = new \SplFileInfo($name);
+ }
+ if (!$localName) {
+ $localName = $name;
+ }
+ if ($fileinfo->isLink()) {
+ $stat = \lstat($fileinfo);
+ $target = $fileinfo->getLinkTarget();
+ if (\dirname($fileinfo->getPathname()) === \dirname($fileinfo->getRealPath())) {
+ $target = \basename($target);
+ }
+ $this->writeEntryHeader(
+ $localName,
+ static::TYPE_LINK,
+ 0,
+ $stat['mode'],
+ $target
+ );
+ } else if ($fileinfo->isDir()) {
+ $this->writeEntryHeader(
+ \rtrim($localName, '/') . '/',
+ static::TYPE_DIR,
+ 0,
+ $fileinfo->getPerms()
+ );
+ } else {
+ $file = $fileinfo->openFile('rb');
+ $this->writeEntryHeader(
+ $localName,
+ static::TYPE_FILE,
+ $file->getSize(),
+ $fileinfo->getPerms()
+ );
+ while (!$file->eof()) {
+ $data = $file->fread(4096);
+ if (false === $data || '' === $data) {
+ break;
+ }
+ $this->write($data);
+ }
+ if ($l = $file->ftell() % 512) {
+ $l = 512 - $l;
+ $this->write(\pack("a{$l}", ''));
+ }
+ unset($file);
+ }
+ }
+
+ public function addFromString(string $name, string $contents): void
+ {
+ $this->start();
+ $this->writeEntryHeader(
+ $name,
+ static::TYPE_FILE,
+ \strlen($contents),
+ $this->stream_info->getPerms()
+ );
+ $this->write($contents);
+ if ($l = \strlen($contents) % 512) {
+ $l = 512 - $l;
+ $this->write(\pack("a{$l}", ''));
+ }
+ }
+
+ public function buildFromDirectory(string $dir, string $pattern = ''): array
+ {
+ $this->start();
+ \clearstatcache();
+ $dir = \rtrim($dir,'\\/') . '/';
+ $dirl = \strlen($dir);
+ $iterator = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS /*| \FilesystemIterator::FOLLOW_SYMLINKS*/),
+ \RecursiveIteratorIterator::SELF_FIRST,
+ \RecursiveIteratorIterator::CATCH_GET_CHILD);
+ $ignore_paths = array();
+ foreach ($iterator as $name => $fileinfo) {
+ if ($fileinfo->isDir() && \is_file("{$fileinfo}/CACHEDIR.TAG")) {
+ // like: tar --exclude-caches -czf file.tgz *
+ $ignore_paths[] = \preg_quote($name . '/', '#');
+ continue;
+ }
+ if ($ignore_paths && \preg_match('#'.\implode('|',$ignore_paths).'#', $name)) {
+ continue;
+ }
+ if (!$pattern || \preg_match($pattern, $name)) {
+ $name = \substr($name, $dirl);
+ $this->addFile($name, null, $fileinfo);
+ }
+ }
+ return [];
+ }
+
+ protected function write(string $string): int
+ {
+ $length = \strlen($string);
+ $written = 0;
+ while ($written < $length) {
+ $bytes = \fwrite($this->stream, $written ? \substr($string, $written) : $string);
+ if (!$bytes) {
+ return $written ?: false;
+ }
+ $written += $bytes;
+ }
+
+ if ($this->gzip['h']) {
+ $this->gzip['l'] += $length;
+ \hash_update($this->gzip['h'], $string);
+ }
+
+ return $written;
+ }
+
+ protected function writeEntryHeader(string $name, int $type, int $size,
+ int $perm = 0, string $link = '', string $prefix = ''
+ ): void
+ {
+ // handle long filename length
+ $paxdata = $paxname = '';
+ if (100 < \strlen($link)) {
+ $length = \strlen($link) + 11;
+ $length += \strlen($length);
+ $paxdata = "{$length} linkpath={$link}\n";
+ $link = '././@LongSymLink';
+ }
+ $l = \strlen($name);
+ if (($paxdata?90:100) < $l) {
+ // split into name and prefix
+ $p = \strpos($name, '/', \max(0, $l - 90));
+ if ($p && $p < $l-1) {
+ $file = \substr($name, $p+1);
+ $prefix = \substr($name, 0, $p);
+ $paxname = \preg_replace('#(^|/)([^/]+)$#', '$1PaxHeader/$2', $file);
+ } else {
+ $file = \basename($name);
+ if (static::TYPE_DIR === $type) {
+ $file .= '/';
+ }
+ $prefix = \dirname($name);
+ $paxname = 'PaxHeader/' . $file;
+ }
+ if (100 < \strlen($file) || 155 < \strlen($prefix)) {
+ // POSIX.1-2001/pax
+ $length = $l + 7;
+ $length += \strlen($length);
+ $paxdata = "{$length} path={$name}\n{$paxdata}";
+ if (static::TYPE_DIR === $type) {
+ $name = \substr($file, 0, 98) . '/';
+ } else {
+ $name = \substr($file, 0, 99);
+ }
+ } else {
+ // POSIX ustar
+ $name = $file;
+ }
+ $paxname = \substr($paxname, 0, 98);
+ }
+ if ($paxdata) {
+ /* Add?
+ $data .= "30 mtime=1461056595.149922432\n"
+ $data .= "30 ctime=1461056595.149922432\n"
+ */
+ $this->writeUStarEntryHeader(
+ $paxname ?: \preg_replace('#(^|/)([^/]+)$#', '$1PaxHeader/$2', $name),
+ 'x',
+ \strlen($paxdata),
+ $perm,
+ '',
+ $prefix
+ );
+ $l = 512 * \ceil(\strlen($paxdata) / 512);
+ $this->write(\pack("a{$l}", $paxdata));
+ }
+
+ $this->writeUStarEntryHeader($name, $type, $size, $perm, $link, $prefix);
+ }
+
+ // Writes Pre-POSIX.1-1988 (i.e. v7) and POSIX UStar headers
+ protected function writeUStarEntryHeader(string $name, int $type, int $size,
+ int $perm = 0, string $link = '', string $prefix = ''
+ ): void
+ {
+ $uid = 0; // $this->stream_info->getOwner(),
+ $gid = 0; // $this->stream_info->getGroup(),
+ $mtime = $this->stream_info->getMTime();
+ $data = \pack('a100a8a8a8a12A12',
+ $name,
+ // values in octal
+ \sprintf("%06u ", \substr(\decoct($perm),-3)),
+ \sprintf("%06u ", \decoct($uid)),
+ \sprintf("%06u ", \decoct($gid)),
+ \sprintf("%011u ", \decoct($size)),
+ \sprintf("%011u", \decoct($mtime)));
+ $this->write($data);
+ $checksum = 0;
+ $i = 148;
+ while ($i--) {
+ $checksum += \ord($data[$i]);
+ }
+
+ $data = \pack('a1a100a6a2a32a32a8a8a155a12', "{$type}", $link, 'ustar', '00', '', '', '000000 ', '000000 ', $prefix, '');
+ $checksum += 256;
+ $i = 356;
+ while ($i--) {
+ $checksum += \ord($data[$i]);
+ }
+
+ $this->write(\pack('a8', \sprintf('%06u ', \decoct($checksum))) . $data);
+ }
}
From 71d10f8cf6a73ad9f3f7e01a9933bd4ceffc5dcd Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 01:07:33 +0100
Subject: [PATCH 172/181] fix(security): require CSRF token for headerless GET
JSON actions
https://github.com/the-djmaze/snappymail/commit/ba3741080a9ba68e8fa6cefbbd35254788de7175
---
.../0.0.0/app/libraries/RainLoop/ServiceActions.php | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php
index b3ad82241..a1bc2637e 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php
@@ -103,11 +103,17 @@ class ServiceActions
$this->oActions->logWrite("{$_SERVER['HTTP_X_SM_TOKEN']} !== {$token} for {$sEmail}", \LOG_ERR, 'Token');
throw new Exceptions\ClientException(Notifications::InvalidToken, null, 'HTTP Token mismatch');
}
- } else if ($this->oHttp->IsPost()) {
- if (empty($_POST['XToken']) || $_POST['XToken'] !== $token) {
+ } else {
+ // A state-changing action must never be triggerable by a bare
+ // headerless POST/GET (e.g.
![]()
or a top-level navigation):
+ // without the X-SM-Token header the token has to arrive as a
+ // query parameter. The first-party UI always sends the header,
+ // so this only affects headerless callers.
+ $xToken = $this->oHttp->IsPost() ? ($_POST['XToken'] ?? '') : ($_GET['XToken'] ?? '');
+ if (empty($xToken) || $xToken !== $token) {
$oAccount = $this->oActions->getAccountFromToken(false);
$sEmail = $oAccount ? $oAccount->Email() : 'guest';
- $this->oActions->logWrite("{$_POST['XToken']} !== {$token} for {$sEmail}", \LOG_ERR, 'XToken');
+ $this->oActions->logWrite("{$xToken} !== {$token} for {$sEmail}", \LOG_ERR, 'XToken');
throw new Exceptions\ClientException(Notifications::InvalidToken, null, 'XToken mismatch');
}
}
From a5592108a5658586021ec49046832cf89d02ccdd Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 01:36:04 +0100
Subject: [PATCH 173/181] Bugfix BaseAppMainCssSri
---
snappymail/v/0.0.0/app/templates/Index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snappymail/v/0.0.0/app/templates/Index.html b/snappymail/v/0.0.0/app/templates/Index.html
index 8970039ec..4fe7be3d9 100644
--- a/snappymail/v/0.0.0/app/templates/Index.html
+++ b/snappymail/v/0.0.0/app/templates/Index.html
@@ -9,7 +9,7 @@
-
+
From 5d43aeef0bdb2ec077d3ad9f201e464e5173e6b0 Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 02:13:15 +0100
Subject: [PATCH 174/181] Convert some abstract enumeration classes to PHP 8.1+
(Backed)Enum
---
.../snappymail/lib/Util/SnappyMailHelper.php | 4 ++-
.../MailSo/Imap/Commands/Messages.php | 7 +++--
.../MailSo/Imap/Enumerations/ResponseType.php | 10 +++----
.../MailSo/Imap/Enumerations/StoreAction.php | 14 +++++-----
.../app/libraries/MailSo/Imap/ImapClient.php | 27 ++++++++++---------
.../app/libraries/MailSo/Imap/Response.php | 2 +-
.../app/libraries/MailSo/Mail/MailClient.php | 4 +--
.../Mime/Enumerations/MessagePriority.php | 13 ++++++---
.../app/libraries/MailSo/Mime/Message.php | 21 ++-------------
.../0.0.0/app/libraries/RainLoop/Actions.php | 10 +++----
.../libraries/RainLoop/Actions/Messages.php | 2 +-
.../app/libraries/RainLoop/Actions/User.php | 10 +++----
.../libraries/RainLoop/Config/Application.php | 2 +-
.../RainLoop/Enumerations/Layout.php | 8 +++---
.../RainLoop/Enumerations/SignMeType.php | 8 +++---
15 files changed, 66 insertions(+), 76 deletions(-)
diff --git a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php
index 43b03a664..8d165466e 100644
--- a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php
+++ b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php
@@ -2,6 +2,8 @@
namespace OCA\SnappyMail\Util;
+use RainLoop\Enumerations\SignMeType;
+
class SnappyMailResponse extends \OCP\AppFramework\Http\Response
{
public function render(): string
@@ -184,7 +186,7 @@ class SnappyMailHelper
try {
$oAccount = $oActions->LoginProcess($aCredentials[1], $aCredentials[2]);
if (!$isOIDC && $oAccount
- && $oConfig->Get('login', 'sign_me_auto', \RainLoop\Enumerations\SignMeType::DefaultOff) === \RainLoop\Enumerations\SignMeType::DefaultOn
+ && $oConfig->Get('login', 'sign_me_auto', SignMeType::DefaultOff->value) === SignMeType::DefaultOn->value
) {
$oActions->SetSignMeToken($oAccount);
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Messages.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Messages.php
index 10003e83a..5ee403a8f 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Messages.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Messages.php
@@ -354,11 +354,10 @@ trait Messages
* @throws \MailSo\RuntimeException
* @throws \MailSo\Net\Exceptions\*
* @throws \MailSo\Imap\Exceptions\*
- * $sStoreAction = \MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT
*/
- public function MessageStoreFlag(SequenceSet $oRange, array $aInputStoreItems, string $sStoreAction) : ?ResponseCollection
+ public function MessageStoreFlag(SequenceSet $oRange, array $aInputStoreItems, StoreAction $eStoreAction) : ?ResponseCollection
{
- if (!\count($oRange) || !\strlen(\trim($sStoreAction)) || !\count($aInputStoreItems)) {
+ if (!\count($oRange) || !\count($aInputStoreItems)) {
return null;
}
@@ -372,7 +371,7 @@ trait Messages
return $this->SendRequestGetResponse(
$oRange->UID ? 'UID STORE' : 'STORE',
- array((string) $oRange, $sStoreAction, $aInputStoreItems)
+ array((string) $oRange, $eStoreAction->value, $aInputStoreItems)
);
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseType.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseType.php
index d80cc3664..1aee9d563 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseType.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseType.php
@@ -16,10 +16,10 @@ namespace MailSo\Imap\Enumerations;
* @package Imap
* @subpackage Enumerations
*/
-abstract class ResponseType
+enum ResponseType
{
- const UNKNOWN = 0;
- const TAGGED = 1;
- const UNTAGGED = 2;
- const CONTINUATION = 3;
+ case UNKNOWN;
+ case TAGGED;
+ case UNTAGGED;
+ case CONTINUATION;
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/StoreAction.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/StoreAction.php
index 172e0e89b..f7cb02570 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/StoreAction.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/StoreAction.php
@@ -16,12 +16,12 @@ namespace MailSo\Imap\Enumerations;
* @package Imap
* @subpackage Enumerations
*/
-abstract class StoreAction
+enum StoreAction: string
{
-// const SET_FLAGS = 'FLAGS';
-// const SET_FLAGS_SILENT = 'FLAGS.SILENT';
- const ADD_FLAGS = '+FLAGS';
- const ADD_FLAGS_SILENT = '+FLAGS.SILENT';
- const REMOVE_FLAGS = '-FLAGS';
- const REMOVE_FLAGS_SILENT = '-FLAGS.SILENT';
+// case SET_FLAGS = 'FLAGS';
+// case SET_FLAGS_SILENT = 'FLAGS.SILENT';
+ case ADD_FLAGS = '+FLAGS';
+ case ADD_FLAGS_SILENT = '+FLAGS.SILENT';
+ case REMOVE_FLAGS = '-FLAGS';
+ case REMOVE_FLAGS_SILENT = '-FLAGS.SILENT';
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php
index 2ec335901..3c0ea7827 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php
@@ -12,6 +12,7 @@
namespace MailSo\Imap;
use MailSo\Net\Enumerations\ConnectionSecurityType;
+use MailSo\Imap\Enumerations\ResponseType;
/**
* @category MailSo
@@ -147,7 +148,7 @@ class ImapClient extends \MailSo\Net\NetClient
if ('CRAM-MD5' === $type)
{
$oResponse = $this->SendRequestGetResponse('AUTHENTICATE', array($type));
- $sChallenge = $this->getResponseValue($oResponse, Enumerations\ResponseType::CONTINUATION);
+ $sChallenge = $this->getResponseValue($oResponse, ResponseType::CONTINUATION);
$sAuth = $SASL->authenticate($sLogin, $sPassword, $sChallenge);
$this->logMask($sAuth);
$this->sendRaw($sAuth);
@@ -164,13 +165,13 @@ class ImapClient extends \MailSo\Net\NetClient
$this->sendRaw($sAuth);
}
$oResponse = $this->getResponse();
-// if ($oResponse->getLast()->ResponseType === Enumerations\ResponseType::CONTINUATION)
+// if ($oResponse->getLast()->ResponseType === ResponseType::CONTINUATION)
if ($SASL->hasChallenge()) {
- $sChallenge = $SASL->challenge($this->getResponseValue($oResponse, Enumerations\ResponseType::CONTINUATION));
+ $sChallenge = $SASL->challenge($this->getResponseValue($oResponse, ResponseType::CONTINUATION));
$this->logMask($sChallenge);
$this->sendRaw($sChallenge);
$oResponse = $this->getResponse();
- $SASL->verify($this->getResponseValue($oResponse, Enumerations\ResponseType::CONTINUATION));
+ $SASL->verify($this->getResponseValue($oResponse, ResponseType::CONTINUATION));
$this->sendRaw('');
$oResponse = $this->getResponse();
}
@@ -181,7 +182,7 @@ class ImapClient extends \MailSo\Net\NetClient
$this->logMask($sAuth);
$oResponse = $this->SendRequestGetResponse('AUTHENTICATE', array($type, $sAuth));
$oR = $oResponse->getLast();
- if ($oR && Enumerations\ResponseType::CONTINUATION === $oR->ResponseType) {
+ if ($oR && ResponseType::CONTINUATION === $oR->ResponseType) {
if (!empty($oR->ResponseList[1]) && \preg_match('/^[a-zA-Z0-9=+\/]+$/', $oR->ResponseList[1])) {
$this->logWrite(\base64_decode($oR->ResponseList[1]), \LOG_WARNING);
}
@@ -192,7 +193,7 @@ class ImapClient extends \MailSo\Net\NetClient
else if ($this->hasCapability('LOGINDISABLED'))
{
$oResponse = $this->SendRequestGetResponse('AUTHENTICATE', array($type));
- $sB64 = $this->getResponseValue($oResponse, Enumerations\ResponseType::CONTINUATION);
+ $sB64 = $this->getResponseValue($oResponse, ResponseType::CONTINUATION);
$sAuth = $SASL->authenticate($sLogin, $sPassword, $sB64);
$this->logMask($sAuth);
$this->sendRaw($sAuth, true);
@@ -363,7 +364,7 @@ class ImapClient extends \MailSo\Net\NetClient
try {
$oResponseCollection = $this->SendRequestGetResponse('NAMESPACE');
foreach ($oResponseCollection as $oResponse) {
- if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType
+ if (ResponseType::UNTAGGED === $oResponse->ResponseType
&& 'NAMESPACE' === $oResponse->StatusOrIndex)
{
return new NamespaceResult($oResponse);
@@ -444,10 +445,10 @@ class ImapClient extends \MailSo\Net\NetClient
return $this->getResponse();
}
- protected function getResponseValue(ResponseCollection $oResponseCollection, int $type = 0): string
+ protected function getResponseValue(ResponseCollection $oResponseCollection, ResponseType $type): string
{
$oResponse = $oResponseCollection->getLast();
- if ($oResponse && (!$type || $type === $oResponse->ResponseType)) {
+ if ($oResponse && ($type === ResponseType::UNKNOWN || $type === $oResponse->ResponseType)) {
$sResult = $oResponse->ResponseList[1] ?? null;
if ($sResult) {
return $sResult;
@@ -501,7 +502,7 @@ class ImapClient extends \MailSo\Net\NetClient
$oResult->append($oResponse);
if ($oResponse->IsStatusResponse
- && Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType
+ && ResponseType::UNTAGGED === $oResponse->ResponseType
&& Enumerations\ResponseStatus::PREAUTH === $oResponse->StatusOrIndex
// && (Enumerations\ResponseStatus::PREAUTH === $oResponse->StatusOrIndex || Enumerations\ResponseStatus::BYE === $oResponse->StatusOrIndex)
) {
@@ -514,7 +515,7 @@ class ImapClient extends \MailSo\Net\NetClient
// \SnappyMail\Log::warning('IMAP', "{$oResponse->OptionalResponse[0]}: {$this->lastCommand}");
}
- if ($sEndTag === $oResponse->Tag || Enumerations\ResponseType::CONTINUATION === $oResponse->ResponseType) {
+ if ($sEndTag === $oResponse->Tag || ResponseType::CONTINUATION === $oResponse->ResponseType) {
if (isset($this->aTagTimeouts[$sEndTag])) {
$this->writeLog((\microtime(true) - $this->aTagTimeouts[$sEndTag]).' ('.$sEndTag.')', \LOG_DEBUG);
@@ -546,7 +547,7 @@ class ImapClient extends \MailSo\Net\NetClient
while (true) {
$oResponse = $this->partialParseResponse();
- if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType) {
+ if (ResponseType::UNTAGGED === $oResponse->ResponseType) {
yield $oResponse;
} else {
$oResult->append($oResponse);
@@ -558,7 +559,7 @@ class ImapClient extends \MailSo\Net\NetClient
// \SnappyMail\Log::warning('IMAP', "{$oResponse->OptionalResponse[0]}: {$this->lastCommand}");
}
- if ($sEndTag === $oResponse->Tag || Enumerations\ResponseType::CONTINUATION === $oResponse->ResponseType) {
+ if ($sEndTag === $oResponse->Tag || ResponseType::CONTINUATION === $oResponse->ResponseType) {
if (isset($this->aTagTimeouts[$sEndTag])) {
$this->writeLog((\microtime(true) - $this->aTagTimeouts[$sEndTag]).' ('.$sEndTag.')', \LOG_DEBUG);
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php
index 1c76dfb2e..a4fec9eb9 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php
@@ -29,7 +29,7 @@ class Response
public bool $IsStatusResponse = false;
- public int $ResponseType = 0;
+ public ResponseType $ResponseType = ResponseType::UNKNOWN;
public ?string $Tag = null;
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php
index ecca5da53..768d6d8da 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php
@@ -119,8 +119,8 @@ class MailClient
{
if (\count($oRange)) {
if ($this->oImapClient->FolderSelect($sFolderName)->IsFlagSupported($sMessageFlag)) {
- $sStoreAction = $bSetAction ? StoreAction::ADD_FLAGS_SILENT : StoreAction::REMOVE_FLAGS_SILENT;
- $this->oImapClient->MessageStoreFlag($oRange, array($sMessageFlag), $sStoreAction);
+ $eStoreAction = $bSetAction ? StoreAction::ADD_FLAGS_SILENT : StoreAction::REMOVE_FLAGS_SILENT;
+ $this->oImapClient->MessageStoreFlag($oRange, array($sMessageFlag), $eStoreAction);
} else if (!$bSkipUnsupportedFlag) {
throw new \MailSo\RuntimeException('Message flag "'.$sMessageFlag.'" is not supported.');
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MessagePriority.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MessagePriority.php
index 56168d8a7..7b727fab7 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MessagePriority.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MessagePriority.php
@@ -16,9 +16,14 @@ namespace MailSo\Mime\Enumerations;
* @package Mime
* @subpackage Enumerations
*/
-abstract class MessagePriority
+enum MessagePriority: int
{
- const LOW = 5;
- const NORMAL = 3;
- const HIGH = 1;
+ case Lowest = 5;
+ case Normal = 3;
+ case Highest = 1;
+
+ public function toMIME() : string
+ {
+ return $this->value . ' (' . $this->name . ')';
+ }
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php
index 48253b33d..161d48a77 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php
@@ -196,26 +196,9 @@ class Message extends Part
return $this;
}
- public function SetPriority(int $iValue) : self
+ public function SetPriority(Enumerations\MessagePriority $eValue) : self
{
- $sResult = '';
- switch ($iValue)
- {
- case Enumerations\MessagePriority::HIGH:
- $sResult = Enumerations\MessagePriority::HIGH.' (Highest)';
- break;
- case Enumerations\MessagePriority::NORMAL:
- $sResult = Enumerations\MessagePriority::NORMAL.' (Normal)';
- break;
- case Enumerations\MessagePriority::LOW:
- $sResult = Enumerations\MessagePriority::LOW.' (Lowest)';
- break;
- }
-
- if (\strlen($sResult)) {
- $this->aHeadersValue[Enumerations\Header::X_PRIORITY] = $sResult;
- }
-
+ $this->aHeadersValue[Enumerations\Header::X_PRIORITY] = $eValue->toMIME();
return $this;
}
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php
index 598170540..8d7140d7f 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php
@@ -600,7 +600,7 @@ class Actions
'SoundNotification' => true,
'NotificationSound' => 'new-mail',
'DesktopNotifications' => true,
- 'Layout' => (int) $oConfig->Get('defaults', 'view_layout', Enumerations\Layout::SIDE_PREVIEW),
+ 'Layout' => (int) $oConfig->Get('defaults', 'view_layout', Enumerations\Layout::SIDE_PREVIEW->value),
'EditorDefaultType' => \str_replace('Forced', '', $oConfig->Get('defaults', 'view_editor_type', '')),
'editorWysiwyg' => 'Squire',
'UseCheckboxesInList' => (bool) $oConfig->Get('defaults', 'view_use_checkboxes', true),
@@ -736,10 +736,10 @@ class Actions
}
$aResult['signMe'] = [
- SignMeType::DefaultOff => 0,
- SignMeType::DefaultOn => 1,
- SignMeType::Unused => 2
- ][(string) $oConfig->Get('login', 'sign_me_auto', SignMeType::DefaultOff)];
+ SignMeType::DefaultOff->value => 0,
+ SignMeType::DefaultOn->value => 1,
+ SignMeType::Unused->value => 2
+ ][(string) $oConfig->Get('login', 'sign_me_auto', SignMeType::DefaultOff->value)];
}
}
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php
index 1153e9bee..487cafb2d 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php
@@ -1012,7 +1012,7 @@ trait Messages
}
if (!empty($this->GetActionParam('markAsImportant', 0))) {
- $oMessage->SetPriority(\MailSo\Mime\Enumerations\MessagePriority::HIGH);
+ $oMessage->SetPriority(\MailSo\Mime\Enumerations\MessagePriority::Highest);
}
$oMessage->SetSubject($this->GetActionParam('subject', ''));
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php
index a60783edd..72e0cfda3 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php
@@ -160,11 +160,11 @@ trait User
return \min(100, \max(10, $iValue));
});
- $this->setSettingsFromParams($oSettings, 'Layout', 'int', function ($iValue) {
- return (int) (\in_array((int) $iValue, array(\RainLoop\Enumerations\Layout::NO_PREVIEW,
- \RainLoop\Enumerations\Layout::SIDE_PREVIEW, \RainLoop\Enumerations\Layout::BOTTOM_PREVIEW)) ?
- $iValue : \RainLoop\Enumerations\Layout::SIDE_PREVIEW);
- });
+ $this->setSettingsFromParams($oSettings, 'Layout', 'int',
+ fn(int $iValue) => (
+ \RainLoop\Enumerations\Layout::tryFrom($iValue) ?: \RainLoop\Enumerations\Layout::SIDE_PREVIEW
+ )->value
+ );
$this->setSettingsFromParams($oSettings, 'EditorDefaultType', 'string');
$this->setSettingsFromParams($oSettings, 'editorWysiwyg', 'string');
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php
index 87478571c..adc04b97d 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php
@@ -300,7 +300,7 @@ When this value is gethostname, the gethostname() value is used.
'determine_user_language' => array(true, 'Detect language from browser header `Accept-Language`'),
'determine_user_domain' => array(false, 'Like default_domain but then HTTP_HOST/SERVER_NAME without www.'),
- 'sign_me_auto' => array(\RainLoop\Enumerations\SignMeType::DefaultOff,
+ 'sign_me_auto' => array(\RainLoop\Enumerations\SignMeType::DefaultOff->value,
'This option allows webmail to remember the logged in user
once they closed the browser window.
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Layout.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Layout.php
index 0fe236ae7..44840dfbd 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Layout.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Layout.php
@@ -2,9 +2,9 @@
namespace RainLoop\Enumerations;
-abstract class Layout
+enum Layout: int
{
- const NO_PREVIEW = 0;
- const SIDE_PREVIEW = 1;
- const BOTTOM_PREVIEW = 2;
+ case NO_PREVIEW = 0;
+ case SIDE_PREVIEW = 1;
+ case BOTTOM_PREVIEW = 2;
}
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php
index bb50c4345..0e8b3b0f6 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php
@@ -2,9 +2,9 @@
namespace RainLoop\Enumerations;
-abstract class SignMeType
+enum SignMeType: string
{
- const DefaultOff = 'DefaultOff';
- const DefaultOn = 'DefaultOn';
- const Unused = 'Unused';
+ case DefaultOff = 'DefaultOff';
+ case DefaultOn = 'DefaultOn';
+ case Unused = 'Unused';
}
From 913d7a8b32f5ef282235d1ec229667659228f7a1 Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 03:13:39 +0100
Subject: [PATCH 175/181] Improceved IDN emailaddress handling
---
.../0.0.0/app/libraries/MailSo/Mime/Email.php | 23 +++++++++++--------
.../libraries/RainLoop/Actions/Accounts.php | 7 +++---
.../RainLoop/Actions/AdminDomains.php | 2 +-
.../libraries/RainLoop/Actions/Messages.php | 2 +-
.../app/libraries/RainLoop/Actions/Pgp.php | 1 +
.../app/libraries/RainLoop/Actions/SMime.php | 1 +
.../libraries/RainLoop/Actions/UserAuth.php | 14 ++++++-----
.../app/libraries/RainLoop/Model/Account.php | 10 ++++++++
.../Providers/AddressBook/PdoAddressBook.php | 6 ++---
9 files changed, 42 insertions(+), 24 deletions(-)
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php
index 246bc40c5..002820bf0 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php
@@ -48,7 +48,7 @@ class Email implements \JsonSerializable
/**
* @throws \ValueError
*/
- public static function Parse(string $sEmailAddress) : self
+ public static function Parse(string $sEmailAddress): self
{
$sEmailAddress = Utils::Trim(Utils::DecodeHeaderValue($sEmailAddress));
if (!\strlen(\trim($sEmailAddress))) {
@@ -153,32 +153,37 @@ class Email implements \JsonSerializable
return new self($sEmail, $sName);
}
- public function GetEmail(bool $bUtf8 = false) : string
+ public function GetEmail(bool $bUtf8 = false): string
{
return $bUtf8 ? \SnappyMail\IDN::emailToUtf8($this->sEmail) : $this->sEmail;
}
- public function GetDisplayName() : string
+ public function GetEmailUtf8(): string
+ {
+ return \SnappyMail\IDN::emailToUtf8($this->sEmail);
+ }
+
+ public function GetDisplayName(): string
{
return $this->sDisplayName;
}
- public function getLocalPart() : string
+ public function getLocalPart(): string
{
return Utils::getEmailAddressLocalPart($this->sEmail);
}
- public function GetDomain(bool $bIdn = false) : string
+ public function GetDomain(bool $bIdn = false): string
{
return Utils::getEmailAddressDomain($this->GetEmail($bIdn));
}
- public function SetDkimStatus(string $sDkimStatus)
+ public function SetDkimStatus(string $sDkimStatus): void
{
$this->sDkimStatus = Enumerations\DkimStatus::normalizeValue($sDkimStatus);
}
- public function ToString(bool $bConvertSpecialsName = false, bool $bUtf8 = false) : string
+ public function ToString(bool $bConvertSpecialsName = false, bool $bUtf8 = false): string
{
$sReturn = '';
if (\strlen($this->sEmail)) {
@@ -195,7 +200,7 @@ class Email implements \JsonSerializable
return $sReturn;
}
- public function __toString() : string
+ public function __toString(): string
{
return $this->ToString();
}
@@ -206,7 +211,7 @@ class Email implements \JsonSerializable
return array(
'@Object' => 'Object/Email',
'name' => Utils::Utf8Clear($this->sDisplayName),
- 'email' => Utils::Utf8Clear($this->GetEmail(true)),
+ 'email' => Utils::Utf8Clear($this->GetEmailUtf8()),
'dkimStatus' => $this->sDkimStatus
);
}
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php
index 4e78236e9..a15ba8307 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php
@@ -81,7 +81,7 @@ trait Accounts
$oMainAccount = $this->getMainAccountFromToken();
$aAccounts = $this->GetAccounts($oMainAccount);
- $sEmail = \trim($this->GetActionParam('email', ''));
+ $sEmail = IDN::emailToAscii(\trim($this->GetActionParam('email', '')));
$oPassword = new \SnappyMail\SensitiveString($this->GetActionParam('password', ''));
$bNew = !empty($this->GetActionParam('new', 1));
@@ -176,8 +176,7 @@ trait Accounts
return $this->FalseResponse();
}
- $sEmailToDelete = \trim($this->GetActionParam('emailToDelete', ''));
- $sEmailToDelete = IDN::emailToAscii($sEmailToDelete);
+ $sEmailToDelete = IDN::emailToAscii(\trim($this->GetActionParam('emailToDelete', '')));
$aAccounts = $this->GetAccounts($oMainAccount);
@@ -252,7 +251,7 @@ trait Accounts
*/
public function DoAccountSwitch(): array
{
- if ($this->switchAccount(\trim($this->GetActionParam('Email', '')))) {
+ if ($this->switchAccount(new \MailSo\Mime\Email($this->GetActionParam('Email', '')))) {
$oAccount = $this->getAccountFromToken();
$aResult = $this->getAccountData($oAccount);
// $this->Plugins()->InitAppData($bAdmin, $aResult, $oAccount);
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php
index bb037054b..ad7c647b9 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php
@@ -64,7 +64,7 @@ trait AdminDomains
public function DoAdminDomainMatch() : array
{
$sCredentials = $this->resolveLoginCredentials(
- $this->GetActionParam('username'),
+ new \MailSo\Mime\Email($this->GetActionParam('username')),
new \SnappyMail\SensitiveString('********')
);
$sEmail = $sCredentials['email'];
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php
index 487cafb2d..d7453b2a3 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php
@@ -335,7 +335,7 @@ trait Messages
$oToCollection = $oMessage->GetTo();
if ($oToCollection) {
foreach ($oToCollection as /* @var $oEmail \MailSo\Mime\Email */ $oEmail) {
- $aArrayToFrec[$oEmail->GetEmail(true)] = $oEmail->ToString(false, true);
+ $aArrayToFrec[$oEmail->GetEmailUtf8()] = $oEmail->ToString(false, true);
}
}
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php
index a87b6d1da..59dc6a13f 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php
@@ -198,6 +198,7 @@ trait Pgp
$sKey = $this->GetActionParam('key', '');
$sKeyId = $this->GetActionParam('keyId', '');
$sEmail = $this->GetActionParam('email', '');
+// $sEmail = \SnappyMail\IDN::emailToAscii($this->GetActionParam('email', ''));
if (!$sKey) {
try {
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php
index 124f3c974..b835126a8 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php
@@ -62,6 +62,7 @@ trait SMime
$cert = new Certificate();
$cert->distinguishedName['commonName'] = $this->GetActionParam('name', '') ?: $oAccount->Name();
$cert->distinguishedName['emailAddress'] = $this->GetActionParam('email', '') ?: $oAccount->Email();
+// $cert->distinguishedName['emailAddress'] = \SnappyMail\IDN::emailToAscii($this->GetActionParam('email', '') ?: $oAccount->Email());
$result = $cert->createSelfSigned($oPassphrase, $this->GetActionParam('privateKey', ''));
return $this->DefaultResponse($result ?: false);
}
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php
index be26a590e..5dfb90577 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php
@@ -2,6 +2,7 @@
namespace RainLoop\Actions;
+use MailSo\Mime\Email;
use RainLoop\Enumerations\Capa;
use RainLoop\Notifications;
use RainLoop\Utils;
@@ -33,9 +34,9 @@ trait UserAuth
/**
* @throws \RainLoop\Exceptions\ClientException
*/
- protected function resolveLoginCredentials(string $sEmail, SensitiveString $oPassword): array
+ protected function resolveLoginCredentials(Email $oEmail, SensitiveString $oPassword): array
{
- $sEmail = \SnappyMail\IDN::emailToAscii(\MailSo\Base\Utils::Trim($sEmail));
+ $sEmail = $oEmail->GetEmail();
$sNewEmail = $sEmail;
$this->Plugins()->RunHook('login.credentials.step-1', array(&$sNewEmail));
@@ -139,9 +140,10 @@ trait UserAuth
/**
* @throws \RainLoop\Exceptions\ClientException
*/
+// public function LoginProcess(Email $sEmail, SensitiveString $oPassword, bool $bMainAccount = true): Account
public function LoginProcess(string $sEmail, SensitiveString $oPassword, bool $bMainAccount = true): Account
{
- $aCredentials = $this->resolveLoginCredentials($sEmail, $oPassword);
+ $aCredentials = $this->resolveLoginCredentials(new Email($sEmail), $oPassword);
if (!\str_contains($aCredentials['email'], '@') || !\strlen($oPassword)) {
throw new ClientException(Notifications::InvalidInputArgument);
@@ -184,14 +186,14 @@ trait UserAuth
return $oAccount;
}
- public function switchAccount(string $sEmail) : bool
+ public function switchAccount(Email $oEmail): bool
{
+ $sEmail = $oEmail->GetEmail();
$this->Http()->ServerNoCache();
$oMainAccount = $this->getMainAccountFromToken(false);
if ($sEmail && $oMainAccount && $this->GetCapa(Capa::ADDITIONAL_ACCOUNTS)) {
$oAccount = null;
- if ($oMainAccount->Email() !== $sEmail) {
- $sEmail = \SnappyMail\IDN::emailToAscii($sEmail);
+ if ($oMainAccount->EmailAscii() !== $sEmail) {
$aAccounts = $this->GetAccounts($oMainAccount);
if (!isset($aAccounts[$sEmail])) {
throw new ClientException(Notifications::AccountDoesNotExist);
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php
index 792cf6030..cc8fb0a95 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php
@@ -28,6 +28,16 @@ abstract class Account implements \JsonSerializable
return $this->sEmail;
}
+ public function EmailAscii(): string
+ {
+ return \SnappyMail\IDN::emailToAscii($this->sEmail);
+ }
+
+ public function EmailUtf8(): string
+ {
+ return \SnappyMail\IDN::emailToUtf8($this->sEmail);
+ }
+
public function Name(): string
{
return $this->sName;
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php
index 00e2c18fd..ed0009b0d 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php
@@ -1022,7 +1022,7 @@ class PdoAddressBook
$aEmailsToCreate = \array_filter($aEmailsObjects, function ($oItem) use ($aExists, &$aEmailsToUpdate) {
if ($oItem) {
- $sEmail = \trim($oItem->GetEmail(true));
+ $sEmail = $oItem->GetEmailUtf8();
if (\strlen($sEmail)) {
$aEmailsToUpdate[] = $sEmail;
return !\in_array(\mb_strtolower($sEmail), $aExists);
@@ -1033,7 +1033,7 @@ class PdoAddressBook
} else {
foreach ($aEmailsObjects as $oItem) {
if ($oItem) {
- $sEmailUpdate = \trim($oItem->GetEmail(true));
+ $sEmailUpdate = $oItem->GetEmailUtf8();
if (\strlen($sEmailUpdate)) {
$aEmailsToUpdate[] = $sEmailUpdate;
}
@@ -1048,7 +1048,7 @@ class PdoAddressBook
$oVCard = new VCard;
$bValid = false;
if ('' !== \trim($oEmail->GetEmail())) {
- $oVCard->add('EMAIL', \trim($oEmail->GetEmail(true)));
+ $oVCard->add('EMAIL', $oEmail->GetEmailUtf8());
$bValid = true;
}
if ('' !== \trim($oEmail->GetDisplayName())) {
From 6cd36e075206ae06fba4fe4aa8b2f9c6ff5f0a38 Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 03:58:26 +0100
Subject: [PATCH 176/181] Added Email::GetEmailUtf8()
---
.../v/0.0.0/app/libraries/MailSo/Mime/Email.php | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php
index 002820bf0..18895a85e 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php
@@ -155,7 +155,7 @@ class Email implements \JsonSerializable
public function GetEmail(bool $bUtf8 = false): string
{
- return $bUtf8 ? \SnappyMail\IDN::emailToUtf8($this->sEmail) : $this->sEmail;
+ return $this->sEmail;
}
public function GetEmailUtf8(): string
@@ -173,9 +173,14 @@ class Email implements \JsonSerializable
return Utils::getEmailAddressLocalPart($this->sEmail);
}
- public function GetDomain(bool $bIdn = false): string
+ public function GetDomain(): string
{
- return Utils::getEmailAddressDomain($this->GetEmail($bIdn));
+ return Utils::getEmailAddressDomain($this->GetEmail());
+ }
+
+ public function GetDomainUtf8(): string
+ {
+ return Utils::getEmailAddressDomain($this->GetEmailUtf8());
}
public function SetDkimStatus(string $sDkimStatus): void
@@ -187,7 +192,7 @@ class Email implements \JsonSerializable
{
$sReturn = '';
if (\strlen($this->sEmail)) {
- $sReturn = $this->GetEmail($bUtf8);
+ $sReturn = $bUtf8 ? $this->GetEmailUtf8() : $this->GetEmail();
$sDisplayName = $this->sDisplayName;
if (\strlen($sDisplayName)) {
$sDisplayName = \str_replace('"', '\"', $sDisplayName);
From b02b28f26b10109ed4bb91e5aee28d180cf092a1 Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 03:58:46 +0100
Subject: [PATCH 177/181] dkim use str_contains instead of strpos
---
snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php
index 1c945bae3..71b4acd97 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php
@@ -299,7 +299,7 @@ class Message implements \JsonSerializable
foreach ($oMessage->oFrom as $oEmail) {
$sEmail = $oEmail->GetEmail();
foreach ($aAuth['dkim'] as $aDkimData) {
- if (\strpos($sEmail, $aDkimData[1])) {
+ if (\str_contains($sEmail, $aDkimData[1])) {
$oEmail->SetDkimStatus($aDkimData[0]);
}
}
From bcd9657ce47d913b1cda5ee4af6402f9c5b994e2 Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 04:33:25 +0100
Subject: [PATCH 178/181] Better BackedEnum handling
---
snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php | 6 +-----
.../app/libraries/RainLoop/Enumerations/SignMeType.php | 10 ++++++++++
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php
index 8d7140d7f..d5e3b2472 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php
@@ -735,11 +735,7 @@ class Actions
$aResult['DevPassword'] = '';
}
- $aResult['signMe'] = [
- SignMeType::DefaultOff->value => 0,
- SignMeType::DefaultOn->value => 1,
- SignMeType::Unused->value => 2
- ][(string) $oConfig->Get('login', 'sign_me_auto', SignMeType::DefaultOff->value)];
+ $aResult['signMe'] = (SignMeType::tryFrom($oConfig->Get('login', 'sign_me_auto', '')) ?: SignMeType::DefaultOff)->toInt();
}
}
diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php
index 0e8b3b0f6..8aa99eed8 100644
--- a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php
+++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php
@@ -7,4 +7,14 @@ enum SignMeType: string
case DefaultOff = 'DefaultOff';
case DefaultOn = 'DefaultOn';
case Unused = 'Unused';
+
+ public function toInt(): int
+ {
+ return \array_search($this, self::cases(), true);
+ return match($this) {
+ SignMeType::DefaultOff => 0,
+ SignMeType::DefaultOn => 1,
+ SignMeType::Unused => 2
+ };
+ }
}
From 75303886d338ecf7115509e364336909bb345442 Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 04:51:05 +0100
Subject: [PATCH 179/181] abstract class ResponseStatus to BackedEnum
---
.../MailSo/Imap/Enumerations/ResponseStatus.php | 12 ++++++------
.../v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php | 4 ++--
.../v/0.0.0/app/libraries/MailSo/Imap/Response.php | 6 +++++-
.../app/libraries/MailSo/Imap/ResponseCollection.php | 2 +-
4 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseStatus.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseStatus.php
index be00915a8..b96a379eb 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseStatus.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/ResponseStatus.php
@@ -16,11 +16,11 @@ namespace MailSo\Imap\Enumerations;
* @package Imap
* @subpackage Enumerations
*/
-abstract class ResponseStatus
+enum ResponseStatus: string
{
- const OK = 'OK';
- const NO = 'NO';
- const BAD = 'BAD';
- const BYE = 'BYE';
- const PREAUTH = 'PREAUTH';
+ case OK = 'OK';
+ case NO = 'NO';
+ case BAD = 'BAD';
+ case BYE = 'BYE';
+ case PREAUTH = 'PREAUTH';
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php
index 3c0ea7827..bb060f85a 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php
@@ -503,8 +503,8 @@ class ImapClient extends \MailSo\Net\NetClient
if ($oResponse->IsStatusResponse
&& ResponseType::UNTAGGED === $oResponse->ResponseType
- && Enumerations\ResponseStatus::PREAUTH === $oResponse->StatusOrIndex
-// && (Enumerations\ResponseStatus::PREAUTH === $oResponse->StatusOrIndex || Enumerations\ResponseStatus::BYE === $oResponse->StatusOrIndex)
+ && Enumerations\ResponseStatus::PREAUTH === $oResponse->ResponseStatus
+// && (Enumerations\ResponseStatus::PREAUTH === $oResponse->ResponseStatus || Enumerations\ResponseStatus::BYE === $oResponse->ResponseStatus)
) {
break;
}
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php
index a4fec9eb9..3086b412d 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Response.php
@@ -11,6 +11,7 @@
namespace MailSo\Imap;
+use MailSo\Imap\Enumerations\ResponseStatus;
use MailSo\Imap\Enumerations\ResponseType;
/**
@@ -29,6 +30,8 @@ class Response
public bool $IsStatusResponse = false;
+ public ?ResponseStatus $ResponseStatus = null;
+
public ResponseType $ResponseType = ResponseType::UNKNOWN;
public ?string $Tag = null;
@@ -46,7 +49,8 @@ class Response
{
$value = \strtoupper($value);
$this->StatusOrIndex = $value;
- $this->IsStatusResponse = \defined("\\MailSo\\Imap\\Enumerations\\ResponseStatus::{$value}");
+ $this->ResponseStatus = ResponseStatus::tryFrom($value);
+ $this->IsStatusResponse = !!$this->ResponseStatus;
}
public function setTag(string $value) : void
diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php
index ba83a0247..188c7043e 100644
--- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php
+++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php
@@ -42,7 +42,7 @@ class ResponseCollection extends \MailSo\Base\Collection
throw new Exceptions\InvalidResponseException($this);
}
- if (Enumerations\ResponseStatus::OK !== $oItem->StatusOrIndex) {
+ if (Enumerations\ResponseStatus::OK !== $oItem->ResponseStatus) {
throw new Exceptions\NegativeResponseException($this);
}
}
From 804df33544dbee71db865f4f61ccb0e68106433a Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 16:10:14 +0100
Subject: [PATCH 180/181] Bugfix undefined $aSri
---
.../v/0.0.0/app/libraries/snappymail/integrity.php | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php b/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php
index 6f0ff3447..d193efbdd 100644
--- a/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php
+++ b/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php
@@ -128,11 +128,11 @@ abstract class Integrity
private static $aSri;
public static function loadSriHashes(): array
{
- if (!\is_array($aSri)) {
+ if (!\is_array(static::$aSri)) {
$sSriPath = APP_VERSION_ROOT_PATH . 'static/sri.json';
- $aSri = \is_readable($sSriPath) ? \json_decode(\file_get_contents($sSriPath), true) : [];
- if (!$aSri) {
- $aSri = [
+ static::$aSri = \is_readable($sSriPath) ? \json_decode(\file_get_contents($sSriPath), true) : [];
+ if (!static::$aSri) {
+ static::$aSri = [
'libs.min.js' => \hash_file('sha384', APP_VERSION_ROOT_PATH . 'static/js/min/libs.min.js'),
'admin.min.js' => \hash_file('sha384', APP_VERSION_ROOT_PATH . 'static/js/min/admin.min.js'),
'app.min.js' => \hash_file('sha384', APP_VERSION_ROOT_PATH . 'static/js/min/app.min.js'),
@@ -141,6 +141,6 @@ abstract class Integrity
];
}
}
- return $aSri;
+ return static::$aSri;
}
}
From 02e059c360972c42583d470279d5fd33e2a203ae Mon Sep 17 00:00:00 2001
From: the-djmaze <3752035+the-djmaze@users.noreply.github.com>
Date: Mon, 2 Nov 2026 16:10:34 +0100
Subject: [PATCH 181/181] aSri
---
snappymail/v/0.0.0/app/libraries/snappymail/integrity.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php b/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php
index d193efbdd..0abf9dab4 100644
--- a/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php
+++ b/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php
@@ -125,7 +125,7 @@ abstract class Integrity
}
// https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity
- private static $aSri;
+ private static ?array $aSri = null;
public static function loadSriHashes(): array
{
if (!\is_array(static::$aSri)) {