Merge branch 'dev/sync-nextcloud-addressbook' into draft/dev_sync-nextcloud-addressbook

This commit is contained in:
Fahim Salam Chowdhury 2024-09-03 11:30:33 +06:00
commit 2e3fbe0a12
87 changed files with 3360 additions and 402 deletions

View file

@ -37,7 +37,11 @@ module.exports = {
// Mailvelope
'mailvelope': "readonly",
// Punycode
'IDN': "readonly"
'IDN': "readonly",
// Turndown
'TurndownService': "readonly",
// Marked
'marked': "readonly"
},
// http://eslint.org/docs/rules/
rules: {

View file

@ -1,3 +1,43 @@
## 2.37.3 2024-08-27
### Added
- 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)
- Show GnuPG version for
[#1560](https://github.com/the-djmaze/snappymail/issues/1560)
- Make sure only scalar values are allowed in $_ENV for
[#1560](https://github.com/the-djmaze/snappymail/issues/1560)
- Change minimum new mail check interval
[#1678](https://github.com/the-djmaze/snappymail/issues/1678)
- Sieve editor does not support "index" extension
[#1709](https://github.com/the-djmaze/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)
- Handling of (token) errors due to
[#1706](https://github.com/the-djmaze/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)
- Creating new message impossible as long as a draft exists?
[#1710](https://github.com/the-djmaze/snappymail/issues/1710)
- InvalidToken error at login
[#1706](https://github.com/the-djmaze/snappymail/issues/1706)
### Nextcloud
- Force Nextcloud personal language by default
[#1428](https://github.com/the-djmaze/snappymail/issues/1428)
## 2.37.2 2024-08-13
### Added

View file

@ -140,26 +140,26 @@ RainLoop 1.17 vs SnappyMail
|js/* |RainLoop |Snappy |
|--------------- |--------: |--------: |
|admin.js |2.170.153 | 84.054 |
|app.js |4.207.787 | 441.754 |
|boot.js | 868.735 | 4.147 |
|admin.js |2.170.153 | 84.203 |
|app.js |4.207.787 | 445.359 |
|boot.js | 868.735 | 4.343 |
|libs.js | 658.812 | 193.716 |
|sieve.js | 0 | 84.598 |
|sieve.js | 0 | 84.571 |
|polyfills.js | 334.608 | 0 |
|serviceworker.js | 0 | 285 |
|TOTAL |8.240.095 | 808.554 |
|TOTAL |8.240.095 | 812.477 |
|js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|--------------- |--------: |--------: |------: |------: |--------: |--------: |
|admin.min.js | 256.831 | 41.162 | 73.606 | 13.885 | 60.877 | 12.434 |
|app.min.js | 515.367 | 199.730 |139.456 | 67.669 |110.485 | 57.672 |
|boot.min.js | 84.659 | 2.087 | 26.998 | 1.204 | 23.643 | 1.002 |
|admin.min.js | 256.831 | 41.256 | 73.606 | 13.940 | 60.877 | 12.493 |
|app.min.js | 515.367 | 201.270 |139.456 | 68.096 |110.485 | 58.078 |
|boot.min.js | 84.659 | 2.231 | 26.998 | 1.271 | 23.643 | 1.067 |
|libs.min.js | 584.772 | 92.365 |180.901 | 34.487 |155.182 | 30.830 |
|sieve.min.js | 0 | 41.093 | 0 | 10.325 | 0 | 9.327 |
|sieve.min.js | 0 | 41.288 | 0 | 10.327 | 0 | 9.318 |
|polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 |
|TOTAL user |1.217.635 | 294.182 |358.761 |103.360 |299.485 | 89.504 |
|TOTAL user+sieve |1.217.635 | 335.275 |358.761 |113.685 |299.485 | 98.831 |
|TOTAL admin | 959.099 | 135.614 |292.911 | 49.576 |249.877 | 44.266 |
|TOTAL user |1.217.635 | 295.866 |358.761 |103.854 |299.485 | 89.975 |
|TOTAL user+sieve |1.217.635 | 337.154 |358.761 |114.181 |299.485 | 99.293 |
|TOTAL admin | 959.099 | 135.852 |292.911 | 49.698 |249.877 | 44.390 |
For a user it is around 68% smaller and faster than traditional RainLoop.

View file

@ -469,6 +469,7 @@ export const
else if (detectHiddenImages
&& ((oStyle.maxHeight && 3 > pInt(oStyle.maxHeight)) // TODO: issue with 'in'
|| (oStyle.maxWidth && 3 > pInt(oStyle.maxWidth)) // TODO: issue with 'in'
|| (oStyle.width && 2 > pInt(oStyle.width))
|| [
'email.microsoftemail.com/open',
'github.com/notifications/beacon/',
@ -675,7 +676,9 @@ export const
forEach('a', a => {
let txt = a.textContent, href = a.href;
return a.replaceWith(
txt.trim() == href || href.includes('mailto:') ? txt : txt + ' ' + href + ' '
txt.replace(/[\s()-]+/g, '').includes(href.replace(/^[a-z]:/, '').replace(/[\s()-]+/g, ''))
? txt
: txt + ' ' + href + ' '
);
});

View file

@ -16,6 +16,7 @@ export class AdminSettingsAbout /*extends AbstractViewSettings*/ {
coreWarning: false,
coreVersion: '',
coreVersionCompare: -2,
php64: true,
load1: 0,
load5: 0,
load15: 0,
@ -69,6 +70,7 @@ export class AdminSettingsAbout /*extends AbstractViewSettings*/ {
this.coreWarning(!!data.core.warning);
this.coreVersion(data.core.version || '');
this.coreVersionCompare(data.core.versionCompare);
this.php64(data.php[1].loaded);
} else {
this.coreReal(false);
this.coreWarning(false);

View file

@ -32,6 +32,8 @@ export class AdminSettingsSecurity extends AbstractViewSettings {
capaOpenPGP: SettingsCapa('OpenPGP')
});
this.gnuPGversion = 'GnuPG v' + SettingsGet('gnupg');
const reset = () => {
this.saveError(false);
this.saveSuccess(false);

View file

@ -4,7 +4,7 @@ import { ThemeStore } from 'Stores/Theme';
import { arePopupsVisible } from 'Knoin/Knoin';
export const AppUserStore = {
allowContacts: () => !!SettingsGet('contactsAllowed') && !!SettingsGet('contactsExternal')
allowContacts: () => !!SettingsGet('contactsAllowed')
};
addObservablesTo(AppUserStore, {

View file

@ -1,4 +1,4 @@
This app packages SnappyMail <upstream>2.37.2</upstream>.
This app packages SnappyMail <upstream>2.37.3</upstream>.
SnappyMail is a simple, modern, lightweight & fast web-based email client.

View file

@ -4,7 +4,7 @@ RUN mkdir -p /app/code
WORKDIR /app/code
# If you change the extraction below, be sure to test on scaleway
VERSION=2.37.2
VERSION=2.37.3
RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \
unzip /tmp/snappymail.zip -d /app/code && \
rm /tmp/snappymail.zip && \

View file

@ -3,7 +3,7 @@
<id>snappymail</id>
<name>SnappyMail</name>
<summary>SnappyMail Webmail</summary>
<version>2.37.2</version>
<version>2.37.3</version>
<licence>agpl</licence>
<author>SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli</author>
<description><![CDATA[**Lightweight & fast email client.**

View file

@ -61,11 +61,15 @@ class FetchController extends Controller {
]);
}
SnappyMailHelper::loadApp();
$oConfig = \RainLoop\Api::Config();
if (!empty($_POST['snappymail-app_path'])) {
$oConfig = \RainLoop\Api::Config();
$oConfig->Set('webmail', 'app_path', $_POST['snappymail-app_path']);
$oConfig->Save();
}
$oConfig->Set('webmail', 'allow_languages_on_settings', empty($_POST['snappymail-nc-lang']));
$oConfig->Set('login', 'allow_languages_on_login', empty($_POST['snappymail-nc-lang']));
$oConfig->Save();
if (!empty($_POST['import-rainloop'])) {
return new JSONResponse([
@ -74,7 +78,6 @@ class FetchController extends Controller {
]);
}
SnappyMailHelper::loadApp();
$debug = !empty($_POST['snappymail-debug']);
$oConfig = \RainLoop\Api::Config();
if ($debug != $oConfig->Get('debug', 'enable', false)) {
@ -133,7 +136,7 @@ class FetchController extends Controller {
// TODO: Handle both exceptions separately?
SnappyMailHelper::loadApp();
\RainLoop\Api::Actions()->DoLogout();
return new JSONResponse([
'status' => 'error',
'Message' => $e->getMessage()

View file

@ -54,6 +54,8 @@ 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', 'allow_languages_on_settings', false);
$oConfig->Set('login', 'allow_languages_on_login', false);
$bSave = true;
}

View file

@ -71,6 +71,7 @@ class AdminSettings implements ISettings
$oConfig->Save();
}
$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);

View file

@ -71,6 +71,13 @@
<br />
<?php } ?>
<p>
<input id="snappymail-nc-lang" name="snappymail-nc-lang" type="checkbox" class="checkbox" <?php if ($_['snappymail-nc-lang']) echo 'checked="checked"'; ?>>
<label for="snappymail-nc-lang">
<?php echo($l->t('Force Nextcloud personal language')); ?>
</label>
</p>
<br />
<p>
<label for="snappymail-app_path">
<?php echo($l->t('app_path')); ?>

View file

@ -20,7 +20,7 @@ return "SnappyMail Webmail is a browser-based multilingual IMAP client with an a
# script_snappymail_versions()
sub script_snappymail_versions
{
return ( "2.37.2" );
return ( "2.37.3" );
}
sub script_snappymail_version_desc

View file

@ -3,7 +3,7 @@
"title": "SnappyMail",
"description": "Simple, modern & fast web-based email client",
"private": true,
"version": "2.37.2",
"version": "2.37.3",
"homepage": "https://snappymail.eu",
"author": {
"name": "DJ Maze",

View file

@ -186,6 +186,10 @@
if (msg?.from?.[0]) {
let url = getAvatar(msg),
fn = url=>{element.src = url};
element.onerror = ()=>{
element.onerror = null;
setIdenticon(msg.from[0], fn);
};
if (url) {
fn(url);
} else if (msg.avatar?.startsWith('data:')) {

View file

@ -10,8 +10,8 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
NAME = 'Avatars',
AUTHOR = 'SnappyMail',
URL = 'https://snappymail.eu/',
VERSION = '1.19',
RELEASE = '2024-07-08',
VERSION = '1.20',
RELEASE = '2024-08-26',
REQUIRED = '2.33.0',
CATEGORY = 'Contacts',
LICENSE = 'MIT',
@ -116,12 +116,16 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
* Nextcloud Mail uses insecure unencrypted 'index.php/apps/mail/api/avatars/url/local%40example.com'
*/
// public function ServiceAvatar(...$aParts)
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail)
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEncryptedEmail)
{
$sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEmail);
$maxAge = 86400;
$sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEncryptedEmail);
$aBimi = \explode('-', $sBimi, 2);
$sBimiSelector = isset($aBimi[1]) ? $aBimi[1] : 'default';
// $sEmail && \MailSo\Base\Http::setETag("{$sBimiSelector}-{$sEncryptedEmail}");
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($aBimi[0]), $sBimiSelector))) {
\header("Cache-Control: max-age={$maxAge}, private");
\header('Expires: '.\gmdate('D, j M Y H:i:s', $maxAge + \time()).' UTC');
\header('Content-Type: '.$aResult[0]);
echo $aResult[1];
} else {

View file

@ -99,11 +99,6 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt
return true;
}
public function IsExternal(): bool
{
return false;
}
public function SetEmail(string $sEmail) : bool
{
return true;

View file

@ -60,13 +60,7 @@ class NextcloudAddressBook implements \RainLoop\Providers\AddressBook\AddressBoo
return true;
}
public function IsExternal(): bool
{
return true;
}
public function SetEmail(string $sEmail): bool
{
public function SetEmail(string $sEmail) : bool {
return true;
}

View file

@ -451,7 +451,7 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
$ignoreSystemAddressbook
);
}
/*
/*
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');

View file

@ -172,9 +172,10 @@ class BodyStructure implements \JsonSerializable
);
}
public function IsAttachment() : bool
protected function IsAttachment(/*BodyStructure*/ $oParent) : bool
{
return 'application/pgp-encrypted' !== $this->sContentType
// return 'application/pgp-encrypted' !== $this->sContentType
return (!$oParent || !$oParent->isPgpEncrypted())
&& (
'attachment' === $this->sDisposition || (
!\str_starts_with($this->sContentType, 'multipart/')
@ -194,8 +195,8 @@ class BodyStructure implements \JsonSerializable
{
$aParts = [];
$gParts = $this->SearchByCallback(function ($oItem) {
return $oItem->isText() && !$oItem->IsAttachment();
$gParts = $this->SearchByCallback(function ($oItem, $oParent) {
return $oItem->isText() && !$oItem->IsAttachment($oParent);
});
foreach ($gParts as $oPart) {
$aParts[] = $oPart;
@ -232,13 +233,13 @@ class BodyStructure implements \JsonSerializable
public function SearchCharset() : string
{
$gParts = $this->SearchByCallback(function ($oPart) {
return $oPart->Charset() && $oPart->isText() && !$oPart->IsAttachment();
$gParts = $this->SearchByCallback(function ($oPart, $oParent) {
return $oPart->Charset() && $oPart->isText() && !$oPart->IsAttachment($oParent);
});
if (!$gParts->valid()) {
$gParts = $this->SearchByCallback(function ($oPart) {
return $oPart->Charset() && $oPart->IsAttachment();
$gParts = $this->SearchByCallback(function ($oPart, $oParent) {
return $oPart->Charset() && $oPart->IsAttachment($oParent);
});
}
@ -259,7 +260,7 @@ class BodyStructure implements \JsonSerializable
{
return $this->SearchByCallback(function ($oItem, $oParent) {
// return $oItem->IsAttachment();
return $oItem->IsAttachment() && (!$oParent || !$oParent->isPgpEncrypted());
return $oItem->IsAttachment($oParent) && (!$oParent || !$oParent->isPgpEncrypted());
});
}

View file

@ -299,6 +299,7 @@ class ImapClient extends \MailSo\Net\NetClient
private function setCapabilities(ResponseCollection $oResponseCollection) : void
{
$aList = $oResponseCollection->getCapabilityResult();
$this->aCapaRaw = $aList;
if ($aList) {
// Strip unused capabilities
$aList = \array_diff($aList, ['PREVIEW=FUZZY', 'SNIPPET=FUZZY', 'SORT=DISPLAY']);

View file

@ -213,6 +213,13 @@ class Actions
case 'suggestions':
$mResult = [];
break;
/* // See function Cacher
case 'cache':
$mResult = new \MailSo\Cache\Drivers\File(
\trim($this->oConfig->Get('cache', 'path', '')) ?: APP_PRIVATE_DATA . 'cache'
);
break;
*/
}
}
@ -460,6 +467,9 @@ class Actions
return $this->oDomainProvider;
}
/**
* bForceFile is only used by admin session token
*/
public function Cacher(?Model\Account $oAccount = null, bool $bForceFile = false): \MailSo\Cache\CacheClient
{
$sKey = '';
@ -481,7 +491,6 @@ class Actions
\trim($this->oConfig->Get('cache', 'path', '')) ?: APP_PRIVATE_DATA . 'cache'
);
}
// $sDriver = \strtoupper(\trim($this->oConfig->Get('cache', 'fast_cache_driver', 'files')));
$oDriver->setPrefix($sKey);
$this->aCachers[$sIndexKey]->SetDriver($oDriver);

View file

@ -184,6 +184,7 @@ trait Accounts
$bReload = false;
$oAccount = $this->getAccountFromToken();
if ($oAccount instanceof AdditionalAccount && $oAccount->Email() === $sEmailToDelete) {
// $this->SetAdditionalAuthToken(null);
\SnappyMail\Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
$bReload = true;
}
@ -207,7 +208,6 @@ trait Accounts
'accountHash' => $oAccount->Hash(),
'mainEmail' => \RainLoop\Api::Actions()->getMainAccountFromToken()->Email(),
'contactsAllowed' => $this->AddressBookProvider($oAccount)->IsActive(),
'contactsExternal' => $this->AddressBookProvider($oAccount)->IsExternal(),
'HideUnsubscribed' => false,
'useThreads' => (bool) $oConfig->Get('defaults', 'mail_use_threads', false),
'threadAlgorithm' => '',

View file

@ -176,23 +176,12 @@ trait UserAuth
$this->Plugins()->RunHook('login.success', array($oAccount));
$this->SetAuthToken($oAccount);
$this->SetAdditionalAuthToken(null);
}
return $oAccount;
}
private static function SetAccountCookie(string $sName, ?Account $oAccount)
{
if ($oAccount) {
Cookies::set(
$sName,
\MailSo\Base\Utils::UrlSafeBase64Encode(\SnappyMail\Crypt::EncryptToJSON($oAccount))
);
} else {
Cookies::clear($sName);
}
}
public function switchAccount(string $sEmail) : bool
{
$this->Http()->ServerNoCache();
@ -336,13 +325,13 @@ trait UserAuth
public function SetAuthToken(MainAccount $oAccount): void
{
$this->SetMainAuthAccount($oAccount);
static::SetAccountCookie(self::AUTH_SPEC_TOKEN_KEY, $oAccount);
Cookies::setSecure(self::AUTH_SPEC_TOKEN_KEY, $oAccount);
}
public function SetAdditionalAuthToken(?AdditionalAccount $oAccount): void
{
$this->oAdditionalAuthAccount = $oAccount ?: false;
static::SetAccountCookie(self::AUTH_ADDITIONAL_TOKEN_KEY, $oAccount);
Cookies::setSecure(self::AUTH_ADDITIONAL_TOKEN_KEY, $oAccount);
}
/**

View file

@ -456,6 +456,9 @@ class ActionsAdmin extends Actions
$aResult['Admin']['language'] = $oActions->ValidateLanguage($oConfig->Get('admin_panel', 'language', 'en'), '', true);
$aResult['Admin']['languages'] = \SnappyMail\L10n::getLanguages(true);
$aResult['Admin']['clientLanguage'] = $oActions->ValidateLanguage($oActions->detectClientLanguage(true), '', true, true);
$gnupg = \SnappyMail\PGP\GnuPG::getInstance('');
$aResult['gnupg'] = $gnupg ? $gnupg->getEngineInfo()['version'] : null;
} else {
$passfile = APP_PRIVATE_DATA.'admin_password.txt';
$sPassword = $oConfig->Get('security', 'admin_password', '');

View file

@ -43,7 +43,7 @@ abstract class Api
$CSP->report_only = $oConfig->Get('debug', 'enable', false); // || SNAPPYMAIL_DEV
// Allow https: due to remote images in e-mails or use proxy
if (!$oConfig->Get('security', 'use_local_proxy_for_external_images', '')) {
if (!$oConfig->Get('labs', 'use_local_proxy_for_external_images', '')) {
$CSP->add('img-src', 'https:');
$CSP->add('img-src', 'http:');
}

View file

@ -25,11 +25,6 @@ class AddressBook extends AbstractProvider
return $this->oDriver && $this->oDriver->IsSupported();
}
public function IsExternal() : bool
{
return $this->IsActive() ? $this->oDriver->IsExternal() : false;
}
public function Sync() : bool
{
return $this->IsActive() ? $this->oDriver->Sync() : false;

View file

@ -6,8 +6,6 @@ interface AddressBookInterface
{
public function IsSupported() : bool;
public function IsExternal() : bool;
public function SetEmail(string $sEmail) : bool;
public function Sync() : bool;

View file

@ -76,11 +76,6 @@ class PdoAddressBook
return \is_array($aDrivers) && \in_array($this->settings->driver, $aDrivers);
}
public function IsExternal(): bool
{
return false;
}
public function SetEmail(string $sEmail) : bool
{
$this->iUserID = $this->getUserId($sEmail);

View file

@ -168,16 +168,6 @@ abstract class Autoconfig
*/
private static function autodiscover(string $domain) : ?array
{
foreach ([
"https://{$domain}",
"https://autodiscover.{$domain}",
"http://autodiscover.{$domain}"
] as $host) {
$result = static::autodiscover_resolve($host, $domain);
if ($result) {
return $result;
}
}
foreach (\SnappyMail\DNS::SRV("_autodiscover._tcp.{$domain}") as $record) {
if (443 == $record['port']) {
$result = static::autodiscover_resolve("https://{$record['target']}", $domain);
@ -190,6 +180,16 @@ abstract class Autoconfig
return $result;
}
}
foreach ([
"https://{$domain}",
"https://autodiscover.{$domain}",
"http://autodiscover.{$domain}"
] as $host) {
$result = static::autodiscover_resolve($host, $domain);
if ($result) {
return $result;
}
}
return null;
}

View file

@ -98,13 +98,15 @@ class ServiceActions
$token = Utils::GetCsrfToken();
if (isset($_SERVER['HTTP_X_SM_TOKEN'])) {
if ($_SERVER['HTTP_X_SM_TOKEN'] !== $token) {
$sEmail = $this->oActions->getAccountFromToken(false)->Email();
$oAccount = $this->oActions->getAccountFromToken(false);
$sEmail = $oAccount ? $oAccount->Email() : 'guest';
$this->oActions->logWrite("{$_SERVER['HTTP_X_SM_TOKEN']} !== {$token} for {$sEmail}", \LOG_ERROR, 'Token');
throw new Exceptions\ClientException(Notifications::InvalidToken, null, 'HTTP Token mismatch');
}
} else if ($this->oHttp->IsPost()) {
if (empty($_POST['XToken']) || $_POST['XToken'] !== $token) {
$sEmail = $this->oActions->getAccountFromToken(false)->Email();
$oAccount = $this->oActions->getAccountFromToken(false);
$sEmail = $oAccount ? $oAccount->Email() : 'guest';
$this->oActions->logWrite("{$_POST['XToken']} !== {$token} for {$sEmail}", \LOG_ERROR, 'XToken');
throw new Exceptions\ClientException(Notifications::InvalidToken, null, 'XToken mismatch');
}

View file

@ -57,7 +57,7 @@ use Sabre\Xml\Reader;
*
* @phpstan-return array<string, mixed>
*/
function keyValue(Reader $reader, string $namespace = null): array
function keyValue(Reader $reader, ?string $namespace = null): array
{
// If there's no children, we don't do anything.
if ($reader->isEmptyElement) {
@ -148,7 +148,7 @@ function keyValue(Reader $reader, string $namespace = null): array
*
* @phpstan-return list<string>
*/
function enum(Reader $reader, string $namespace = null): array
function enum(Reader $reader, ?string $namespace = null): array
{
// If there's no children, we don't do anything.
if ($reader->isEmptyElement) {
@ -223,6 +223,9 @@ function valueObject(Reader $reader, string $className, string $namespace): obje
// Ignore property
$reader->next();
}
} elseif (Reader::ELEMENT === $reader->nodeType) {
// Skipping element from different namespace
$reader->next();
} else {
if (Reader::END_ELEMENT !== $reader->nodeType && !$reader->read()) {
break;

View file

@ -31,7 +31,7 @@ class LibXMLException extends ParseException
*
* @param \LibXMLError[] $errors
*/
public function __construct(array $errors, int $code = 0, \Throwable $previousException = null)
public function __construct(array $errors, int $code = 0, ?\Throwable $previousException = null)
{
$this->errors = $errors;
parent::__construct($errors[0]->message.' on line '.$errors[0]->line.', column '.$errors[0]->column, $code, $previousException);

View file

@ -107,7 +107,7 @@ class Reader extends \XMLReader
*
* @return array<int,array<string, mixed>>
*/
public function parseGetElements(array $elementMap = null): array
public function parseGetElements(?array $elementMap = null): array
{
$result = $this->parseInnerTree($elementMap);
if (!is_array($result)) {
@ -132,7 +132,7 @@ class Reader extends \XMLReader
*
* @return array<int,array<string, mixed>>|string|null
*/
public function parseInnerTree(array $elementMap = null)
public function parseInnerTree(?array $elementMap = null)
{
$text = null;
$elements = [];

View file

@ -107,7 +107,7 @@ class Service
*
* @throws ParseException
*/
public function parse($input, string $contextUri = null, string &$rootElementName = null)
public function parse($input, ?string $contextUri = null, ?string &$rootElementName = null)
{
if (is_resource($input)) {
// Unfortunately the XMLReader doesn't support streams. When it
@ -151,7 +151,7 @@ class Service
*
* @throws ParseException
*/
public function expect($rootElementName, $input, string $contextUri = null)
public function expect($rootElementName, $input, ?string $contextUri = null)
{
if (is_resource($input)) {
// Unfortunately the XMLReader doesn't support streams. When it
@ -200,7 +200,7 @@ class Service
*
* @param string|array<int|string, mixed>|object|XmlSerializable $value
*/
public function write(string $rootElementName, $value, string $contextUri = null): string
public function write(string $rootElementName, $value, ?string $contextUri = null): string
{
$w = $this->getWriter();
$w->openMemory();
@ -241,7 +241,6 @@ class Service
public function mapValueObject(string $elementName, string $className): void
{
list($namespace) = self::parseClarkNotation($elementName);
$namespace = $namespace ?? '';
require_once __DIR__ . '/Deserializer/functions.php';
$this->elementMap[$elementName] = function (Reader $reader) use ($className, $namespace) {
@ -265,7 +264,7 @@ class Service
*
* @throws \InvalidArgumentException
*/
public function writeValueObject(object $object, string $contextUri = null): string
public function writeValueObject(object $object, ?string $contextUri = null): string
{
if (!isset($this->valueObjectMap[get_class($object)])) {
throw new \InvalidArgumentException('"'.get_class($object).'" is not a registered value object class. Register your class with mapValueObject.');
@ -284,7 +283,7 @@ class Service
*
* If the string was invalid, it will throw an InvalidArgumentException.
*
* @return array{string|null, string}
* @return array{string, string}
*
* @throws \InvalidArgumentException
*/

View file

@ -16,5 +16,5 @@ class Version
/**
* Full version number.
*/
public const VERSION = '4.0.4';
public const VERSION = '4.0.5';
}

View file

@ -125,7 +125,6 @@ class Writer extends \XMLWriter
if ('{' === $name[0]) {
list($namespace, $localName) =
Service::parseClarkNotation($name);
$namespace = $namespace ?? '';
if (array_key_exists($namespace, $this->namespaceMap)) {
$result = $this->startElementNS(
@ -240,7 +239,6 @@ class Writer extends \XMLWriter
$localName
) = Service::parseClarkNotation($name);
$namespace = $namespace ?? '';
if (array_key_exists($namespace, $this->namespaceMap)) {
// It's an attribute with a namespace we know
return $this->writeAttribute(

View file

@ -47,6 +47,18 @@ class Cookies
: null;
}
public static function setSecure(string $sName, $data): void
{
if (\is_null($data)) {
static::clear($sName);
} else {
static::set(
$sName,
\MailSo\Base\Utils::UrlSafeBase64Encode(Crypt::EncryptToJSON($data))
);
}
}
private static function _set(string $sName, string $sValue, int $iExpire, bool $httponly = true) : bool
{
$sPath = static::$DefaultPath;
@ -134,5 +146,11 @@ class Cookies
{
static::init();
static::_set($sName, '', 0);
// Delete 4K split cookie parts
foreach (\array_keys($_COOKIE) as $sCookieName) {
if (\strtok($sCookieName, '~') === $sName) {
static::_set($sCookieName, '', 0);
}
}
}
}

View file

@ -76,12 +76,16 @@ abstract class Base
function __destruct()
{
$this->proc_close();
$gpgconf = static::findBinary('gpgconf');
if ($gpgconf) {
$cmd = $gpgconf . ' --kill gpg-agent';
// https://github.com/the-djmaze/snappymail/issues/1560#issuecomment-2144817883
// if (\version_compare($this->version, '2.4.0', '<')) {
$cmd .= ' ' . \escapeshellarg($this->options['homedir']);
// }
$env = ['GNUPGHOME' => $this->options['homedir']];
$pipes = [];
if ($process = \proc_open($gpgconf . ' --kill gpg-agent --homedir ' . \escapeshellarg($this->options['homedir']), [], $pipes, null, $env)) {
if ($process = \proc_open($cmd, [], $pipes, null, $env)) {
\proc_close($process);
}
}

View file

@ -867,6 +867,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
// Don't localize GnuPG results.
$env = $_ENV;
$env['LC_ALL'] = 'C';
$env = \array_filter($env, fn($var) => \is_scalar($var));
$proc_pipes = [];

View file

@ -58,20 +58,6 @@ class SMIME extends Base
}
}
function __destruct()
{
$this->proc_close();
$gpgconf = static::findBinary('gpgconf');
if ($gpgconf) {
$env = ['GNUPGHOME' => $this->options['homedir']];
$pipes = [];
if ($process = \proc_open($gpgconf . ' --kill gpg-agent --homedir ' . \escapeshellarg($this->options['homedir']), [], $pipes, null, $env)) {
\proc_close($process);
}
}
}
public static function isSupported() : bool
{
return parent::isSupported() && static::findBinary('gpgsm');

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "المهملات",
"ARCHIVE_NAME": "الأرشيف"
},
"QUOTA": {
"TITLE": "Quota usage"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "أعد تحميل قائمة الرسائل",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Сметніца",
"ARCHIVE_NAME": "Архіў"
},
"QUOTA": {
"TITLE": "Дыскавая квота"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Абнавіць Спіс Лістоў",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Кошче",
"ARCHIVE_NAME": "Архив"
},
"QUOTA": {
"TITLE": "Използване по дялове"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Презареди списъка със съобщения",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Koš",
"ARCHIVE_NAME": "Archiv"
},
"QUOTA": {
"TITLE": "Využití kvóty"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Obnovit seznam zpráv",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Slettede meddelelser",
"ARCHIVE_NAME": "Arkiverede meddelelser"
},
"QUOTA": {
"TITLE": "Forbrug"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Genindlæs meddelelser",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Papierkorb",
"ARCHIVE_NAME": "Archiv"
},
"QUOTA": {
"TITLE": "Speicherplatz-Nutzung"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Nachrichtenliste neu laden",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Άχρηστα",
"ARCHIVE_NAME": "Αρχείο"
},
"QUOTA": {
"TITLE": "Χρήση αποθηκευτικού χώρου"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Φόρτωσε ξανά τη λίστα των μηνυμάτων",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Trash",
"ARCHIVE_NAME": "Archive"
},
"QUOTA": {
"TITLE": "Quota usage"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Reload Message List",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Papelera",
"ARCHIVE_NAME": "Archivo"
},
"QUOTA": {
"TITLE": "Cuota"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Actualizar lista de mensajes",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Prügikast",
"ARCHIVE_NAME": "Arhiiv"
},
"QUOTA": {
"TITLE": "Andmemaht"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Laadi kirjade nimekiri uuesti",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Zakarrontzia",
"ARCHIVE_NAME": "Artxiboa"
},
"QUOTA": {
"TITLE": "Kuota"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Eguneratu mezuen zerrenda",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "سطل آشغال",
"ARCHIVE_NAME": "بایگانی"
},
"QUOTA": {
"TITLE": "سهمیه استفاده شده"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "بازنگری لیست پیام‌ها",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Roskakori",
"ARCHIVE_NAME": "Arkisto"
},
"QUOTA": {
"TITLE": "Kiintiö"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Päivitä viestit",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Corbeille",
"ARCHIVE_NAME": "Archive"
},
"QUOTA": {
"TITLE": "Quota"
"IMAP": {
"ACL_RIGHTS": "Droits",
"ACL_A": "Administrer",
"ACL_E": "Supprimer",
"ACL_I": "Insérer des messages",
"ACL_K": "Créer des sous-dossiers",
"ACL_L": "Afficher les messages",
"ACL_P": "Envoyer un courrier",
"ACL_R": "Sélectionner un dossier",
"ACL_S": "\\VUE balise",
"ACL_T": "\\SUPPRIMÉE balise",
"ACL_W": "Autres balises",
"ACL_X": "Supprimer\/Renommer le dossier"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Recharger la liste des messages",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Lomtár",
"ARCHIVE_NAME": "Archívum"
},
"QUOTA": {
"TITLE": "Helyfoglalás"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Üzenetlista újratöltés",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Sampah",
"ARCHIVE_NAME": "Arsip"
},
"QUOTA": {
"TITLE": "Jatah digunakan"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Muat Ulang Daftar Pesan",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Ruslafata",
"ARCHIVE_NAME": "Safnskrá"
},
"QUOTA": {
"TITLE": "Disknotkun"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Endurhlaða skilaboðalista",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Cestino",
"ARCHIVE_NAME": "Archivio"
},
"QUOTA": {
"TITLE": "Uso della quota"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Aggiorna",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "ごみ箱",
"ARCHIVE_NAME": "アーカイブ"
},
"QUOTA": {
"TITLE": "クォータ使用量"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "リスト更新",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "휴지통",
"ARCHIVE_NAME": "보관함"
},
"QUOTA": {
"TITLE": "사용량"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "메일 목록 새로고침",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Šiukšlynas",
"ARCHIVE_NAME": "Archyvas"
},
"QUOTA": {
"TITLE": "Kvotos naudojimas"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Atnaujinti žinučių sąrašą",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Miskaste",
"ARCHIVE_NAME": "Arhīvs"
},
"QUOTA": {
"TITLE": "Kvotas izmantojums"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Pārlādēt ziņojumu sarakstu",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Papirkurv",
"ARCHIVE_NAME": "Arkiv"
},
"QUOTA": {
"TITLE": "Kvotebruk"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Hent nye meldinger",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Verwijderde items",
"ARCHIVE_NAME": "Archief"
},
"QUOTA": {
"TITLE": "Quota gebruik"
"IMAP": {
"ACL_RIGHTS": "Rechten",
"ACL_A": "Beheren",
"ACL_E": "Uitwissen",
"ACL_I": "Berichten toevoegen",
"ACL_K": "Submappen maken",
"ACL_L": "Toon berichten",
"ACL_P": "Verstuur mail",
"ACL_R": "Map selecteren",
"ACL_S": "\\SEEN label",
"ACL_T": "\\DELETED label",
"ACL_W": "Andere labels",
"ACL_X": "Map verwijderen\/hernoemen"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Berichten controleren",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Kosz",
"ARCHIVE_NAME": "Archiwum"
},
"QUOTA": {
"TITLE": "Wykorzystane miejsce"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Odśwież listę wiadomości",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Lixeira",
"ARCHIVE_NAME": "Arquivadas"
},
"QUOTA": {
"TITLE": "Uso do Armazenamento"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Atualizar Lista de Mensagens",

View file

@ -11,7 +11,7 @@
"CLOSE": "Fechar",
"CONTACTS": "Contactos",
"DELETE": "Eliminar",
"UNDELETE": "Undelete",
"UNDELETE": "Restaurar",
"DONE": "Terminado",
"EMAIL": "Email",
"FROM": "De",
@ -77,7 +77,7 @@
"SUBFOLDERS_SUBTREE": "Todas",
"SUBFOLDERS_SUBTREE_ONE": "Um nível",
"REPLIED": "Respondidas",
"KEYWORD": "Etiqueta"
"KEYWORD": "Marcador"
},
"FOLDER_LIST": {
"BUTTON_COMPOSE": "Escrever",
@ -88,8 +88,19 @@
"TRASH_NAME": "Lixo",
"ARCHIVE_NAME": "Arquivo"
},
"QUOTA": {
"TITLE": "Uso da Cota"
"IMAP": {
"ACL_RIGHTS": "Permissões",
"ACL_A": "Administrar",
"ACL_E": "Purgar",
"ACL_I": "Inserir mensagens",
"ACL_K": "Criar sub-pastas",
"ACL_L": "Mostrar mensagens",
"ACL_P": "Enviar correio",
"ACL_R": "Escolher pasta",
"ACL_S": "Marcador \\SEEN (lida)",
"ACL_T": "Marcador \\DELETED (eliminada)",
"ACL_W": "Outros marcadores",
"ACL_X": "Eliminar\/Renomear pasta"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Atualizar lista de mensagens",
@ -161,8 +172,8 @@
"LINK_DOWNLOAD_AS_ZIP": "Transferir em arquivo zip",
"SPAM_SCORE": "Pontuação de Spam",
"HAS_VIRUS_WARNING": "AVISO: vírus detetado",
"TAGS": "Etiquetas",
"NEW_TAG": "Nova etiqueta",
"TAGS": "Marcadores",
"NEW_TAG": "Novo marcador",
"ENABLE_TRACKING_LINKS": "Ativar ligações de rastreamento",
"TRACKING_ENABLED": "As ligações irão ser rastreadas quando clicadas!"
},
@ -217,7 +228,7 @@
"ADD_MENU_URL": "Website",
"ADD_MENU_ADDRESS": "Morada",
"ADD_MENU_BIRTHDAY": "Aniversário",
"ADD_MENU_TAGS": "Etiquetas",
"ADD_MENU_TAGS": "Marcadores",
"BUTTON_SHARE_ALL": "Todos",
"BUTTON_SYNC": "Sincronização (CardDAV)",
"SEND_TO_ALL_CONTACT_EMAILS": "Enviar para todos os contactos"
@ -488,7 +499,7 @@
"IMAGES_OPTION_ALWAYS": "Mostrar sempre",
"IMAGES_WHITELIST": "Lista Branca de imagens",
"MESSAGE_POPUP_WINDOW": "Abrir em nova janela em vez de um separador",
"MAILTO": "Register as 'mailto:' links handler"
"MAILTO": "Registar como manipulador de ligações 'mailto:'"
},
"SETTINGS_CONTACTS": {
"LABEL_CONTACTS_AUTOSAVE": "Adicionar destinatários automaticamente à sua lista de endereços",
@ -610,7 +621,7 @@
"CantSaveFilters": "Não é possível guardar os filtros",
"CantGetFilters": "Não é possível obter os filtros",
"CantActivateFiltersScript": "Não é possível ativar o script de filtração",
"CantDeleteFiltersScript": "Não é possível apagar o script de filtração",
"CantDeleteFiltersScript": "Não é possível eliminar o script de filtração",
"FiltersAreNotCorrect": "Os filtros não estão corretos",
"CantCreateFolder": "Não é possível criar a pasta",
"CantRenameFolder": "Não é possível renomear a pasta",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Șterse",
"ARCHIVE_NAME": "Archive"
},
"QUOTA": {
"TITLE": "Cotă"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Actualizare",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Корзина",
"ARCHIVE_NAME": "Архив"
},
"QUOTA": {
"TITLE": "Дисковая квота"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Обновить Список Писем",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Kôš",
"ARCHIVE_NAME": "Archív"
},
"QUOTA": {
"TITLE": "Spotreba kvóty"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Znovu načítať zoznam správ",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Smeti",
"ARCHIVE_NAME": "Arhiv"
},
"QUOTA": {
"TITLE": "Poraba prostora"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Ponovno naloži seznam sporočil",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Papperskorg",
"ARCHIVE_NAME": "Arkiv"
},
"QUOTA": {
"TITLE": "Kvotanvändning"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Ladda meddelandelista",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Çöp Kutusu",
"ARCHIVE_NAME": "Arşiv"
},
"QUOTA": {
"TITLE": "Kota Kullanımı"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Mesaj Listesini Yenile",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Кошик",
"ARCHIVE_NAME": "Архів"
},
"QUOTA": {
"TITLE": "Дискова квота"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Оновити список листів",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "Thùng rác",
"ARCHIVE_NAME": "Kho lưu trữ"
},
"QUOTA": {
"TITLE": "Dung lượng sử dụng"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "Tải lại danh sách thư",

View file

@ -1,182 +1,182 @@
{
"GLOBAL": {
"ADD": "新增",
"CLOSE": "關閉",
"SAVE": "儲存",
"TEST": "測試",
"UPDATE": "更新",
"USERNAME": "使用者名稱",
"PASSWORD": "密碼",
"CANCEL": "取消",
"SEARCH": "搜尋"
},
"LOGIN": {
"LABEL_LOGIN": "登入",
"LABEL_TOTP": "TOTP 密碼",
"BUTTON_LOGIN": "登入到控制面板"
},
"TOP_PANEL": {
"LABEL_ADMIN_PANEL": "控制面板"
},
"SETTINGS_LABELS": {
"GENERAL": "常規",
"LOGIN": "登入",
"BRANDING": "Logo",
"CONTACTS": "聯絡人",
"DOMAINS": "域名",
"SECURITY": "安全",
"PACKAGES": "外掛包",
"CONFIG": "配置",
"ABOUT": "關於"
},
"TAB_GENERAL": {
"LEGEND_INTERFACE": "介面",
"LABEL_LANGUAGE": "語言",
"LABEL_LANGUAGE_ADMIN": "語言(後臺)",
"LABEL_THEME": "主題",
"LABEL_ALLOW_LANGUAGES_ON_SETTINGS": "允許在設定介面中選擇語言",
"LABEL_ALLOW_THEMES_ON_SETTINGS": "允許在設定介面中選擇主題",
"LABEL_ALLOW_BACKGROUND_ON_SETTINGS": "允許在設定介面中選擇背景",
"LABEL_SHOW_THUMBNAILS": "顯示縮圖(附件)",
"LEGEND_MAIN": "主要設定",
"LABEL_ATTACHMENT_SIZE_LIMIT": "附件大小限制",
"LABEL_ALLOW_ADDITIONAL_ACCOUNTS": "允許額外的賬戶",
"LABEL_ALLOW_IDENTITIES": "允許多重身份",
"ALERT_DATA_ACCESS": "資料資料夾可被訪問。請配置您的 Web 伺服器以阻止從外部訪問資料資料夾。獲取更多幫助:",
"ALERT_WARNING": "警告!",
"HTML_ALERT_WEAK_PASSWORD": "您正在使用預設的管理員密碼\n<br>\n安全起見請立即將密碼\n<strong><a href=\"#\/security\">更改<\/a><\/strong>\n為其他的字串。\n"
},
"TAB_LOGIN": {
"LEGEND_LOGIN_SCREEN": "登入介面",
"LABEL_DEFAULT_DOMAIN": "預設域名",
"LABEL_DETERMINE_USER_DOMAIN": "嘗試判斷使用者使用的域名",
"LABEL_ALLOW_LANGUAGES_ON_LOGIN": "允許在登入介面選擇語言",
"LABEL_DETERMINE_USER_LANGUAGE": "嘗試判斷使用者使用語言"
},
"TAB_BRANDING": {
"LEGEND_BRANDING": "Logo",
"LABEL_PAGE_TITLE": "頁面標題",
"LABEL_LOADING_DESCRIPTION": "載入介面文字",
"LABEL_FAVICON_URL": "網站圖示"
},
"TAB_CONTACTS": {
"LEGEND_CONTACTS": "聯絡人",
"LEGEND_STORAGE": "儲存(PDO)",
"LABEL_ENABLE_CONTACTS": "啟用聯絡人",
"LABEL_ALLOW_SYNC": "允許同步聯絡人(依靠外部CardDAV伺服器)",
"LABEL_STORAGE_TYPE": "型別",
"LABEL_STORAGE_DSN": "Dsn",
"LABEL_STORAGE_USER": "使用者",
"LABEL_STORAGE_PASSWORD": "密碼",
"SQLITE_GLOBAL": "使用全域性資料庫替代各賬號的資料庫",
"ALERT_NOTICE": "提示!",
"SUGGESTIONS_LIMIT": "建議數量上限",
"HTML_ALERT_DO_NOT_USE_THIS_DATABASE": "如果有大量的活躍使用者,請不要選擇此資料庫型別。",
"HTML_ALERT_DOES_NOT_SUPPORTED": "您的系統不支援聯絡人\n<br>\n您需要在您的伺服器上安裝或啟用<strong>PDO (SQLite \/ MySQL \/ PostgreSQL)<\/strong> 元件。\n"
},
"TAB_DOMAINS": {
"LEGEND_DOMAINS": "域名",
"BUTTON_ADD_DOMAIN": "新增域名",
"BUTTON_ADD_ALIAS": "新增別名",
"DELETE_ARE_YOU_SURE": "您確定嗎?",
"HTML_DOMAINS_HELPER": "允許訪問的域名列表。\n<br>\n點選域名進行設定。\n"
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "安全",
"LABEL_USE_IMAGE_PROXY": "對外部圖片使用本地代理",
"LABEL_AUTO_VERIFY": "自動驗證簽名",
"LABEL_SHOW_PHP_INFO": "顯示 PHP 資訊",
"LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "管理員面板訪問憑據",
"LABEL_CURRENT_PASSWORD": "當前密碼",
"LABEL_NEW_PASSWORD": "新密碼",
"LABEL_REPEAT_PASSWORD": "重複輸入新密碼",
"LABEL_REQUIRE_VERIFICATION": "要求驗證 SSL 證書",
"CRYPTO": "加密",
"LABEL_ALLOW_SELF_SIGNED": "允許自簽名證書"
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "啟用外掛",
"LEGEND_AVAILABLE_FOR_UPDATE": "有更新可用",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "可用於安裝",
"LEGEND_INSTALLED_PACKAGES": "已安裝的外掛包",
"ALERT_CANNOT_ACCESS_REPOSITORY": "目前無法訪問外掛倉庫"
},
"TAB_ABOUT": {
"LEGEND_ABOUT": "關於",
"LABEL_TAG_HINT": "簡潔、現代化、快速的網頁郵件客戶端",
"LABEL_ALL_RIGHTS_RESERVED": "保留所有權利",
"HINT_READ_CHANGE_LOG": "請在更新前閱讀更新日誌",
"HINT_IS_UP_TO_DATE": "SnappyMail已是最新版本",
"HTML_NEW_VERSION": "有新的<b>%VERSION%<\/b> 版本可用.",
"LABEL_UPDATING": "更新中",
"LABEL_CHECKING": "檢查更新",
"BUTTON_RELEASES": "發行版"
},
"POPUPS_DOMAIN_ALIAS": {
"TITLE_ADD_DOMAIN_ALIAS": "新增別名",
"LABEL_ALIAS": "別名",
"LABEL_DOMAIN": "域名"
},
"POPUPS_DOMAIN": {
"TITLE_ADD_DOMAIN": "新增域名",
"TITLE_ADD_DOMAIN_WITH_NAME": "新增域名 \"%NAME%\"",
"TITLE_EDIT_DOMAIN": "編輯域名 \"%NAME%\"",
"LABEL_NAME": "名字",
"NAME_HELPER": "支援萬用字元",
"LABEL_SERVER": "伺服器",
"LABEL_PORT": "埠",
"LABEL_SECURE": "加密",
"TIMEOUT": "超時",
"DISABLE_CAPABILITIES": "禁用能力",
"LIMITS": "限制",
"FOLDERS": "資料夾",
"MESSAGES": "訊息",
"SECURE_OPTION_NONE": "無",
"LABEL_ALLOW_SIEVE_SCRIPTS": "可使用篩選指令碼",
"LABEL_USE_SHORT_LOGIN": "使用短使用者名稱登入",
"LABEL_LOWERCASE_LOGIN": "小寫登入 (大小寫敏感)",
"LABEL_USE_AUTH": "使用驗證",
"LABEL_SET_SENDER": "使用登入名作為發件人",
"LABEL_AUTH_PLAIN_LINE": "強制單一 AUTH PLAIN 命令登入",
"LABEL_USE_PHP_MAIL": "使用 php mail() 函式",
"BUTTON_WHITE_LIST": "白名單",
"NEW_DOMAIN_DESC": "此域名配置將允許您和<strong>%NAME%<\/strong>電子郵件地址一起工作.",
"WHITE_LIST_ALERT": "允許訪問的域使用者;使用空格分隔。\n",
"VALID_SPAM_HEADERS": "Valid SPAM headers",
"VALID_VIRUS_HEADERS": "Valid virus headers"
},
"POPUPS_PLUGIN": {
"TITLE_PLUGIN": "外掛",
"DESC_NOTHING_TO_CONFIGURE": "無可用配置"
},
"POPUPS_ASK": {
"BUTTON_YES": "是",
"BUTTON_NO": "否",
"DESC_WANT_CLOSE_THIS_WINDOW": "您確定要關閉此視窗嗎?"
},
"POPUPS_LANGUAGES": {
"TITLE_LANGUAGES": "選擇您的語言"
},
"HINTS": {
"BETA": "測試版本",
"WARNING": "警告"
},
"ERRORS": {
"DOMAIN_ALREADY_EXISTS": "域名已經存在",
"UNKNOWN_ERROR": "未知錯誤"
},
"NOTIFICATIONS": {
"RequestError": "請求失敗",
"RequestAborted": "請求中止",
"RequestTimeout": "請求超時",
"InvalidToken": "標記無效",
"AuthError": "驗證失敗",
"DomainAlreadyExists": "域名已經存在",
"CantSavePluginSettings": "無法儲存設定",
"CantInstallPackage": "安裝外掛包失敗",
"CantDeletePackage": "移除外掛包失敗",
"InvalidPluginPackage": "無效外掛包",
"UnsupportedPluginPackage": "不支援的外掛包",
"UnknownError": "未知錯誤"
}
}
{
"GLOBAL": {
"ADD": "新增",
"CLOSE": "關閉",
"SAVE": "儲存",
"TEST": "測試",
"UPDATE": "更新",
"USERNAME": "使用者名稱",
"PASSWORD": "密碼",
"CANCEL": "取消",
"SEARCH": "搜尋"
},
"LOGIN": {
"LABEL_LOGIN": "登入",
"LABEL_TOTP": "TOTP 密碼",
"BUTTON_LOGIN": "登入到控制面板"
},
"TOP_PANEL": {
"LABEL_ADMIN_PANEL": "控制面板"
},
"SETTINGS_LABELS": {
"GENERAL": "常規",
"LOGIN": "登入",
"BRANDING": "Logo",
"CONTACTS": "聯絡人",
"DOMAINS": "域名",
"SECURITY": "安全",
"PACKAGES": "外掛包",
"CONFIG": "配置",
"ABOUT": "關於"
},
"TAB_GENERAL": {
"LEGEND_INTERFACE": "介面",
"LABEL_LANGUAGE": "語言",
"LABEL_LANGUAGE_ADMIN": "語言(後臺)",
"LABEL_THEME": "主題",
"LABEL_ALLOW_LANGUAGES_ON_SETTINGS": "允許在設定介面中選擇語言",
"LABEL_ALLOW_THEMES_ON_SETTINGS": "允許在設定介面中選擇主題",
"LABEL_ALLOW_BACKGROUND_ON_SETTINGS": "允許在設定介面中選擇背景",
"LABEL_SHOW_THUMBNAILS": "顯示縮圖(附件)",
"LEGEND_MAIN": "主要設定",
"LABEL_ATTACHMENT_SIZE_LIMIT": "附件大小限制",
"LABEL_ALLOW_ADDITIONAL_ACCOUNTS": "允許額外的賬戶",
"LABEL_ALLOW_IDENTITIES": "允許多重身份",
"ALERT_DATA_ACCESS": "資料資料夾可被訪問。請配置您的 Web 伺服器以阻止從外部訪問資料資料夾。獲取更多幫助:",
"ALERT_WARNING": "警告!",
"HTML_ALERT_WEAK_PASSWORD": "您正在使用預設的管理員密碼\n<br>\n安全起見請立即將密碼\n<strong><a href=\"#\/security\">更改<\/a><\/strong>\n為其他的字串。\n"
},
"TAB_LOGIN": {
"LEGEND_LOGIN_SCREEN": "登入介面",
"LABEL_DEFAULT_DOMAIN": "預設域名",
"LABEL_DETERMINE_USER_DOMAIN": "嘗試判斷使用者使用的域名",
"LABEL_ALLOW_LANGUAGES_ON_LOGIN": "允許在登入介面選擇語言",
"LABEL_DETERMINE_USER_LANGUAGE": "嘗試判斷使用者使用語言"
},
"TAB_BRANDING": {
"LEGEND_BRANDING": "Logo",
"LABEL_PAGE_TITLE": "頁面標題",
"LABEL_LOADING_DESCRIPTION": "載入介面文字",
"LABEL_FAVICON_URL": "網站圖示"
},
"TAB_CONTACTS": {
"LEGEND_CONTACTS": "聯絡人",
"LEGEND_STORAGE": "儲存(PDO)",
"LABEL_ENABLE_CONTACTS": "啟用聯絡人",
"LABEL_ALLOW_SYNC": "允許同步聯絡人(依靠外部CardDAV伺服器)",
"LABEL_STORAGE_TYPE": "型別",
"LABEL_STORAGE_DSN": "Dsn",
"LABEL_STORAGE_USER": "使用者",
"LABEL_STORAGE_PASSWORD": "密碼",
"SQLITE_GLOBAL": "使用全域性資料庫替代各賬號的資料庫",
"ALERT_NOTICE": "提示!",
"SUGGESTIONS_LIMIT": "建議數量上限",
"HTML_ALERT_DO_NOT_USE_THIS_DATABASE": "如果有大量的活躍使用者,請不要選擇此資料庫型別。",
"HTML_ALERT_DOES_NOT_SUPPORTED": "您的系統不支援聯絡人\n<br>\n您需要在您的伺服器上安裝或啟用<strong>PDO (SQLite \/ MySQL \/ PostgreSQL)<\/strong> 元件。\n"
},
"TAB_DOMAINS": {
"LEGEND_DOMAINS": "域名",
"BUTTON_ADD_DOMAIN": "新增域名",
"BUTTON_ADD_ALIAS": "新增別名",
"DELETE_ARE_YOU_SURE": "您確定嗎?",
"HTML_DOMAINS_HELPER": "允許訪問的域名列表。\n<br>\n點選域名進行設定。\n"
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "安全",
"LABEL_USE_IMAGE_PROXY": "對外部圖片使用本地代理",
"LABEL_AUTO_VERIFY": "自動驗證簽名",
"LABEL_SHOW_PHP_INFO": "顯示 PHP 資訊",
"LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "管理員面板訪問憑據",
"LABEL_CURRENT_PASSWORD": "當前密碼",
"LABEL_NEW_PASSWORD": "新密碼",
"LABEL_REPEAT_PASSWORD": "重複輸入新密碼",
"LABEL_REQUIRE_VERIFICATION": "要求驗證 SSL 證書",
"CRYPTO": "加密",
"LABEL_ALLOW_SELF_SIGNED": "允許自簽名證書"
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "啟用外掛",
"LEGEND_AVAILABLE_FOR_UPDATE": "有更新可用",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "可用於安裝",
"LEGEND_INSTALLED_PACKAGES": "已安裝的外掛包",
"ALERT_CANNOT_ACCESS_REPOSITORY": "目前無法訪問外掛倉庫"
},
"TAB_ABOUT": {
"LEGEND_ABOUT": "關於",
"LABEL_TAG_HINT": "簡潔、現代化、快速的網頁郵件客戶端",
"LABEL_ALL_RIGHTS_RESERVED": "保留所有權利",
"HINT_READ_CHANGE_LOG": "請在更新前閱讀更新日誌",
"HINT_IS_UP_TO_DATE": "SnappyMail已是最新版本",
"HTML_NEW_VERSION": "有新的<b>%VERSION%<\/b> 版本可用.",
"LABEL_UPDATING": "更新中",
"LABEL_CHECKING": "檢查更新",
"BUTTON_RELEASES": "發行版"
},
"POPUPS_DOMAIN_ALIAS": {
"TITLE_ADD_DOMAIN_ALIAS": "新增別名",
"LABEL_ALIAS": "別名",
"LABEL_DOMAIN": "域名"
},
"POPUPS_DOMAIN": {
"TITLE_ADD_DOMAIN": "新增域名",
"TITLE_ADD_DOMAIN_WITH_NAME": "新增域名 \"%NAME%\"",
"TITLE_EDIT_DOMAIN": "編輯域名 \"%NAME%\"",
"LABEL_NAME": "名字",
"NAME_HELPER": "支援萬用字元",
"LABEL_SERVER": "伺服器",
"LABEL_PORT": "埠",
"LABEL_SECURE": "加密",
"TIMEOUT": "超時",
"DISABLE_CAPABILITIES": "禁用能力",
"LIMITS": "限制",
"FOLDERS": "資料夾",
"MESSAGES": "訊息",
"SECURE_OPTION_NONE": "無",
"LABEL_ALLOW_SIEVE_SCRIPTS": "可使用篩選指令碼",
"LABEL_USE_SHORT_LOGIN": "使用短使用者名稱登入",
"LABEL_LOWERCASE_LOGIN": "小寫登入 (大小寫敏感)",
"LABEL_USE_AUTH": "使用驗證",
"LABEL_SET_SENDER": "使用登入名作為發件人",
"LABEL_AUTH_PLAIN_LINE": "強制單一 AUTH PLAIN 命令登入",
"LABEL_USE_PHP_MAIL": "使用 php mail() 函式",
"BUTTON_WHITE_LIST": "白名單",
"NEW_DOMAIN_DESC": "此域名配置將允許您和<strong>%NAME%<\/strong>電子郵件地址一起工作.",
"WHITE_LIST_ALERT": "允許訪問的域使用者;使用空格分隔。\n",
"VALID_SPAM_HEADERS": "Valid SPAM headers",
"VALID_VIRUS_HEADERS": "Valid virus headers"
},
"POPUPS_PLUGIN": {
"TITLE_PLUGIN": "外掛",
"DESC_NOTHING_TO_CONFIGURE": "無可用配置"
},
"POPUPS_ASK": {
"BUTTON_YES": "是",
"BUTTON_NO": "否",
"DESC_WANT_CLOSE_THIS_WINDOW": "您確定要關閉此視窗嗎?"
},
"POPUPS_LANGUAGES": {
"TITLE_LANGUAGES": "選擇您的語言"
},
"HINTS": {
"BETA": "測試版本",
"WARNING": "警告"
},
"ERRORS": {
"DOMAIN_ALREADY_EXISTS": "域名已經存在",
"UNKNOWN_ERROR": "未知錯誤"
},
"NOTIFICATIONS": {
"RequestError": "請求失敗",
"RequestAborted": "請求中止",
"RequestTimeout": "請求超時",
"InvalidToken": "標記無效",
"AuthError": "驗證失敗",
"DomainAlreadyExists": "域名已經存在",
"CantSavePluginSettings": "無法儲存設定",
"CantInstallPackage": "安裝外掛包失敗",
"CantDeletePackage": "移除外掛包失敗",
"InvalidPluginPackage": "無效外掛包",
"UnsupportedPluginPackage": "不支援的外掛包",
"UnknownError": "未知錯誤"
}
}

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "垃圾桶",
"ARCHIVE_NAME": "封存"
},
"QUOTA": {
"TITLE": "配額使用"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "重新載入郵件清單",
@ -310,7 +321,7 @@
"STORE_PRIVATE_KEY_IN_GNUPG": "以 GnuPG 儲存私鑰在伺服器上",
"BACKUP_ON_SERVER": "在伺服器中儲存 (已加密)",
"BACKUP_PUBLIC_KEY_ON_SERVER": "在伺服器中儲存公鑰",
"BACKUP_PRIVATE_KEY_ON_SERVER": "在伺服器中儲存私鑰",
"BACKUP_PRIVATE_KEY_ON_SERVER": "在伺服器中儲存私鑰"
},
"SMIME": {
"POPUP_IMPORT_TITLE": "匯入 S\/MIME 證書",

View file

@ -88,8 +88,19 @@
"TRASH_NAME": "已删除邮件",
"ARCHIVE_NAME": "存档"
},
"QUOTA": {
"TITLE": "存储空间使用量"
"IMAP": {
"ACL_RIGHTS": "Rights",
"ACL_A": "Administer",
"ACL_E": "Expunge",
"ACL_I": "Insert messages",
"ACL_K": "Create sub-folders",
"ACL_L": "Show messages",
"ACL_P": "Send mail",
"ACL_R": "Select folder",
"ACL_S": "\\SEEN tag",
"ACL_T": "\\DELETED tag",
"ACL_W": "Other tags",
"ACL_X": "Delete\/Rename folder"
},
"MESSAGE_LIST": {
"BUTTON_RELOAD": "刷新消息列表",

View file

@ -75,7 +75,12 @@
</tbody>
</table>
<h3>PHP optional extensions</h3>
<h3>PHP</h3>
<div data-bind="hidden: php64" class="alert">
<i class="fontastic" style="color:red"></i>
PHP is running in 32bit mode. <a href="https://github.com/the-djmaze/snappymail/issues/830#issuecomment-1372112793">read here</a>
</div>
<table class="table table-hover table-bordered"><thead><tr>
<th>Extension</th>
<th>Loaded</th>

View file

@ -17,7 +17,7 @@
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'GnuPG',
label: gnuPGversion,
value: capaGnuPG
}
}"></div>

View file

@ -11,62 +11,62 @@
<span data-bind="visible: !create(), text: identifier"></span>
</div>
<div class="control-group">
<label>Rights</label>
<label data-i18n="IMAP/ACL_RIGHTS"></label>
<table class="table table-hover table-bordered" style="width:auto"><tbody>
<tr title="SETACL/DELETEACL/GETACL/LISTRIGHTS">
<td>Administer rights</td>
<td data-i18n="IMAP/ACL_A"></td>
<td><input type="checkbox" value="a" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="SELECT the mailbox, perform STATUS">
<td>Select folder</td>
<td data-i18n="IMAP/ACL_R"></td>
<td><input type="checkbox" value="r" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="Mailbox is visible to LIST/LSUB commands, SUBSCRIBE mailbox">
<td>Show messages</td>
<td data-i18n="IMAP/ACL_L"></td>
<td><input type="checkbox" value="l" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="perform APPEND, COPY into mailbox">
<td>Insert messages</td>
<td data-i18n="IMAP/ACL_I"></td>
<td><input type="checkbox" value="i" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="set or clear \SEEN flag via STORE, also set \SEEN during APPEND/COPY/FETCH BODY[...]">
<td>\SEEN flag</td>
<td data-i18n="IMAP/ACL_S"></td>
<td><input type="checkbox" value="s" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="set or clear \DELETED flag via STORE, set \DELETED flag during APPEND/COPY">
<td>\DELETED flag</td>
<td data-i18n="IMAP/ACL_T"></td>
<td><input type="checkbox" value="t" data-bind="checked: rights, disable: mine" /></td>
</tr>
<!--
<tr title="STORE DELETED flag, perform EXPUNGE">
<td>Old \DELETED</td>
<td data-i18n="IMAP/ACL_T">Old</td>
<td><input type="checkbox" value="d" data-bind="checked: rights, disable: mine" /></td>
</tr>
-->
<tr title="set or clear flags other than \SEEN and \DELETED via STORE, also set them during APPEND/COPY">
<td>Other flags</td>
<td data-i18n="IMAP/ACL_W"></td>
<td><input type="checkbox" value="w" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="send mail to submission address for mailbox, not enforced by IMAP4 itself">
<td>Send mail</td>
<td data-i18n="IMAP/ACL_P"></td>
<td><input type="checkbox" value="p" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="CREATE new sub-mailboxes in any implementation-defined hierarchy, parent mailbox for the new mailbox name in RENAME">
<td>Create sub-folders</td>
<td data-i18n="IMAP/ACL_K"></td>
<td><input type="checkbox" value="k" data-bind="checked: rights, disable: mine" /></td>
</tr>
<!--
<tr title="CREATE new sub-mailboxes in any implementation-defined hierarchy">
<td>CREATE_OLD</td>
<td data-i18n="IMAP/ACL_K">Old</td>
<td><input type="checkbox" value="c" data-bind="checked: rights, disable: mine" /></td>
</tr>
-->
<tr title="DELETE mailbox, old mailbox name in RENAME">
<td>Delete/Rename folder</td>
<td data-i18n="IMAP/ACL_X"></td>
<td><input type="checkbox" value="x" data-bind="checked: rights, disable: mine" /></td>
</tr>
<tr title="perform EXPUNGE and expunge as a part of CLOSE">
<td>Expunge</td>
<td data-i18n="IMAP/ACL_E"></td>
<td><input type="checkbox" value="e" data-bind="checked: rights, disable: mine" /></td>
</tr>
</tbody></table>

View file

@ -25,6 +25,7 @@ ko.utils = {
: node => node.cloneNode(true)),
setDomNodeChildren: (domNode, childNodes) => {
// domNode.replaceChildren(...childNodes);
ko.utils.emptyDomNode(domNode);
childNodes && domNode.append(...childNodes);
},

2483
vendors/marked/marked.js vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,6 @@
*/
const TurndownService = (() => {
'use strict';
const repeat = (character, count) => Array(count + 1).join(character),