diff --git a/localization/ar.inc b/localization/ar.inc
new file mode 100644
index 0000000..5892a61
--- /dev/null
+++ b/localization/ar.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/ar_SA.inc b/localization/ar_SA.inc
new file mode 100644
index 0000000..b21a5ad
--- /dev/null
+++ b/localization/ar_SA.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/bg_BG.inc b/localization/bg_BG.inc
new file mode 100644
index 0000000..4b4da6f
--- /dev/null
+++ b/localization/bg_BG.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/cs_CZ.inc b/localization/cs_CZ.inc
new file mode 100644
index 0000000..cc9e6ee
--- /dev/null
+++ b/localization/cs_CZ.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/da_DK.inc b/localization/da_DK.inc
new file mode 100644
index 0000000..45cfec8
--- /dev/null
+++ b/localization/da_DK.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/de_CH.inc b/localization/de_CH.inc
new file mode 100644
index 0000000..9f17640
--- /dev/null
+++ b/localization/de_CH.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/de_DE.inc b/localization/de_DE.inc
index 01a9013..9f17640 100644
--- a/localization/de_DE.inc
+++ b/localization/de_DE.inc
@@ -1,6 +1,5 @@
diff --git a/localization/el_GR.inc b/localization/el_GR.inc
new file mode 100644
index 0000000..c99bcdf
--- /dev/null
+++ b/localization/el_GR.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/en_CA.inc b/localization/en_CA.inc
new file mode 100644
index 0000000..b1f3b89
--- /dev/null
+++ b/localization/en_CA.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/en_GB.inc b/localization/en_GB.inc
new file mode 100644
index 0000000..04ea2b0
--- /dev/null
+++ b/localization/en_GB.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/es_419.inc b/localization/es_419.inc
new file mode 100644
index 0000000..80e6de7
--- /dev/null
+++ b/localization/es_419.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/es_AR.inc b/localization/es_AR.inc
new file mode 100644
index 0000000..80e6de7
--- /dev/null
+++ b/localization/es_AR.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/es_ES.inc b/localization/es_ES.inc
new file mode 100644
index 0000000..80e6de7
--- /dev/null
+++ b/localization/es_ES.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/et_EE.inc b/localization/et_EE.inc
new file mode 100644
index 0000000..595dc12
--- /dev/null
+++ b/localization/et_EE.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/fi_FI.inc b/localization/fi_FI.inc
new file mode 100644
index 0000000..51d217b
--- /dev/null
+++ b/localization/fi_FI.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/fr_FR.inc b/localization/fr_FR.inc
index e745b90..9be8061 100644
--- a/localization/fr_FR.inc
+++ b/localization/fr_FR.inc
@@ -1,6 +1,5 @@
diff --git a/localization/he_IL.inc b/localization/he_IL.inc
new file mode 100644
index 0000000..d509f81
--- /dev/null
+++ b/localization/he_IL.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/hu_HU.inc b/localization/hu_HU.inc
new file mode 100644
index 0000000..eeb3ff0
--- /dev/null
+++ b/localization/hu_HU.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/id_ID.inc b/localization/id_ID.inc
new file mode 100644
index 0000000..28ba802
--- /dev/null
+++ b/localization/id_ID.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/it_IT.inc b/localization/it_IT.inc
new file mode 100644
index 0000000..a710698
--- /dev/null
+++ b/localization/it_IT.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/ja_JP.inc b/localization/ja_JP.inc
new file mode 100644
index 0000000..5397c88
--- /dev/null
+++ b/localization/ja_JP.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/ko_KR.inc b/localization/ko_KR.inc
new file mode 100644
index 0000000..a00715b
--- /dev/null
+++ b/localization/ko_KR.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/lt_LT.inc b/localization/lt_LT.inc
new file mode 100644
index 0000000..6cc5c26
--- /dev/null
+++ b/localization/lt_LT.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/lv_LV.inc b/localization/lv_LV.inc
new file mode 100644
index 0000000..c16654a
--- /dev/null
+++ b/localization/lv_LV.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/nl_BE.inc b/localization/nl_BE.inc
new file mode 100644
index 0000000..44399da
--- /dev/null
+++ b/localization/nl_BE.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/nl_NL.inc b/localization/nl_NL.inc
index e4aa53d..44399da 100644
--- a/localization/nl_NL.inc
+++ b/localization/nl_NL.inc
@@ -1,6 +1,5 @@
diff --git a/localization/nn_NO.inc b/localization/nn_NO.inc
new file mode 100644
index 0000000..ec10e1a
--- /dev/null
+++ b/localization/nn_NO.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/pl_PL.inc b/localization/pl_PL.inc
new file mode 100644
index 0000000..dde1ad2
--- /dev/null
+++ b/localization/pl_PL.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/pt_BR.inc b/localization/pt_BR.inc
new file mode 100644
index 0000000..20583b6
--- /dev/null
+++ b/localization/pt_BR.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/pt_PT.inc b/localization/pt_PT.inc
new file mode 100644
index 0000000..9913f80
--- /dev/null
+++ b/localization/pt_PT.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/ro_RO.inc b/localization/ro_RO.inc
new file mode 100644
index 0000000..80556ad
--- /dev/null
+++ b/localization/ro_RO.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/ru_RU.inc b/localization/ru_RU.inc
index bd33eaa..7a84236 100644
--- a/localization/ru_RU.inc
+++ b/localization/ru_RU.inc
@@ -1,6 +1,5 @@
diff --git a/localization/sk_SK.inc b/localization/sk_SK.inc
new file mode 100644
index 0000000..e12e4bf
--- /dev/null
+++ b/localization/sk_SK.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/sl_SI.inc b/localization/sl_SI.inc
new file mode 100644
index 0000000..f830fe2
--- /dev/null
+++ b/localization/sl_SI.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/sv_SE.inc b/localization/sv_SE.inc
new file mode 100644
index 0000000..7dc94d6
--- /dev/null
+++ b/localization/sv_SE.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/tr_TR.inc b/localization/tr_TR.inc
new file mode 100644
index 0000000..4ba652d
--- /dev/null
+++ b/localization/tr_TR.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/uk_UA.inc b/localization/uk_UA.inc
new file mode 100644
index 0000000..5735354
--- /dev/null
+++ b/localization/uk_UA.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/vi_VN.inc b/localization/vi_VN.inc
new file mode 100644
index 0000000..dabe7d1
--- /dev/null
+++ b/localization/vi_VN.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/zh_CN.inc b/localization/zh_CN.inc
new file mode 100644
index 0000000..f9f66f9
--- /dev/null
+++ b/localization/zh_CN.inc
@@ -0,0 +1,5 @@
+
diff --git a/localization/zh_TW.inc b/localization/zh_TW.inc
new file mode 100644
index 0000000..f9f66f9
--- /dev/null
+++ b/localization/zh_TW.inc
@@ -0,0 +1,5 @@
+
diff --git a/test/TlsIconTest.php b/test/TlsIconTest.php
index b50e545..817c79d 100644
--- a/test/TlsIconTest.php
+++ b/test/TlsIconTest.php
@@ -11,27 +11,12 @@
final class TlsIconTest extends TestCase
{
-
- /** @var string */
- private $strUnEnCrypted = '
';
-
- /** @var string */
- private $strCryptedTlsv12 = '
';
-
- /** @var string */
- private $strCryptedTlsv12WithCipher = '
';
-
- /** @var string */
- private $strInternal = '
';
-
- /** @var string */
- private $strSendmailCryptedTlsv13WithCipherNoVerify = '
';
-
- /** @var string */
- private $strSendmailCryptedTlsv12WithCipherVerify = '
';
-
- /** @var string */
- private $strStalwartCryptedTlsv13WithCipher = '
';
+ private function assertSubjectContainsIcon($headersProcessed, $title)
+ {
+ $value = $headersProcessed['output']['subject']['value'];
+ $this->assertStringContainsString('src="data:image/svg+xml;charset=UTF-8,', $value);
+ $this->assertStringContainsString('title="' . $title . '"', $value);
+ }
/** @var string */
private $strNewPostfixTLSv13 = '
';
@@ -68,6 +53,7 @@ public function testMessageHeadersNothing()
public function testMessageHeadersNoMatching()
{
$o = new tls_icon();
+ $o->init();
$headersProcessed = $o->message_headers([
'output' => [
'subject' => [
@@ -80,11 +66,19 @@ public function testMessageHeadersNoMatching()
]
]
]);
- $this->assertEquals([
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'Message received over an unencrypted connection!');
+ }
+
+ public function testMessageHeadersNoMatchingWithUrlbaseWithoutTrailingSlash()
+ {
+ $o = new tls_icon();
+ $o->urlbase = 'plugins/tls_icon';
+ $o->init();
+ $headersProcessed = $o->message_headers([
'output' => [
'subject' => [
- 'value' => 'Sent to you' . $this->strUnEnCrypted,
- 'html' => 1,
+ 'value' => 'Sent to you',
],
],
'headers' => (object)[
@@ -92,12 +86,15 @@ public function testMessageHeadersNoMatching()
'received' => 'my header',
]
]
- ], $headersProcessed);
+ ]);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'Message received over an unencrypted connection!');
}
- public function testMessageHeadersTlsWithCipher()
+ public function testMessageHeadersNoMatchingUsesAbsolutePluginAssetUrl()
{
$o = new tls_icon();
+ $o->init();
$headersProcessed = $o->message_headers([
'output' => [
'subject' => [
@@ -106,18 +103,20 @@ public function testMessageHeadersTlsWithCipher()
],
'headers' => (object)[
'others' => [
- 'received' => 'from smtp.github.com (out-21.smtp.github.com [192.30.252.204])
- (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested)
- by mail.example.org (Postfix) with ESMTPS id 46B4C497C2
- for ; Sat, 9 Jul 2022 14:03:01 +0000 (UTC)',
+ 'received' => 'my header',
]
]
]);
- $this->assertEquals([
+ $this->assertStringContainsString('src="data:image/svg+xml;charset=UTF-8,', $headersProcessed['output']['subject']['value']);
+ }
+
+ public function testMessageHeadersTlsWithCipher()
+ {
+ $o = new tls_icon();
+ $headersProcessed = $o->message_headers([
'output' => [
'subject' => [
- 'value' => 'Sent to you' . $this->strCryptedTlsv12WithCipher,
- 'html' => 1,
+ 'value' => 'Sent to you',
],
],
'headers' => (object)[
@@ -128,7 +127,9 @@ public function testMessageHeadersTlsWithCipher()
for ; Sat, 9 Jul 2022 14:03:01 +0000 (UTC)',
]
]
- ], $headersProcessed);
+ ]);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)');
}
public function testMessageHeadersTls()
@@ -149,22 +150,8 @@ public function testMessageHeadersTls()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strCryptedTlsv12,
- 'html' => 1,
- ],
- ],
- 'headers' => (object)[
- 'others' => [
- 'received' => 'from smtp.github.com (out-21.smtp.github.com [192.30.252.204])
- (using TLSv1.2) (No client certificate requested)
- by mail.example.org (Postfix) with ESMTPS id 46B4C497C2
- for ; Sat, 9 Jul 2022 14:03:01 +0000 (UTC)',
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'TLSv1.2');
}
public function testMessageHeadersInternal()
@@ -183,20 +170,8 @@ public function testMessageHeadersInternal()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strInternal,
- 'html' => 1,
- ],
- ],
- 'headers' => (object)[
- 'others' => [
- 'received' => 'by aaa.bbb.ccc (Postfix, from userid 0)
- id A70248414D5; Sun, 26 Apr 2020 16:49:01 +0200 (CEST)',
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'Mail was internal');
}
@@ -325,19 +300,8 @@ public function testMessageHeadersMultiFromWithConfig()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strCryptedTlsv12WithCipher,
- 'html' => 1,
- ],
- ],
- 'headers' => (object)[
- 'others' => [
- 'received' => $inputHeaders,
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)');
}
public function testMessageHeadersMultiFromWithBadConfig()
@@ -364,19 +328,8 @@ public function testMessageHeadersMultiFromWithBadConfig()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strUnEnCrypted,
- 'html' => 1,
- ],
- ],
- 'headers' => (object)[
- 'others' => [
- 'received' => $inputHeaders,
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'Message received over an unencrypted connection!');
}
public function testSendmailTLS13NoVerify()
@@ -397,22 +350,8 @@ public function testSendmailTLS13NoVerify()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strSendmailCryptedTlsv13WithCipherNoVerify,
- 'html' => 1,
- ],
- ],
- 'headers' => (object)[
- 'others' => [
- 'received' => 'from 69-171-232-143.mail-mail.facebook.com (69-171-232-143.mail-mail.facebook.com [69.171.232.143])
- by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BI73F8b1489360
- (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO)
- for ; Sun, 18 Dec 2022 07:03:16 GMT',
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO');
}
public function testSendmailTLS12WithVerify()
@@ -433,22 +372,8 @@ public function testSendmailTLS12WithVerify()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strSendmailCryptedTlsv12WithCipherVerify,
- 'html' => 1,
- ],
- ],
- 'headers' => (object)[
- 'others' => [
- 'received' => 'from smtp.github.com (out-18.smtp.github.com [192.30.252.201])
- by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BGMf4uY685293
- (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK)
- for ; Fri, 16 Dec 2022 22:41:05 GMT',
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK');
}
public function testSendmailTLS13MultipleRecipients()
@@ -469,22 +394,8 @@ public function testSendmailTLS13MultipleRecipients()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strSendmailCryptedTlsv13WithCipherNoVerify,
- 'html' => 1,
- ],
- ],
- 'headers' => (object)[
- 'others' => [
- 'received' => 'from mout.kundenserver.de (mout.kundenserver.de [212.227.126.134])
- by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BLGrgYw3602565
- (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO);
- Wed, 21 Dec 2022 16:53:42 GMT',
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO');
}
public function testStalwartTls()
@@ -505,21 +416,7 @@ public function testStalwartTls()
]
]
]);
- $this->assertEquals([
- 'output' => [
- 'subject' => [
- 'value' => 'Sent to you' . $this->strStalwartCryptedTlsv13WithCipher,
- 'html' => 1,
- ],
- ],
- 'headers' => (object) [
- 'others' => [
- 'received' => 'from mail-yw1-f174.google.com (mail-yw1-f174.google.com [209.85.128.174] (AS15169 Google LLC, US))
- (using TLSv1.3 with cipher TLS13_AES_256_GCM_SHA384)
- by mail.example.org (Stalwart SMTP) with ESMTPS id 36DAF29F3A02098;
- Mon, 16 Jun 2025 13:33:03 +0000',
- ]
- ]
- ], $headersProcessed);
+ $this->assertSame(1, $headersProcessed['output']['subject']['html']);
+ $this->assertSubjectContainsIcon($headersProcessed, 'TLSv1.3 with cipher TLS13_AES_256_GCM_SHA384');
}
}
diff --git a/test/rcmail.php b/test/rcmail.php
index c3e8052..b70412d 100644
--- a/test/rcmail.php
+++ b/test/rcmail.php
@@ -30,6 +30,11 @@ class rcmail
*/
public $config;
+ /**
+ * @var rcmail_output
+ */
+ public $output;
+
/**
* @var self|null
*/
@@ -38,6 +43,7 @@ class rcmail
public function __construct()
{
$this->config = new rcmail_config();
+ $this->output = new rcmail_output();
}
public static function get_instance() {
@@ -48,3 +54,11 @@ public static function get_instance() {
return static::$instance;
}
}
+
+class rcmail_output
+{
+ public function abs_url($path)
+ {
+ return '/' . ltrim($path, '/');
+ }
+}
diff --git a/test/rcube_plugin.php b/test/rcube_plugin.php
index d931491..7084d04 100644
--- a/test/rcube_plugin.php
+++ b/test/rcube_plugin.php
@@ -5,6 +5,12 @@
*/
class rcube_plugin
{
+ public $urlbase = 'plugins/tls_icon/';
+
+ public function url($path) {
+ return rtrim($this->urlbase, '/') . '/' . ltrim($path, '/');
+ }
+
public function gettext($label) {
global $labels;
require_once __DIR__ . '/../localization/en_US.inc';
diff --git a/tls_icon.php b/tls_icon.php
index 4a17f33..388d860 100644
--- a/tls_icon.php
+++ b/tls_icon.php
@@ -10,6 +10,51 @@ class tls_icon extends rcube_plugin
private $icon_img;
private $rcmail;
+ private function asset_url($file)
+ {
+ $file = ltrim($file, '/');
+ $url = '';
+
+ if (method_exists($this, 'url')) {
+ $url = $this->url($file);
+ } elseif (!empty($this->urlbase) && is_string($this->urlbase)) {
+ $url = rtrim($this->urlbase, '/') . '/' . $file;
+ } else {
+ $url = 'plugins/tls_icon/' . $file;
+ }
+
+ if (preg_match('/^(?:[a-z][a-z0-9+.-]*:|\\/)/i', $url)) {
+ return $url;
+ }
+
+ if ($this->rcmail && isset($this->rcmail->output) && method_exists($this->rcmail->output, 'abs_url')) {
+ return $this->rcmail->output->abs_url($url);
+ }
+
+ return $url;
+ }
+
+ private function icon_tag($file, $title)
+ {
+ return '
';
+ }
+
+ private function icon_src($file)
+ {
+ $file = ltrim($file, '/');
+ $path = __DIR__ . '/' . $file;
+
+ if (is_readable($path) && preg_match('/\.svg$/i', $file)) {
+ $svg = @file_get_contents($path);
+
+ if (is_string($svg) && $svg !== '') {
+ return 'data:image/svg+xml;charset=UTF-8,' . rawurlencode($svg);
+ }
+ }
+
+ return $this->asset_url($file);
+ }
+
function init()
{
$this->rcmail = rcmail::get_instance();
@@ -64,10 +109,12 @@ public function message_headers($p)
preg_match_all(tls_icon::SENDMAIL_TLS_REGEX, $Received, $items, PREG_PATTERN_ORDER)
) {
$data = $items[1][0];
- $this->icon_img .= '
';
+ $this->icon_img .= $this->icon_tag('lock.svg', htmlentities($data));
} elseif (preg_match_all(tls_icon::POSTFIX_LOCAL_REGEX, $Received, $items, PREG_PATTERN_ORDER)) {
- $this->icon_img .= '
';
+ $this->icon_img .= $this->icon_tag('blue_lock.svg', $this->gettext('internal'));
} else {
+ // TODO: Mails received from localhost but without TLS are currently flagged insecure
+ $this->icon_img .= $this->icon_tag('unlock.svg', $this->gettext('unencrypted'));
$this->icon_img .= '
';
}
}