Skip to content

Commit

Permalink
Merge branch 'stable2.2' into bug/1037/iconv_as_fallback_for_mb_conve…
Browse files Browse the repository at this point in the history
…rt_encoding
  • Loading branch information
Astinus-Eberhard authored Aug 2, 2023
2 parents a47ca70 + 9c41fcd commit 46947cb
Show file tree
Hide file tree
Showing 24 changed files with 187 additions and 147 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Download schema
run: wget https://apps.nextcloud.com/schema/apps/info.xsd
- name: Lint info.xml
uses: ChristophWurst/xmllint-action@v1
uses: ChristophWurst/xmllint-action@39155a91429af431d65fafc21fa52ba5c4f5cb71 # v1.1
with:
xml-file: ./appinfo/info.xml
xml-schema-file: ./info.xsd
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# Changelog
All notable changes to this project will be documented in this file.

## 2.2.7 - 2023-06-28
### Fixed
- Truncate email addresses that are more than 244 characters long
- Check strict cookies for image proxy
- Only fetch mailbox STATUS once
- Missing background box for redirect page

## 2.2.6 - 2023-04-20
### Fixed
- Gap between primary actions
- Button style
- Clear config cache after every mutation
- Chunk UIDs by string length

## 2.2.5 - 2023-03-23
### Fixed
- Validate favicon hosts
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
- **🙈 We’re not reinventing the wheel!** Based on the great [Horde](https://horde.org) libraries.
- **📬 Want to host your own mail server?** We do not have to reimplement this as you could set up [Mail-in-a-Box](https://mailinabox.email)!
]]></description>
<version>2.2.5</version>
<version>2.2.7</version>
<licence>agpl</licence>
<author>Greta Doçi</author>
<author homepage="https://github.com/nextcloud/groupware">Nextcloud Groupware Team</author>
Expand Down
7 changes: 7 additions & 0 deletions lib/Controller/ProxyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use OCP\IURLGenerator;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Log\LoggerInterface;
use function file_get_contents;

class ProxyController extends Controller {
private IURLGenerator $urlGenerator;
Expand Down Expand Up @@ -105,6 +106,12 @@ public function proxy(string $src): ProxyDownloadResponse {
// close the session to allow parallel downloads
$this->session->close();

// If strict cookies are set it means we come from the same domain so no open redirect
if (!$this->request->passesStrictCookieCheck()) {
$content = file_get_contents(__DIR__ . '/../../img/blocked-image.png');
return new ProxyDownloadResponse($content, $src, 'application/octet-stream');
}

$client = $this->clientService->newClient();
try {
$response = $client->get($src);
Expand Down
2 changes: 1 addition & 1 deletion lib/Db/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ public function insertBulk(Account $account, Message ...$messages): void {
$qb2->setParameter('message_id', $messageId, IQueryBuilder::PARAM_INT);
$qb2->setParameter('type', $type, IQueryBuilder::PARAM_INT);
$qb2->setParameter('label', mb_strcut($recipient->getLabel(), 0, 255), IQueryBuilder::PARAM_STR);
$qb2->setParameter('email', $recipient->getEmail(), IQueryBuilder::PARAM_STR);
$qb2->setParameter('email', mb_strcut($recipient->getEmail(), 0, 255), IQueryBuilder::PARAM_STR);

$qb2->execute();
}
Expand Down
8 changes: 6 additions & 2 deletions lib/Folder.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,16 @@ class Folder {
/** @var string[] */
private $specialUse;

public function __construct(int $accountId, Horde_Imap_Client_Mailbox $mailbox, array $attributes, ?string $delimiter) {
public function __construct(int $accountId,
Horde_Imap_Client_Mailbox $mailbox,
array $attributes,
?string $delimiter,
array $status) {
$this->accountId = $accountId;
$this->mailbox = $mailbox;
$this->attributes = $attributes;
$this->delimiter = $delimiter;
$this->status = [];
$this->status = $status;
$this->specialUse = [];
}

Expand Down
52 changes: 12 additions & 40 deletions lib/IMAP/FolderMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,21 @@ public function getFolders(Account $account, Horde_Imap_Client_Socket $client,
'delimiter' => true,
'attributes' => true,
'special_use' => true,
'status' => Horde_Imap_Client::STATUS_ALL,
]);

return array_filter(array_map(function (array $mailbox) use ($account, $client) {
return array_filter(array_map(static function (array $mailbox) use ($account) {
if (in_array($mailbox['mailbox']->utf8, self::DOVECOT_SIEVE_FOLDERS, true)) {
// This is a special folder that must not be shown
return null;
}

try {
$client->status($mailbox["mailbox"]);
} catch (Horde_Imap_Client_Exception $e) {
// ignore folders which cause errors on access
// (i.e. server-side system I/O errors)
if (in_array($e->getCode(), [
Horde_Imap_Client_Exception::UNSPECIFIED,
], true)) {
return null;
}
}

return new Folder(
$account->getId(),
$mailbox['mailbox'],
$mailbox['attributes'],
$mailbox['delimiter']
$mailbox['delimiter'],
$mailbox['status'],
);
}, $mailboxes));
}
Expand All @@ -97,39 +87,21 @@ public function createFolder(Horde_Imap_Client_Socket $client,
'delimiter' => true,
'attributes' => true,
'special_use' => true,
'status' => Horde_Imap_Client::STATUS_ALL,
]);
$mb = reset($list);

if ($mb === null) {
throw new ServiceException("Created mailbox does not exist");
}

return new Folder($account->getId(), $mb['mailbox'], $mb['attributes'], $mb['delimiter']);
}

/**
* @param Folder[] $folders
* @param Horde_Imap_Client_Socket $client
*
* @throws Horde_Imap_Client_Exception
*
* @return void
*/
public function getFoldersStatus(array $folders,
Horde_Imap_Client_Socket $client): void {
$mailboxes = array_map(function (Folder $folder) {
return $folder->getMailbox();
}, array_filter($folders, function (Folder $folder) {
return !in_array('\noselect', $folder->getAttributes());
}));

$status = $client->status($mailboxes);

foreach ($folders as $folder) {
if (isset($status[$folder->getMailbox()])) {
$folder->setStatus($status[$folder->getMailbox()]);
}
}
return new Folder(
$account->getId(),
$mb['mailbox'],
$mb['attributes'],
$mb['delimiter'],
$mb['status'],
);
}

/**
Expand Down
1 change: 0 additions & 1 deletion lib/IMAP/MailboxSync.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ public function sync(Account $account,

try {
$folders = $this->folderMapper->getFolders($account, $client);
$this->folderMapper->getFoldersStatus($folders, $client);
} catch (Horde_Imap_Client_Exception $e) {
throw new ServiceException(
sprintf("IMAP error synchronizing account %d: %s", $account->getId(), $e->getMessage()),
Expand Down
16 changes: 8 additions & 8 deletions lib/IMAP/Sync/Synchronizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
use OCA\Mail\Exception\UidValidityChangedException;
use OCA\Mail\Exception\MailboxDoesNotSupportModSequencesException;
use OCA\Mail\IMAP\MessageMapper;
use function array_chunk;
use function array_merge;
use function OCA\Mail\chunk_uid_sequence;

class Synchronizer {
/**
Expand All @@ -43,7 +43,7 @@ class Synchronizer {
* shown to cause IMAP errors for some accounts where the UID list can't be
* compressed much by Horde.
*/
private const UID_CHUNK_SIZE = 10000;
private const UID_CHUNK_MAX_BYTES = 10000;

/** @var MessageMapper */
private $messageMapper;
Expand Down Expand Up @@ -137,13 +137,13 @@ private function getChangedMessageUids(Horde_Imap_Client_Base $imapClient, Horde
return array_merge(
[], // for php<7.4 https://www.php.net/manual/en/function.array-merge.php
...array_map(
function (array $uids) use ($imapClient, $mailbox, $request) {
function (Horde_Imap_Client_Ids $uids) use ($imapClient, $mailbox, $request) {
return $imapClient->sync($mailbox, $request->getToken(), [
'criteria' => Horde_Imap_Client::SYNC_FLAGSUIDS,
'ids' => new Horde_Imap_Client_Ids($uids),
'ids' => $uids,
])->flagsuids->ids;
},
array_chunk($request->getUids(), self::UID_CHUNK_SIZE)
chunk_uid_sequence($request->getUids(), self::UID_CHUNK_MAX_BYTES)
)
);
}
Expand All @@ -167,13 +167,13 @@ private function getVanishedMessageUids(Horde_Imap_Client_Base $imapClient, Hord
$vanishedUids = array_merge(
[], // for php<7.4 https://www.php.net/manual/en/function.array-merge.php
...array_map(
function (array $uids) use ($imapClient, $mailbox, $request) {
function (Horde_Imap_Client_Ids $uids) use ($imapClient, $mailbox, $request) {
return $imapClient->sync($mailbox, $request->getToken(), [
'criteria' => Horde_Imap_Client::SYNC_VANISHEDUIDS,
'ids' => new Horde_Imap_Client_Ids($uids),
'ids' => $uids,
])->vanisheduids->ids;
},
array_chunk($request->getUids(), self::UID_CHUNK_SIZE)
chunk_uid_sequence($request->getUids(), self::UID_CHUNK_MAX_BYTES)
)
);
return $vanishedUids;
Expand Down
1 change: 0 additions & 1 deletion lib/Service/MailManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ public function createMailbox(Account $account, string $name): Mailbox {
$client = $this->imapClientFactory->getClient($account);
try {
$folder = $this->folderMapper->createFolder($client, $account, $name);
$this->folderMapper->getFoldersStatus([$folder], $client);
} catch (Horde_Imap_Client_Exception $e) {
throw new ServiceException(
"Could not get mailbox status: " .
Expand Down
22 changes: 18 additions & 4 deletions lib/Service/Provisioning/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
use Psr\Log\LoggerInterface;

class Manager {
public const MAIL_PROVISIONINGS = 'mail_provisionings';
/** @var IUserManager */
private $userManager;

Expand Down Expand Up @@ -96,8 +97,8 @@ public function getConfigById(int $provisioningId): ?Provisioning {

public function getConfigs(): array {
$cache = null;
if ($this->cacheFactory->isLocalCacheAvailable()) {
$cache = $this->cacheFactory->createLocal('provisionings');
if ($this->cacheFactory->isAvailable()) {
$cache = $this->cacheFactory->createDistributed(self::MAIL_PROVISIONINGS);
$cached = $cache->get('provisionings_all');
if ($cached !== null) {
return unserialize($cached, ['allowed_classes' => [Provisioning::class]]);
Expand All @@ -106,7 +107,7 @@ public function getConfigs(): array {

$provisionings = $this->provisioningMapper->getAll();
// let's cache the provisionings for 5 minutes
if ($this->cacheFactory->isLocalCacheAvailable()) {
if ($cache !== null) {
$cache->set('provisionings_all', serialize($provisionings), 60 * 5);
}
return $provisionings;
Expand Down Expand Up @@ -254,7 +255,12 @@ public function provisionSingleUser(array $provisionings, IUser $user): bool {
*/
public function newProvisioning(array $data): Provisioning {
$provisioning = $this->provisioningMapper->validate($data);
return $this->provisioningMapper->insert($provisioning);
$provisioning = $this->provisioningMapper->insert($provisioning);
if ($this->cacheFactory->isAvailable()) {
$cache = $this->cacheFactory->createDistributed(self::MAIL_PROVISIONINGS);
$cache->clear();
}
return $provisioning;
}

/**
Expand All @@ -264,6 +270,10 @@ public function newProvisioning(array $data): Provisioning {
public function updateProvisioning(array $data): void {
$provisioning = $this->provisioningMapper->validate($data);
$this->provisioningMapper->update($provisioning);
if ($this->cacheFactory->isAvailable()) {
$cache = $this->cacheFactory->createDistributed(self::MAIL_PROVISIONINGS);
$cache->clear();
}
}

private function updateAccount(IUser $user, MailAccount $account, Provisioning $config): MailAccount {
Expand Down Expand Up @@ -300,6 +310,10 @@ private function updateAccount(IUser $user, MailAccount $account, Provisioning $
public function deprovision(Provisioning $provisioning): void {
$this->mailAccountMapper->deleteProvisionedAccounts($provisioning->getId());
$this->provisioningMapper->delete($provisioning);
if ($this->cacheFactory->isAvailable()) {
$cache = $this->cacheFactory->createDistributed(self::MAIL_PROVISIONINGS);
$cache->clear();
}
}

public function updatePassword(IUser $user, string $password): void {
Expand Down
1 change: 1 addition & 0 deletions lib/Service/Search/FilterStringParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ private function parseFilterToken(SearchQuery $query, string $token): bool {
'answered' => Flag::is(Flag::ANSWERED),
'read' => Flag::is(Flag::SEEN),
'unread' => Flag::not(Flag::SEEN),
'starred' => Flag::is(Flag::FLAGGED),
'important' => Flag::is(Flag::IMPORTANT),
'is_important' => FlagExpression::and(
Flag::is(Flag::IMPORTANT)
Expand Down
24 changes: 24 additions & 0 deletions lib/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@

namespace OCA\Mail;

use Horde_Imap_Client_Ids;
use function array_slice;
use function array_splice;
use function max;
use function strlen;

function array_flat_map(callable $map, array $data): array {
return array_merge([], ...array_map($map, $data));
}

/**
* @param int[] $uids
* @return Horde_Imap_Client_Ids[]
*/
function chunk_uid_sequence(array $uids, int $bytes): array {
$chunks = [];
while (!empty($uids)) {
$take = count($uids);
while (strlen((new Horde_Imap_Client_Ids(array_slice($uids, 0, $take)))->tostring) >= $bytes) {
$take = (int)($take * 0.75);
}
$chunks[] = new Horde_Imap_Client_Ids(
array_splice($uids, 0, max($take, 1))
);
}
return $chunks;
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "nextcloud-mail",
"description": "Nextcloud Mail",
"version": "2.2.5",
"version": "2.2.7",
"author": "Christoph Wurst <christoph@winzerhof-wurst.at>",
"license": "agpl",
"private": true,
Expand Down
1 change: 1 addition & 0 deletions src/components/Composer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,7 @@ export default {
display: flex;
flex-direction: row;
padding: 12px;
gap: 5px;
}
.composer-actions--primary-actions .button {
padding: 2px;
Expand Down
2 changes: 1 addition & 1 deletion src/components/SieveFilterForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{{ errorMessage }}
</p>
<ButtonVue
class="primary"
type="primary"
:disabled="loading"
@click="saveActiveScript">
<template #icon>
Expand Down
2 changes: 1 addition & 1 deletion templates/redirect.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
</p>
</div>
<?php else: ?>
<div class="update">
<div class="update guest-box">
<h2><?php p($l->t('Redirect')); ?></h2>
<p><?php p($l->t('The link leads to %s', [$_['urlHost']])); ?></p>
<p class="infogroup"><?php print_unescaped($l->t('If you do not want to visit that page, you can return to <a href="%s">Mail</a>.',
Expand Down
Loading

0 comments on commit 46947cb

Please sign in to comment.