summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2024-06-30 19:06:22 +0200
committerSimeon Simeonov2024-06-30 19:06:22 +0200
commit991d0d96e5e079dae7feef4fc9c4736c306f6e57 (patch)
treee5ef1df00ea2b872f74a3c475bc17db400e37a30
parent8459629cd213b551f3ccb6a519247594fa137425 (diff)
Fix legacy ebuilds and fix pkgcheck issues
-rw-r--r--README.md10
-rw-r--r--dev-python/etoolkit/Manifest9
-rw-r--r--dev-python/etoolkit/etoolkit-1.0.0-r1.ebuild41
-rw-r--r--dev-python/etoolkit/etoolkit-1.1.0.ebuild34
-rw-r--r--dev-python/etoolkit/etoolkit-1.2.0.ebuild44
-rw-r--r--dev-python/etoolkit/etoolkit-2.0.0.ebuild5
-rw-r--r--dev-python/pyotp2289/Manifest4
-rw-r--r--dev-python/pyotp2289/pyotp2289-1.1.1.ebuild27
-rw-r--r--dev-python/pyotp2289/pyotp2289-1.2.1.ebuild9
-rw-r--r--mail-client/sylpheed/Manifest8
-rw-r--r--mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch2149
-rw-r--r--mail-client/sylpheed/files/sylpheed-3.7.0-PGP-signature-fix.patch18
-rw-r--r--mail-client/sylpheed/files/sylpheed-3.7.0-master-password.patch1850
-rw-r--r--mail-client/sylpheed/files/sylpheed-3.7.0-signature-box.patch267
-rw-r--r--mail-client/sylpheed/files/sylpheed-3.7.0-version.patch26
-rw-r--r--mail-client/sylpheed/metadata.xml10
-rw-r--r--mail-client/sylpheed/sylpheed-3.7.0-r1.ebuild76
-rw-r--r--metadata/layout.conf9
-rw-r--r--sec-keys/openpgp-keys-simeonsimeonov/Manifest2
-rw-r--r--sec-keys/openpgp-keys-simeonsimeonov/openpgp-keys-simeonsimeonov-20220409.ebuild6
-rw-r--r--www-servers/ngus/Manifest3
-rw-r--r--www-servers/ngus/metadata.xml13
-rw-r--r--www-servers/ngus/ngus-1.0.ebuild31
-rw-r--r--x11-misc/i3lock-extended/Manifest7
-rw-r--r--x11-misc/i3lock-extended/i3lock-extended-2.2.13a.ebuild44
-rw-r--r--x11-misc/i3lock-extended/i3lock-extended-2.2.14.ebuild40
-rw-r--r--x11-misc/i3lock-extended/i3lock-extended-2.2.15.ebuild2
-rw-r--r--x11-terms/terminator/Manifest10
-rw-r--r--x11-terms/terminator/files/terminator-1.91-desktop.patch12
-rw-r--r--x11-terms/terminator/files/terminator-1.91-without-icon-cache.patch16
-rw-r--r--x11-terms/terminator/files/terminator-1.92-single-tab.patch24
-rw-r--r--x11-terms/terminator/metadata.xml22
-rw-r--r--x11-terms/terminator/terminator-2.1.2-r1.ebuild66
-rw-r--r--x11-terms/terminator/terminator-2.1.3-r1.ebuild65
34 files changed, 16 insertions, 4943 deletions
diff --git a/README.md b/README.md
index f00421f..0c95e96 100644
--- a/README.md
+++ b/README.md
@@ -16,19 +16,9 @@ variables in a flexible and secure way - https://github.com/blackm0re/etoolkit
16* dev-python/pyotp2289 - a pure Python 3 implementation of "A One-Time 16* dev-python/pyotp2289 - a pure Python 3 implementation of "A One-Time
17Password System" - RFC-2289. - https://github.com/blackm0re/pyotp2289 17Password System" - RFC-2289. - https://github.com/blackm0re/pyotp2289
18 18
19* mail-client/sylpheed - Sylpheed fork that implements features and fixes not
20present in the official Sylpheed release - https://github.com/blackm0re/sylpheed
21
22* www-servers/ngus - is a minimalist HTTP server written in pure Python and
23intended for receiving file uploads - https://github.com/blackm0re/ngus
24
25* x11-misc/i3lock-extended - a fork of i3lock offering some additional features 19* x11-misc/i3lock-extended - a fork of i3lock offering some additional features
26and niceties - https://github.com/blackm0re/i3lock-extended 20and niceties - https://github.com/blackm0re/i3lock-extended
27 21
28* x11-terms/terminator - contains a "single tab patch" that propagates events
29like 'next_tab', 'swicth_tab', etc instead of consuming them when only a single
30tab is used on the terminal
31
32 22
33## Setup 23## Setup
34 24
diff --git a/dev-python/etoolkit/Manifest b/dev-python/etoolkit/Manifest
index b0d06d9..56111da 100644
--- a/dev-python/etoolkit/Manifest
+++ b/dev-python/etoolkit/Manifest
@@ -1,11 +1,4 @@
1DIST etoolkit-1.0.0.tar.gz 37130 BLAKE2B f9b3aeb9f0a34e4f2c6ccd0919314622c3a7a61aacfb8aca692bcb020adb2669be716b6a9f97ebf7b0e84db92203d37d9b8b888168f160396af2824a81a52087 SHA512 90f51a01a1fcf1b9f6a4139c022930c925e6fa2c5b4403110a0e4527f35fa083f47270a769361632689e03fb28f32487623b9e72611f1755e64e299f4c0de1fb
2DIST etoolkit-1.1.0.tar.gz 38325 BLAKE2B 7e21bf5042715899c61f56c660ce15fc429cf274bafea8b1f344a15d57b2eef8f444786547da90b6fe0f27837b0bd763326bbd578b30d92a6957fbf13c8c0af0 SHA512 6912b7c04e53d728eee4f6df50afcc38b44e7d2d646efaae5ab63198088d11e9f136982478e410c7c5d36996e990682be48b681c177106b47f092b1e18075b65
3DIST etoolkit-1.2.0.tar.gz 38641 BLAKE2B 38bf7b9a68a8165d86487950af651470b13874d7db0e1fc3bdb071ea96db50023ff95f8a04a27b982984f08e319a2698cb2ac37026d6f68e3d1ef73481289d75 SHA512 5e0663640a62160639ca810dde549858dd528f0b6df43f4c2f8ced151e02967c51c53e90dcde0ac9cd2fd7315be8e35836900436b8f1cb0b04f2e054eadeeabc
4DIST etoolkit-1.2.0.tar.gz.asc 858 BLAKE2B 217ff5f338693811bffd339d8d44dc1510ee2719e47b53bb8b9d0f7ed8ff1b4fcd584d97eea5598a49ea86edce1a392abddd1271349d57aa85198f7368f36030 SHA512 3503d84b9e911065d2cd8a70428d5d2d0b60ae90c0fc92ac115c698ae44e40e52e180af6ce7bd14ff24084fb3542322f5562c4d63e54f00e7388940eb884a7b5
5DIST etoolkit-2.0.0.tar.gz 45963 BLAKE2B 22f46ba5a0596d90ce4c9e39aebe423028038971b5966a1ae687437f052bcac4f25de3dbca532a3688883e17daed326306f6a93e919bcbd55f6948a70c12dd58 SHA512 77ccd8aaf073f9f5baf3b5f20209cab824b165e8dba5978015cfdf2bc9e8ecab8e8e6864e70016c210d037e8ec3ede27766a9757dbdfe12518b848ca4377e43b 1DIST etoolkit-2.0.0.tar.gz 45963 BLAKE2B 22f46ba5a0596d90ce4c9e39aebe423028038971b5966a1ae687437f052bcac4f25de3dbca532a3688883e17daed326306f6a93e919bcbd55f6948a70c12dd58 SHA512 77ccd8aaf073f9f5baf3b5f20209cab824b165e8dba5978015cfdf2bc9e8ecab8e8e6864e70016c210d037e8ec3ede27766a9757dbdfe12518b848ca4377e43b
6DIST etoolkit-2.0.0.tar.gz.asc 858 BLAKE2B 49448ef405ebb2c8db052f39cf32ede961993c0c2a0ea0eb52258e2105b4ce2fd760430942880183472d1255be265c833e171c57fc66042db234dff5fdd4789b SHA512 6e872ac393d551198bd61d90a4492d737c54adcf24f97592b8c1877967c8edee0034c21e4abb39cd9a7d6ebbc540bb78a9d1cb5eac0da0b1e6ca1a6a62022c48 2DIST etoolkit-2.0.0.tar.gz.asc 858 BLAKE2B 49448ef405ebb2c8db052f39cf32ede961993c0c2a0ea0eb52258e2105b4ce2fd760430942880183472d1255be265c833e171c57fc66042db234dff5fdd4789b SHA512 6e872ac393d551198bd61d90a4492d737c54adcf24f97592b8c1877967c8edee0034c21e4abb39cd9a7d6ebbc540bb78a9d1cb5eac0da0b1e6ca1a6a62022c48
7EBUILD etoolkit-1.0.0-r1.ebuild 939 BLAKE2B d9d833fd8d5f786a46c30f44ef3ab35e5a69d6bc758cb9e378270004d7a324d9d74955a9316fefcd55cab2d2f347ed77e8ce6731e0d2421ab41d1d69d7f757b5 SHA512 51e75559aff5207fe327e929512e46bac7cdda8df3cd39da73bba20bc7acb6a08c2861ab27610a63545c1dbb51839cace30cbaca5752a9f2dd709ad115ec779e 3EBUILD etoolkit-2.0.0.ebuild 975 BLAKE2B 8064a87e9beb15755b7180b45e18842eced29f83167ff942cbeab0a4e929f5c2d79f5122ca49981eaa258f25403ef489b4fef7bbdb6564ac912680e8f4669aa0 SHA512 8b99cf4b3362735a5899b172acd6ad43bfea74dabd0067fd9a5dd0c52d9a50c8bdac13bec43e89cf1245a242cfee60d52f07ec361c658829c89f37be088d734f
8EBUILD etoolkit-1.1.0.ebuild 758 BLAKE2B 5fb34b7e494784c1390963a1423db5e193e0292437c0b5e73ec118bf1d360068cb927d2129209ccd04877989f09acfe2505f223e4ea7a8881fd6234e83109b1b SHA512 f1fbb10318571855bc57ea484056ce5251bc1fc9363e47e526de360382964413a148b7f2dee23a002c2b37254bd811835e720312ac11122e6f951608376ca611
9EBUILD etoolkit-1.2.0.ebuild 1002 BLAKE2B c123ac3f24d4f8812c8d2088e56475a3778ca45733affec40ffadb59ef03ab0f418243440c80a90dba552313020ae8cd6c378ae79bd1893df213454d21f28fb5 SHA512 fbdea781421e7869d5596acbf9e9961579afee380fc65cab25c6447742789f4c4932ce790ee3ae537d64ea6067ee495504f79fcfc5413fa58ac8e33b7fd37464
10EBUILD etoolkit-2.0.0.ebuild 1002 BLAKE2B 7f275f9460dbe2a8dd0ccee43145da3a9baa26afed185210cf6c87913873c069075f58287685b04c32d5b24c1409c838489ce7dcba21be1b990828045c15363e SHA512 30c50ce49c0741b7cd6fe06951ec4db9c2fe89393673e83ab19b15d541b8a972d3e4b08ae5fcd2b0dfb6b2e2bbf5e3f1b31e1982697acc4a5c08e6993cc71d50
11MISC metadata.xml 400 BLAKE2B ca972e1b3593dba8814075aea91870d3782c2dc72f52925df71087e1901585f45c5fc5bbdf7e95b62fe1e396e0e9caa7e2282414bbd0434362e1984b0a6fad39 SHA512 9dd16ca280c598a6ce48816bf5e6e1b7a11b71e1915c06df0981e12995726fcaeebbe18b8106f793504963202d841ac7cb8c11f6afea77eed71e4b864147e7a4 4MISC metadata.xml 400 BLAKE2B ca972e1b3593dba8814075aea91870d3782c2dc72f52925df71087e1901585f45c5fc5bbdf7e95b62fe1e396e0e9caa7e2282414bbd0434362e1984b0a6fad39 SHA512 9dd16ca280c598a6ce48816bf5e6e1b7a11b71e1915c06df0981e12995726fcaeebbe18b8106f793504963202d841ac7cb8c11f6afea77eed71e4b864147e7a4
diff --git a/dev-python/etoolkit/etoolkit-1.0.0-r1.ebuild b/dev-python/etoolkit/etoolkit-1.0.0-r1.ebuild
deleted file mode 100644
index 4ebb343..0000000
--- a/dev-python/etoolkit/etoolkit-1.0.0-r1.ebuild
+++ /dev/null
@@ -1,41 +0,0 @@
1# Copyright 1999-2022 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=8
5
6DISTUTILS_USE_SETUPTOOLS=rdepend
7PYTHON_COMPAT=( python3_{7..10} )
8
9inherit distutils-r1
10
11DESCRIPTION="A simple toolkit for setting environment variables in a flexible way"
12HOMEPAGE="https://github.com/blackm0re/etoolkit"
13MY_PN="etoolkit"
14MY_P="${MY_PN}-${PV}"
15SRC_URI="mirror://pypi/${MY_P:0:1}/${MY_PN}/${MY_P}.tar.gz"
16KEYWORDS="amd64 arm arm64 hppa ia64 ppc ppc64 riscv s390 x86"
17S="${WORKDIR}/${MY_P}"
18
19LICENSE="BSD"
20SLOT="0"
21IUSE="test"
22RESTRICT="!test? ( test )"
23
24RDEPEND="
25 >=dev-python/cryptography-3.2.0[${PYTHON_USEDEP}]"
26
27BDEPEND="
28 dev-python/setuptools[${PYTHON_USEDEP}]
29 test? (
30 ${RDEPEND}
31 dev-python/pytest[${PYTHON_USEDEP}]
32 )"
33
34python_test() {
35 PYTHONPATH=${S}:${PYTHONPATH} \
36 pytest -vv -p no:httpbin || die "Testing failed with ${EPYTHON}"
37}
38
39python_install_all() {
40 distutils-r1_python_install_all
41}
diff --git a/dev-python/etoolkit/etoolkit-1.1.0.ebuild b/dev-python/etoolkit/etoolkit-1.1.0.ebuild
deleted file mode 100644
index 0d3de77..0000000
--- a/dev-python/etoolkit/etoolkit-1.1.0.ebuild
+++ /dev/null
@@ -1,34 +0,0 @@
1# Copyright 1999-2022 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=8
5
6DISTUTILS_USE_PEP517=setuptools
7PYTHON_COMPAT=( python3_{8..10} )
8
9inherit distutils-r1
10
11DESCRIPTION="A simple toolkit for setting environment variables in a flexible way"
12HOMEPAGE="https://github.com/blackm0re/etoolkit"
13MY_PN="etoolkit"
14MY_P="${MY_PN}-${PV}"
15
16SRC_URI="mirror://pypi/${MY_P:0:1}/${MY_PN}/${MY_P}.tar.gz"
17KEYWORDS="~alpha amd64 arm arm64 hppa ~ia64 ~m68k ~mips ppc ppc64 ~riscv ~s390 sparc x86"
18S="${WORKDIR}/${MY_P}"
19
20LICENSE="GPL-3+"
21SLOT="0"
22
23RDEPEND="
24 >=dev-python/cryptography-3.2.0[${PYTHON_USEDEP}]"
25
26distutils_enable_tests pytest
27
28python_test() {
29 epytest -s
30}
31
32python_install_all() {
33 distutils-r1_python_install_all
34}
diff --git a/dev-python/etoolkit/etoolkit-1.2.0.ebuild b/dev-python/etoolkit/etoolkit-1.2.0.ebuild
deleted file mode 100644
index df53f69..0000000
--- a/dev-python/etoolkit/etoolkit-1.2.0.ebuild
+++ /dev/null
@@ -1,44 +0,0 @@
1# Copyright 1999-2023 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=8
5
6DISTUTILS_USE_PEP517=setuptools
7PYTHON_COMPAT=( python3_{8..12} )
8
9inherit distutils-r1 verify-sig
10
11DESCRIPTION="A simple toolkit for setting environment variables in a flexible way"
12HOMEPAGE="https://github.com/blackm0re/etoolkit"
13MY_PN="etoolkit"
14MY_P="${MY_PN}-${PV}"
15
16SRC_URI="
17 https://github.com/blackm0re/etoolkit/releases/download/${PV}/${MY_P}.tar.gz
18 verify-sig? ( https://github.com/blackm0re/etoolkit/releases/download/${PV}/${MY_P}.tar.gz.asc )
19"
20
21KEYWORDS="amd64 arm arm64 ppc ppc64 sparc x86"
22S="${WORKDIR}/${MY_P}"
23
24LICENSE="GPL-3+"
25SLOT="0"
26
27RDEPEND="
28 >=dev-python/cryptography-3.2.0[${PYTHON_USEDEP}]
29"
30BDEPEND="
31 verify-sig? ( sec-keys/openpgp-keys-simeonsimeonov )
32"
33
34distutils_enable_tests pytest
35
36VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/simeonsimeonov.asc
37
38python_test() {
39 epytest -s
40}
41
42python_install_all() {
43 distutils-r1_python_install_all
44}
diff --git a/dev-python/etoolkit/etoolkit-2.0.0.ebuild b/dev-python/etoolkit/etoolkit-2.0.0.ebuild
index d120e58..2e4b298 100644
--- a/dev-python/etoolkit/etoolkit-2.0.0.ebuild
+++ b/dev-python/etoolkit/etoolkit-2.0.0.ebuild
@@ -18,12 +18,13 @@ SRC_URI="
18 verify-sig? ( https://github.com/blackm0re/etoolkit/releases/download/${PV}/${MY_P}.tar.gz.asc ) 18 verify-sig? ( https://github.com/blackm0re/etoolkit/releases/download/${PV}/${MY_P}.tar.gz.asc )
19" 19"
20 20
21KEYWORDS="amd64 arm arm64 ppc ppc64 sparc x86"
22S="${WORKDIR}/${MY_P}" 21S="${WORKDIR}/${MY_P}"
23 22
24LICENSE="GPL-3+" 23LICENSE="GPL-3+"
25SLOT="0" 24SLOT="0"
26 25
26KEYWORDS="amd64 arm64 x86"
27
27RDEPEND=" 28RDEPEND="
28 >=dev-python/cryptography-3.2.0[${PYTHON_USEDEP}] 29 >=dev-python/cryptography-3.2.0[${PYTHON_USEDEP}]
29" 30"
@@ -33,7 +34,7 @@ BDEPEND="
33 34
34distutils_enable_tests pytest 35distutils_enable_tests pytest
35 36
36VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/simeonsimeonov.asc 37VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/simeonsimeonov.asc
37 38
38python_test() { 39python_test() {
39 epytest -s 40 epytest -s
diff --git a/dev-python/pyotp2289/Manifest b/dev-python/pyotp2289/Manifest
index 066f737..321b3bf 100644
--- a/dev-python/pyotp2289/Manifest
+++ b/dev-python/pyotp2289/Manifest
@@ -1,6 +1,4 @@
1DIST pyotp2289-1.1.1.tar.gz 26118 BLAKE2B b6d197bc24843d4aebc71d1c24f3a714d7c21a1b8002c3112846e7bd891cc39a5396b51e4eedd7a4e0ab5b31930585e0c77321c4dbdae68ebdbb90dc68160402 SHA512 2efa25941d7933851f17a455dd5d5a2f8d94da83d4f159d3fe1967a6b638a2aa2004ac136a21cd8d13098da74864da18b3bd02404ad99b849da83a408470aadc
2DIST pyotp2289-1.2.1.tar.gz 30629 BLAKE2B 4a6d3dd95da20e4c42db3ec6c57a0a5e5b8c470f1b5e0fc4463b48e7724db87a817a0f4aaab7f1dd18b410da82a558ad635b9a197c8fc846114386dced2c149b SHA512 8af43dddf0beb905e5cde92635f6bb6937cab088bba626656671f901934070d941548ed96dd9b965a3db0b58766dc28fab59484aa99986a4072cde681a60dd61 1DIST pyotp2289-1.2.1.tar.gz 30629 BLAKE2B 4a6d3dd95da20e4c42db3ec6c57a0a5e5b8c470f1b5e0fc4463b48e7724db87a817a0f4aaab7f1dd18b410da82a558ad635b9a197c8fc846114386dced2c149b SHA512 8af43dddf0beb905e5cde92635f6bb6937cab088bba626656671f901934070d941548ed96dd9b965a3db0b58766dc28fab59484aa99986a4072cde681a60dd61
3DIST pyotp2289-1.2.1.tar.gz.asc 858 BLAKE2B 0f5e8cd7cd1c45546866889b8fd27b1c8287f711e01f7e69d1c6508f743f512ddd737062506695e9d71ed59f62fba364bdba739d12387dfda67db6ebfcede782 SHA512 3ef207de88553585cbb2b58c6698ac01d67a780eff5ed6fe8ba0bf23eab842ccbbd2653c2f96079b28056c271701f87671bb4b3ae1551a86380f3ec074121536 2DIST pyotp2289-1.2.1.tar.gz.asc 858 BLAKE2B 0f5e8cd7cd1c45546866889b8fd27b1c8287f711e01f7e69d1c6508f743f512ddd737062506695e9d71ed59f62fba364bdba739d12387dfda67db6ebfcede782 SHA512 3ef207de88553585cbb2b58c6698ac01d67a780eff5ed6fe8ba0bf23eab842ccbbd2653c2f96079b28056c271701f87671bb4b3ae1551a86380f3ec074121536
4EBUILD pyotp2289-1.1.1.ebuild 643 BLAKE2B 080438c182ac1d3a1dfb5520ee6071e1ce37a467df8db5823940f148cf380334dc847f3f8787d8a63e6bcddd70e3e99d3a3620f3c8d357175248b3134c7e8257 SHA512 f4a72d9e1c61b14e4ecf2a3b63bb654bdb1a913ea895e70fed5166a019dca1c464be33a5c55c9e9d7e1a9b4eb1a85af812698501b5d79b21c347c44ed66e5b40 3EBUILD pyotp2289-1.2.1.ebuild 903 BLAKE2B 0eee2052148198f5232428ac028cf34f6288964388d213bc115879f03ddc9a2b79b5d006d2b63d31b2aee2e7e3e0ae89939252b85200bfbcdb35e3e259998294 SHA512 ffcc7679a7eb30fb4851eb3188f181f97e9a716463d94e065dec4d60f03553961e1b69a270070e128d3f96cdc8819b7580607ad0e8d5cd5194685810be0983be
5EBUILD pyotp2289-1.2.1.ebuild 910 BLAKE2B a890a8a0d36ea81f9a182ea793fbf6d9e2f4723f7560845d495f2839dd2c100adecaaeb7e909e953667d150a8b8fd517663ce6a0359cddac94a7b52ff1fd2df2 SHA512 d69e6c53d0b2dc0b7628980ad82abd328efa6493cf03b6a3320f1f407760b1b28681387d4f8d615fbb620050a4e3008a564a53f22d024085701f5676b1e44894
6MISC metadata.xml 402 BLAKE2B 3169f6b02fb30ba6db5edd85532a4caf08670e44e27a9b690063549b5561a765e8d2a4b3cd129d196827e2d51e97b7e6da04347c07387cd25aec2792516b89a5 SHA512 2268e0dd77a8771835d0cd97ec124fd47d51a5ceec7f5c21baf62979e021c0aa38aa506740147b9fde97f9af3094f8e4635424043fe06d5db2ddf45398912928 4MISC metadata.xml 402 BLAKE2B 3169f6b02fb30ba6db5edd85532a4caf08670e44e27a9b690063549b5561a765e8d2a4b3cd129d196827e2d51e97b7e6da04347c07387cd25aec2792516b89a5 SHA512 2268e0dd77a8771835d0cd97ec124fd47d51a5ceec7f5c21baf62979e021c0aa38aa506740147b9fde97f9af3094f8e4635424043fe06d5db2ddf45398912928
diff --git a/dev-python/pyotp2289/pyotp2289-1.1.1.ebuild b/dev-python/pyotp2289/pyotp2289-1.1.1.ebuild
deleted file mode 100644
index 4252090..0000000
--- a/dev-python/pyotp2289/pyotp2289-1.1.1.ebuild
+++ /dev/null
@@ -1,27 +0,0 @@
1# Copyright 1999-2022 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=8
5
6DISTUTILS_USE_PEP517=setuptools
7PYTHON_COMPAT=( python3_{8..11} )
8
9inherit distutils-r1
10
11DESCRIPTION="A pure Python 3 implementation of RFC-2289 - 'A One-Time Password System'"
12HOMEPAGE="https://github.com/blackm0re/pyotp2289"
13MY_PN="pyotp2289"
14MY_P="${MY_PN}-${PV}"
15
16SRC_URI="mirror://pypi/${MY_P:0:1}/${MY_PN}/${MY_P}.tar.gz"
17KEYWORDS="amd64 arm arm64 ppc ppc64 x86 ~amd64-linux ~x86-linux"
18S="${WORKDIR}/${MY_P}"
19
20LICENSE="BSD"
21SLOT="0"
22
23distutils_enable_tests pytest
24
25python_install_all() {
26 distutils-r1_python_install_all
27}
diff --git a/dev-python/pyotp2289/pyotp2289-1.2.1.ebuild b/dev-python/pyotp2289/pyotp2289-1.2.1.ebuild
index 2488fba..82f4af9 100644
--- a/dev-python/pyotp2289/pyotp2289-1.2.1.ebuild
+++ b/dev-python/pyotp2289/pyotp2289-1.2.1.ebuild
@@ -1,10 +1,10 @@
1# Copyright 1999-2023 Gentoo Authors 1# Copyright 1999-2024 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2 2# Distributed under the terms of the GNU General Public License v2
3 3
4EAPI=8 4EAPI=8
5 5
6DISTUTILS_USE_PEP517=setuptools 6DISTUTILS_USE_PEP517=setuptools
7PYTHON_COMPAT=( python3_{8..12} ) 7PYTHON_COMPAT=( python3_{8..13} )
8 8
9inherit distutils-r1 verify-sig 9inherit distutils-r1 verify-sig
10 10
@@ -18,19 +18,20 @@ SRC_URI="
18 verify-sig? ( https://github.com/blackm0re/pyotp2289/releases/download/v${PV}/${MY_P}.tar.gz.asc ) 18 verify-sig? ( https://github.com/blackm0re/pyotp2289/releases/download/v${PV}/${MY_P}.tar.gz.asc )
19" 19"
20 20
21KEYWORDS="amd64 arm arm64 ppc ppc64 x86"
22S="${WORKDIR}/${MY_P}" 21S="${WORKDIR}/${MY_P}"
23 22
24LICENSE="BSD" 23LICENSE="BSD"
25SLOT="0" 24SLOT="0"
26 25
26KEYWORDS="amd64 arm arm64 ppc ppc64 x86"
27
27BDEPEND=" 28BDEPEND="
28 verify-sig? ( sec-keys/openpgp-keys-simeonsimeonov ) 29 verify-sig? ( sec-keys/openpgp-keys-simeonsimeonov )
29" 30"
30 31
31distutils_enable_tests pytest 32distutils_enable_tests pytest
32 33
33VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/simeonsimeonov.asc 34VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/simeonsimeonov.asc
34 35
35python_install_all() { 36python_install_all() {
36 distutils-r1_python_install_all 37 distutils-r1_python_install_all
diff --git a/mail-client/sylpheed/Manifest b/mail-client/sylpheed/Manifest
deleted file mode 100644
index 557767e..0000000
--- a/mail-client/sylpheed/Manifest
+++ /dev/null
@@ -1,8 +0,0 @@
1AUX sylpheed-3.7.0-ALL.patch 63415 BLAKE2B c40c55b039d5c457bf555e0b738f8c97925966f75b0357dad7cc4f10f9fbaa9e6156e84e65c7e89880e28b36e286de96761693718faac9d1056f720a2cae8b1e SHA512 96d4edfea92439d2f0dd4dec11305710e8d5513979d4bd96113acdbe2338c9452da64ded98204b7af7c84ed86f22337a0248be109a6a8e20d3d1b62929ce70a3
2AUX sylpheed-3.7.0-PGP-signature-fix.patch 565 BLAKE2B c97f48f7844498323d944d1dc1925601ee470e50da26d4e434eb497b29b59fdf5144d019e30efc7392093422111f7c4bb0ff71a148d553db9fb2a2d26e86599a SHA512 089883841eae1c2ed806db1992ff94dd2906b1e825dbab7ae1b5d484815e446979d9f031851d4506ad0f4b00c462997d3704cbf605f7cbc9292e4b53bff5a0fe
3AUX sylpheed-3.7.0-master-password.patch 53668 BLAKE2B c2e898dbbaa4351fa9306a925c5aab56b6b63df4cebc130be7d44a33434524cf61e279fdc9b743d4b9d0c23dc84eda02c56a8c1b33c2309959a4c03c497ad232 SHA512 dcc987a9835ca2c38f06fe6aafdc9d05b750d691b1d1838b818557776be1129cc90b2982169510495ad30bc5e684b69dd614c747fff27b938620c7e0c8133498
4AUX sylpheed-3.7.0-signature-box.patch 9787 BLAKE2B 664b60b3a3421f12cc76a1a1cea64cd773509f17583e32acd9445581e0bed75289674ce82e018dd688204f932228ed379c6f68c31ff25c09e912ad74cbfe7394 SHA512 f1d1c165b701469cacf24eaab5512750d307145a551013e3ecd20e49817b4cd4d4113bd690d8ff6b1e6e9b1d96a45fb25c9417f5aa455e4063f85fc2d48ed464
5AUX sylpheed-3.7.0-version.patch 626 BLAKE2B fe3b90263c384429f9520bc357066f6cea80f81389cdc915ac30932276d5d3dbde5fdea16f649fb553f81d3487afbbdd41151604bd388e9d3ec12f44c7b9097a SHA512 26765397fe73ea03306513363f217f71b9a344590ad8716b34532279b1ade8c5feac0a9b981951bc58e85ae399c8efaab7435534441002c14a963c4c0b001eb8
6DIST sylpheed-3.7.0.tar.bz2 3612328 BLAKE2B bd8182db8a46b956e12b3da4b15d3ee8184a612e2fb216aca20fd4a022610b17416f994d36b390a5a92835915e95f08bd59bf71154a86962c9564162be891f21 SHA512 490837528bf7ba9d26994cd5fff00b6e5390a127419b9d0efd9fc25c38be1291d55c5b8daebdf5ca9d9159a51c938449e76212328f3eae40cc039db88cb5caa4
7EBUILD sylpheed-3.7.0-r1.ebuild 1801 BLAKE2B 35c5232beea2bf460817409bd552e3914b7d5453db4e5b2438cfd70be617b08d278b019873a48cc6430f75319289d077c3840023513cac31d4e4609a1d710a41 SHA512 13a808da1cb04f34c727cf1eaf3c8bca2158073efa459eb446c065fc46a6798d232d18af0aa8c221d2b22399f3c4c47f778ca45bcb5a59bffb24c03a91452e79
8MISC metadata.xml 333 BLAKE2B 45fdb2ceec80729593ac9d58a70399596ebff40c7eb68010f02a6025347b19775c8ec7483006e6c2abe826e6426cd53685835ccf2ed2b4670b16be8ea3da974c SHA512 0e5e8e8dcf6bbb73a907313cbc5bd234bc31c645759bd9ec33193fae5bd9b9b08cd884ea4adcb941b6693049f27522cd210bc3e7e6d14acaf1db595883ddbd4e
diff --git a/mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch b/mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch
deleted file mode 100644
index 0a34542..0000000
--- a/mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch
+++ /dev/null
@@ -1,2149 +0,0 @@
1diff --git a/.gitignore b/.gitignore
2new file mode 100644
3index 0000000..9742351
4--- /dev/null
5+++ b/.gitignore
6@@ -0,0 +1,21 @@
7+*.o
8+*.lo
9+*.la
10+Makefile
11+config.h
12+config.log
13+config.status
14+libsylph/.deps/
15+libsylph/.libs/
16+libtool
17+plugin/attachment_tool/.deps/
18+plugin/attachment_tool/.libs/
19+plugin/test/.deps/
20+plugin/test/.libs/
21+po/Makefile.in
22+po/POTFILES
23+src/.deps/
24+src/.libs/
25+src/sylpheed
26+src/sylpheed.rc
27+stamp-h1
28diff --git a/README.md b/README.md
29new file mode 100644
30index 0000000..1cf6d21
31--- /dev/null
32+++ b/README.md
33@@ -0,0 +1,219 @@
34+## Implemented features and fixes not present in the official Sylpheed release
35+
36+- (fix)
37+ PGP signature not verified properly when the message has no newline
38+ at the end. https://sylpheed.sraoss.jp/redmine/issues/288
39+
40+- (feature)
41+ Make it possible to select "Show signature check result in a popup window"
42+ only for bad signatures.
43+
44+- (feature)
45+ Support for encrypting and storing encrypted passwords using a master password.
46+ Read bellow for more details!
47+
48+
49+## Master password
50+
51+The master password feature is developed by Simeon Simeonov (sgs)
52+and is currently in an experimental state.
53+
54+
55+### Motivation
56+
57+Currently Sylpheed is storing passwords in plain-text. One can always refrain
58+from storing passwords and let Sylpheed prompt for them, but the more accounts
59+one has, the more annoying this becomes.
60+
61+The goal is to have the passwords stored in a secure way and let Sylpheed only
62+prompt for the master password.
63+
64+
65+### Security goals
66+
67+- attacker (A) should not be able to derive the password from the digest.
68+
69+- A should not be able to derive the master password even if she has
70+ read and write access to the storage.
71+
72+- A should not be able to determine the length of the encrypted password
73+ even if she has read and write access to the storage.
74+
75+- A should not be able to craft an edited password without obtaining the
76+ master password.
77+
78+- A should not be able to compromise the master password or force Sylpheed
79+ to store decrypted passwords even if she has access to a running Sylpheed.
80+
81+- a warning / prompt should be given if a user accidentally types in a "wrong"
82+ master password before decryption is initiated.
83+
84+
85+### Usage in Sylpheed
86+
87+- backup your Sylpheed profile (often $HOME/.sylpheed-2.0)!
88+
89+- start Sylpheed and open "Configuration" -> "Common preferences..."!
90+
91+- select the "Master password" tab, enable "Use master password" and
92+ apply the changes!
93+
94+- restart Sylpheed (exit and then start Sylpheed again)!
95+
96+- you will be asked to type and verify a new master password.
97+
98+- set your new passwords from the "Configuration" -> "Edit accounts..."!
99+
100+- select "Automatically unload master password after session initialization"
101+ for increased security and decreased convenience.
102+
103+Note:
104+Sylpheed will automatically convert existing stored passwords, but it will not
105+touch your backups. You will have to remove all remnants of plain-text
106+passwords manually.
107+
108+
109+### Choice of cryptographic primitives
110+
111+The primary concern when selecting cryptographic primitives was portability.
112+The desire was to go for primitives that are both strong and available in all
113+supported production distributions of OpenSSL and LibreSSL.
114+
115+
116+#### Cipher
117+
118+When it comes to implementation, there are several advantages in using stream
119+cipher or a block cipher that behaves like a stream cipher when used in a
120+certain mode of operation. One is avoiding to deal with padding.
121+
122+AES-256 operating in CFB was selected for these reasons.
123+ChaCha20 should be considered as a replacement in the future.
124+
125+
126+#### Hash-function
127+
128+Hash-functions are used for:
129+- key derivation
130+- plain-text digest
131+
132+Those operations do not have to use the same hash-function.
133+(See the "Encryption & decryption scheme" section for more details!)
134+
135+Key-derivation:
136+Since AES-256 uses a 256 bits key, we need a hash-function with at least
137+the same digest size or bigger.
138+Since the digest (which is the key itself) is considered confidential and is
139+stored only in memory for only a limited amount of time, SHA-256 is considered
140+sufficiently strong for that purpose.
141+
142+Size + plain-text + padding digest:
143+Since the digest is created of both the plain-text and the plain-text size,
144+as well as being encrypted, SHA-256 is considered sufficiently strong.
145+SHA-512 may increase security at the price of adding additional 32 bytes
146+to the encrypted password digest.
147+
148+Stronger hash-functions like SHA-3 or BLAKE2b can be considered as a
149+replacement in the future.
150+
151+
152+#### Master password digest
153+
154+In order to be able to decide whether the user typed a "wrong" master password,
155+before attempting to decrypt, Sylpheed stores a digest of the master password
156+in 'master_password_hash' in sylpheedrc.
157+100000 iterations of PBKDF2_HMAC with SHA-512 and 16 bytes salt is used.
158+Note that this digest is useless as a key and even if a plain-text that
159+produces the same digest is found, it will most probably be useless as a
160+master-password.
161+
162+
163+### Encryption & decryption scheme
164+
165+
166+#### Encryption
167+
168+
169+Input:
170+
171+- plain-text password to be encrypted (P)
172+
173+- plain-text master-password used for key derivation (M)
174+
175+- integer minimum password length (0 < L < 100)
176+
177+
178+Output:
179+
180+- an encrypted password digest (base64) (B)
181+
182+
183+Operation:
184+
185+- generate 16 bytes of random data to be used as a salt (S)
186+
187+- derive the key (K): K = SHA_256(S + M)
188+
189+- produce a 2 byte string (N) indicating the length of P
190+
191+- generate random padding and set N:
192+ - if the length of P < L, produce L - P bytes of random data (R), N = "%02d"
193+ - if the length of P >= L, N = "-1"
194+
195+- produce a hash digest (H): H = SHA_256(N + P + R (if the length of P < L))
196+
197+- encrypt (E): E = AES_256_CFB_ENCRYPT(H + N + P + R (if the length of P < L), K)
198+
199+- B = mpes1:BASE64_ENCODE(S + E)
200+
201+example:
202+mpes1:vo7lsIpD7i6byBA6+vlUoF4OVDfEe+aYRRk4FRtfJ2gMY8M43Kj6WfdfgbViIOl83bI4XEc96okhPW5Mla813aAR1gbPjDg0xmCyIbWOiUv/dg==
203+
204+
205+#### Decryption
206+
207+
208+Input:
209+
210+- encrypted password digest (base64) (B)
211+
212+- plain-text master-password used for key derivation (M)
213+
214+
215+Output:
216+
217+- plain-text password (P)
218+
219+
220+Operation:
221+
222+- remove the prefix (mpes1:) and base64-decode the rest of the digest: B = BASE64_DECODE(B)
223+
224+- fetch the first 16 bytes for the salt: S = B[0 : 15]
225+
226+- derive the key (K): K = SHA_256(S + M)
227+
228+- decrypt the rest of B (D): D = AES_256_CFB_DECRYPT(B[16 :], K)
229+
230+- extract the first 16 bytes for the hash digest (H): H = D[0 : 15]
231+
232+- in order to detect data-inconsistency, assert H == SHA_256(D[16 :])
233+
234+- extract the next 2 bytes for the length of P (N): N = D[16 : 17]
235+
236+- fetch the plain-text:
237+ - if N == "-1" the password is the remaining bytes of D: P = D[18 :]
238+ - if N != "-1", extract the next N-bytes from D: P = D[18 : (18 + N)]
239+
240+
241+### Limitations
242+
243+- currently only the 'password' and 'smtp_password' keys in accountrc
244+ are encrypted.
245+ A mechanism that allows for any key and even folders to be encrypted
246+ should be considered in the future.
247+
248+- currently it is not possible to select alternative ciphers, hash-functions
249+ and modes of operation (without editing the source code).
250+
251+- currently it is not possible to change your master password without having to
252+ (re)set your passwords manually.
253diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am
254index 8f2b76b..40d26a9 100644
255--- a/libsylph/Makefile.am
256+++ b/libsylph/Makefile.am
257@@ -19,6 +19,7 @@ libsylph_0_la_SOURCES = \
258 folder.c \
259 html.c \
260 imap.c \
261+ masterpassword.c \
262 mbox.c \
263 md5.c \
264 md5_hmac.c \
265@@ -62,6 +63,7 @@ libsylph_0include_HEADERS = \
266 folder.h \
267 html.h \
268 imap.h \
269+ masterpassword.h \
270 mbox.h \
271 md5.h \
272 md5_hmac.h \
273diff --git a/libsylph/Makefile.in b/libsylph/Makefile.in
274index 6e3c999..ac8fefd 100644
275--- a/libsylph/Makefile.in
276+++ b/libsylph/Makefile.in
277@@ -129,8 +129,8 @@ libsylph_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
278 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
279 am_libsylph_0_la_OBJECTS = account.lo base64.lo codeconv.lo \
280 customheader.lo displayheader.lo filter.lo folder.lo html.lo \
281- imap.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo nntp.lo \
282- pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \
283+ imap.lo masterpassword.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo \
284+ nntp.lo pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \
285 procmime.lo procmsg.lo quoted-printable.lo recv.lo session.lo \
286 smtp.lo socket.lo socks.lo ssl.lo ssl_hostname_validation.lo \
287 stringtable.lo sylmain.lo unmime.lo utils.lo uuencode.lo \
288@@ -402,6 +402,7 @@ libsylph_0_la_SOURCES = \
289 folder.c \
290 html.c \
291 imap.c \
292+ masterpassword.c \
293 mbox.c \
294 md5.c \
295 md5_hmac.c \
296@@ -445,6 +446,7 @@ libsylph_0include_HEADERS = \
297 folder.h \
298 html.h \
299 imap.h \
300+ masterpassword.h \
301 mbox.h \
302 md5.h \
303 md5_hmac.h \
304@@ -579,6 +581,7 @@ distclean-compile:
305 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folder.Plo@am__quote@
306 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Plo@am__quote@
307 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Plo@am__quote@
308+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/masterpassword.Plo@am__quote@
309 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox.Plo@am__quote@
310 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@
311 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_hmac.Plo@am__quote@
312diff --git a/libsylph/defs.h b/libsylph/defs.h
313index 9e3f82b..67325bf 100644
314--- a/libsylph/defs.h
315+++ b/libsylph/defs.h
316@@ -58,6 +58,9 @@
317 #define DISPLAY_HEADER_RC "dispheaderrc"
318 #define MENU_RC "menurc"
319 #define ACTIONS_RC "actionsrc"
320+#ifdef USE_SSL
321+#define SECURE_RC "securerc"
322+#endif
323 #define COMMAND_HISTORY "command_history"
324 #define TEMPLATE_DIR "templates"
325 #define TMP_DIR "tmp"
326diff --git a/libsylph/imap.c b/libsylph/imap.c
327index aa0d737..e1c7cfd 100644
328--- a/libsylph/imap.c
329+++ b/libsylph/imap.c
330@@ -52,6 +52,7 @@
331 #include "utils.h"
332 #include "prefs_common.h"
333 #include "virtual.h"
334+#include "masterpassword.h"
335
336 #define IMAP4_PORT 143
337 #if USE_SSL
338@@ -705,9 +706,13 @@ static gint imap_session_connect(IMAPSession *session)
339 account = (PrefsAccount *)(SESSION(session)->data);
340
341 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
342- SESSION(session)->server, SESSION(session)->port);
343-
344- pass = account->passwd;
345+ SESSION(session)->server, SESSION(session)->port);
346+ if (master_password_active()) {
347+ pass = decrypt_with_master_password(account->passwd);
348+ /* a new string is allocated. To be removed ... */
349+ } else {
350+ pass = account->passwd;
351+ }
352 if (!pass)
353 pass = account->tmp_pass;
354 if (!pass) {
355@@ -770,8 +775,11 @@ static gint imap_session_connect(IMAPSession *session)
356 #endif
357
358 if (!session->authenticated &&
359- imap_auth(session, account->userid, pass, account->imap_auth_type)
360- != IMAP_SUCCESS) {
361+ imap_auth(session, account->userid, pass, account->imap_auth_type)
362+ != IMAP_SUCCESS) {
363+ if (master_password_active()) {
364+ g_free(pass); /* remove the decrypted password */
365+ }
366 if (account->tmp_pass) {
367 g_free(account->tmp_pass);
368 account->tmp_pass = NULL;
369@@ -780,6 +788,10 @@ static gint imap_session_connect(IMAPSession *session)
370 return IMAP_AUTHFAIL;
371 }
372
373+ if (master_password_active()) {
374+ g_free(pass); /* remove the decrypted password */
375+ }
376+
377 return IMAP_SUCCESS;
378 }
379
380diff --git a/libsylph/masterpassword.c b/libsylph/masterpassword.c
381new file mode 100644
382index 0000000..3d7af94
383--- /dev/null
384+++ b/libsylph/masterpassword.c
385@@ -0,0 +1,216 @@
386+/*
387+ * LibSylph -- E-Mail client library
388+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto
389+ *
390+ * This library is free software; you can redistribute it and/or
391+ * modify it under the terms of the GNU Lesser General Public
392+ * License as published by the Free Software Foundation; either
393+ * version 2.1 of the License, or (at your option) any later version.
394+ *
395+ * This library is distributed in the hope that it will be useful,
396+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
397+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
398+ * Lesser General Public License for more details.
399+ *
400+ * You should have received a copy of the GNU Lesser General Public
401+ * License along with this library; if not, write to the Free Software
402+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
403+ */
404+
405+#ifdef HAVE_CONFIG_H
406+#include "config.h"
407+#endif
408+
409+#include <glib/gi18n.h>
410+
411+#include "prefs_common.h"
412+#include "ssl.h"
413+#include "utils.h"
414+#include "masterpassword.h"
415+
416+
417+gchar *master_password;
418+gboolean master_password_enabled_on_init;
419+
420+void set_master_password(const char *password) {
421+ master_password = password;
422+}
423+
424+gchar *get_master_password(void) {
425+ return master_password;
426+}
427+
428+void cleanse_buffer(void *buf, size_t len) {
429+#if USE_SSL
430+ OPENSSL_cleanse(buf, len);
431+#else
432+ memset(buf, 0, len); /* better than nothing */
433+#endif
434+}
435+
436+void unload_master_password(void) {
437+
438+ debug_print("Unloading master password\n");
439+ if (master_password == NULL) {
440+ /* not loaded / already unloaded */
441+ return;
442+ }
443+ cleanse_buffer(master_password, strlen(master_password));
444+ g_free(master_password);
445+ master_password = NULL;
446+ debug_print("Master password unloaded\n");
447+
448+}
449+
450+gint mpes_string_prefix(const gchar *str) {
451+
452+ /* this function will be expanded in time as the format changes */
453+ if (g_str_has_prefix(str, "mpes1:"))
454+ return 6;
455+ return 0;
456+
457+}
458+
459+gboolean master_password_active(void) {
460+
461+#if USE_SSL
462+ return ((master_password != NULL) &&
463+ prefs_common.use_master_password);
464+#else
465+ return FALSE;
466+#endif
467+
468+}
469+
470+gchar *decrypt_with_master_password(const gchar *str) {
471+
472+#if USE_SSL
473+ gchar *new_str;
474+ gint str_prefix;
475+
476+ if ((!str) || (!prefs_common.use_master_password))
477+ return g_strdup(str);
478+
479+ if (master_password == NULL) {
480+ /* we have empty or auto unloaded master password */
481+ if ((!prefs_common.auto_unload_master_password) ||
482+ (check_master_password_interactively(3) != MP_RC_OK)) {
483+ return g_strdup(str);
484+ }
485+ debug_print("Reloaded master password\n");
486+ }
487+
488+ str_prefix = mpes_string_prefix(str);
489+ if (!str_prefix)
490+ return g_strdup(str);
491+
492+ if (decrypt_data(&new_str,
493+ str + str_prefix,
494+ master_password,
495+ strlen(str) + 1 - str_prefix) != MP_RC_OK) {
496+ OPENSSL_cleanse(new_str, strlen(new_str));
497+ g_free(new_str);
498+ return g_strdup(str);
499+ }
500+
501+ return new_str;
502+#else
503+ return g_strdup(str);
504+#endif
505+
506+}
507+
508+gchar *encrypt_with_master_password(const gchar *str) {
509+
510+#if USE_SSL
511+ gchar *new_str, *mpes1_str;
512+ gint length_encrypted;
513+
514+ if ((!str) || (!master_password_active()))
515+ return NULL;
516+
517+ /*
518+ * unlike the decrypt function, here it is up to the caller
519+ * to make sure that auto unloaded master password is handled properly
520+ */
521+
522+ if (encrypt_data(&new_str,
523+ &length_encrypted,
524+ str,
525+ master_password,
526+ strlen(str) + 1,
527+ prefs_common.encrypted_password_min_length,
528+ TRUE) != MP_RC_OK) {
529+ g_free(new_str);
530+ return NULL;
531+ }
532+
533+ mpes1_str = g_strdup_printf("mpes1:%s", new_str);
534+ g_free(new_str);
535+
536+ return mpes1_str;
537+#else
538+ return NULL;
539+#endif
540+
541+}
542+
543+#if USE_SSL
544+gint set_master_password_interactively(guint max_attempts) {
545+
546+ if (master_password == NULL)
547+ master_password = input_set_new_password(max_attempts);
548+
549+ if (master_password == NULL)
550+ return 1;
551+
552+ if (generate_password_hash(
553+ &prefs_common.master_password_hash,
554+ master_password,
555+ NULL) != MP_RC_OK) {
556+ /* should not really happen unless buggy code / library */
557+ g_free(prefs_common.master_password_hash);
558+ prefs_common.master_password_hash = NULL;
559+ debug_print(_("Could not generate master password hash"));
560+ return 1;
561+ }
562+
563+ prefs_common_write_config();
564+ return 0;
565+
566+}
567+
568+gint check_master_password_interactively(guint max_attempts) {
569+
570+ guint cnt;
571+
572+ g_return_val_if_fail(max_attempts > 0, 1);
573+ g_return_val_if_fail(prefs_common.master_password_hash != NULL, 1);
574+
575+ if (master_password != NULL) {
576+ /* password already cached */
577+ debug_print("Master password already cached\n");
578+ return check_password(master_password,
579+ prefs_common.master_password_hash);
580+ }
581+
582+ for (cnt = 0; cnt < max_attempts; ++cnt) {
583+ master_password = input_query_master_password();
584+ if (master_password == NULL) {
585+ /* input canceled or query_master_password_func not set */
586+ continue;
587+ }
588+ if (check_password(master_password,
589+ prefs_common.master_password_hash) == MP_RC_OK) {
590+ return MP_RC_OK; /* match */
591+ }
592+ debug_print(_("Wrong master password entered (%d)\n"), cnt);
593+ OPENSSL_cleanse(master_password, strlen(master_password));
594+ g_free(master_password);
595+ master_password = NULL;
596+ }
597+
598+ return 1; /* no match */
599+
600+}
601+#endif /* USE_SSL */
602diff --git a/libsylph/masterpassword.h b/libsylph/masterpassword.h
603new file mode 100644
604index 0000000..7254643
605--- /dev/null
606+++ b/libsylph/masterpassword.h
607@@ -0,0 +1,47 @@
608+/*
609+ * LibSylph -- E-Mail client library
610+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto
611+ *
612+ * This library is free software; you can redistribute it and/or
613+ * modify it under the terms of the GNU Lesser General Public
614+ * License as published by the Free Software Foundation; either
615+ * version 2.1 of the License, or (at your option) any later version.
616+ *
617+ * This library is distributed in the hope that it will be useful,
618+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
619+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
620+ * Lesser General Public License for more details.
621+ *
622+ * You should have received a copy of the GNU Lesser General Public
623+ * License along with this library; if not, write to the Free Software
624+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
625+ */
626+
627+#ifndef __MASTERPASSWORD_H__
628+#define __MASTERPASSWORD_H__
629+
630+#ifdef HAVE_CONFIG_H
631+#include "config.h"
632+#endif
633+
634+#define MP_RC_OK 0
635+#define MP_RC_WRONG_HASH_OR_KEY 1
636+#define MP_RC_INVALID_FORMAT 2 /* invalid digest format */
637+
638+extern gchar *master_password;
639+extern gboolean master_password_enabled_on_init; /* m.p. enabled on init? */
640+void set_master_password(const char *password);
641+gchar *get_master_password(void);
642+void cleanse_buffer(void *buf, size_t len);
643+void unload_master_password(void);
644+gint mpes_string_prefix(const gchar *str);
645+gboolean master_password_active(void);
646+gchar *decrypt_with_master_password(const gchar *str);
647+gchar *encrypt_with_master_password(const gchar *str);
648+
649+#if USE_SSL
650+gint set_master_password_interactively(guint max_attempts);
651+gint check_master_password_interactively(guint max_attempts);
652+#endif /* USE_SSL */
653+
654+#endif /* __MASTERPASSWORD_H__ */
655diff --git a/libsylph/news.c b/libsylph/news.c
656index ffff9f9..36d3b10 100644
657--- a/libsylph/news.c
658+++ b/libsylph/news.c
659@@ -44,6 +44,7 @@
660 #include "utils.h"
661 #include "prefs_common.h"
662 #include "prefs_account.h"
663+#include "masterpassword.h"
664 #if USE_SSL
665 # include "ssl.h"
666 #endif
667@@ -249,10 +250,11 @@ static Session *news_session_new_for_folder(Folder *folder)
668 ac = folder->account;
669 if (ac->use_nntp_auth && ac->userid && ac->userid[0]) {
670 userid = ac->userid;
671- if (ac->passwd && ac->passwd[0])
672- passwd = g_strdup(ac->passwd);
673- else
674+ if (ac->passwd && ac->passwd[0]) {
675+ passwd = decrypt_with_master_password(ac->passwd);
676+ } else {
677 passwd = input_query_password(ac->nntp_server, userid);
678+ }
679 }
680
681 if (ac->use_socks && ac->use_socks_for_recv && ac->proxy_host) {
682diff --git a/libsylph/pop.c b/libsylph/pop.c
683index 8cb7f5c..85f3ed9 100644
684--- a/libsylph/pop.c
685+++ b/libsylph/pop.c
686@@ -39,6 +39,7 @@
687 #include "prefs_account.h"
688 #include "utils.h"
689 #include "recv.h"
690+#include "masterpassword.h"
691
692 gint pop3_greeting_recv (Pop3Session *session,
693 const gchar *msg);
694@@ -437,8 +438,9 @@ Session *pop3_session_new(PrefsAccount *account)
695 session->error_msg = NULL;
696
697 session->user = g_strdup(account->userid);
698- session->pass = account->passwd ? g_strdup(account->passwd) :
699- account->tmp_pass ? g_strdup(account->tmp_pass) : NULL;
700+ session->pass = account->passwd ? decrypt_with_master_password(
701+ account->passwd) : account->tmp_pass ? g_strdup(
702+ account->tmp_pass) : NULL;
703
704 SESSION(session)->server = g_strdup(account->recv_server);
705
706diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c
707index 1aecba9..54cbc89 100644
708--- a/libsylph/prefs_account.c
709+++ b/libsylph/prefs_account.c
710@@ -34,6 +34,7 @@
711 #include "customheader.h"
712 #include "account.h"
713 #include "utils.h"
714+#include "masterpassword.h"
715
716 static PrefsAccount tmp_ac_prefs;
717
718@@ -205,7 +206,7 @@ PrefsAccount *prefs_account_new(void)
719 void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label)
720 {
721 const gchar *p = label;
722- gchar *rcpath;
723+ gchar *rcpath, *tmp_str;
724 gint id;
725
726 g_return_if_fail(ac_prefs != NULL);
727@@ -229,6 +230,34 @@ void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label)
728 ac_prefs->use_apop_auth = TRUE;
729 }
730
731+ if (master_password_active()) {
732+ if ((ac_prefs->passwd != NULL) &&
733+ !mpes_string_prefix(ac_prefs->passwd)) {
734+ /* TODO: Perhaps some prompt? */
735+ debug_print(
736+ "%s -> converting passwd cleartext to encrypted\n",
737+ label);
738+ tmp_str = ac_prefs->passwd;
739+ ac_prefs->passwd = encrypt_with_master_password(ac_prefs->passwd);
740+ cleanse_buffer(tmp_str, strlen(tmp_str));
741+ g_free(tmp_str);
742+ }
743+
744+ if ((ac_prefs->smtp_passwd != NULL) &&
745+ !mpes_string_prefix(ac_prefs->smtp_passwd)) {
746+ /* TODO: Perhaps some prompt? */
747+ debug_print(
748+ "%s -> converting smtp_passwd from cleartext to encrypted\n",
749+ label);
750+ tmp_str = ac_prefs->smtp_passwd;
751+ ac_prefs->smtp_passwd = encrypt_with_master_password(
752+ ac_prefs->smtp_passwd);
753+ cleanse_buffer(tmp_str, strlen(tmp_str));
754+ g_free(tmp_str);
755+ }
756+
757+ }
758+
759 custom_header_read_config(ac_prefs);
760 }
761
762diff --git a/libsylph/prefs_common.c b/libsylph/prefs_common.c
763index 8ed74a3..192964e 100644
764--- a/libsylph/prefs_common.c
765+++ b/libsylph/prefs_common.c
766@@ -406,6 +406,8 @@ static PrefParam param[] = {
767 P_BOOL},
768 {"gpg_signature_popup", "FALSE", &prefs_common.gpg_signature_popup,
769 P_BOOL},
770+ {"gpg_signature_popup_mode", "1", &prefs_common.gpg_signature_popup_mode,
771+ P_INT},
772 {"store_passphrase", "FALSE", &prefs_common.store_passphrase, P_BOOL},
773 {"store_passphrase_timeout", "0",
774 &prefs_common.store_passphrase_timeout, P_INT},
775@@ -416,6 +418,17 @@ static PrefParam param[] = {
776 {"show_gpg_warning", "TRUE", &prefs_common.gpg_warning, P_BOOL},
777 #endif
778
779+ /* Master password */
780+ {"use_master_password", "FALSE", &prefs_common.use_master_password,
781+ P_BOOL},
782+ {"master_password_hash", NULL, &prefs_common.master_password_hash,
783+ P_STRING},
784+ {"encrypted_password_min_length", "32",
785+ &prefs_common.encrypted_password_min_length, P_INT},
786+ {"auto_unload_master_password", "FALSE",
787+ &prefs_common.auto_unload_master_password,
788+ P_BOOL},
789+
790 /* Interface */
791 {"separate_folder", "FALSE", &prefs_common.sep_folder, P_BOOL},
792 {"separate_message", "FALSE", &prefs_common.sep_msg, P_BOOL},
793diff --git a/libsylph/prefs_common.h b/libsylph/prefs_common.h
794index ba9ddb5..6f9c364 100644
795--- a/libsylph/prefs_common.h
796+++ b/libsylph/prefs_common.h
797@@ -243,11 +243,18 @@ struct _PrefsCommon
798 /* Privacy */
799 gboolean auto_check_signatures;
800 gboolean gpg_signature_popup;
801+ gint gpg_signature_popup_mode;
802 gboolean store_passphrase;
803 gint store_passphrase_timeout;
804 gboolean passphrase_grab;
805 gboolean gpg_warning;
806
807+ /* Master password */
808+ gboolean use_master_password;
809+ gchar *master_password_hash;
810+ guint encrypted_password_min_length;
811+ gboolean auto_unload_master_password;
812+
813 /* Interface */
814 gboolean sep_folder;
815 gboolean sep_msg;
816diff --git a/libsylph/ssl.c b/libsylph/ssl.c
817index 8413925..e782b0e 100644
818--- a/libsylph/ssl.c
819+++ b/libsylph/ssl.c
820@@ -32,6 +32,13 @@
821 #include "ssl.h"
822 #include "ssl_hostname_validation.h"
823
824+#define SALT_SIZE 16
825+#define CIPHER EVP_aes_256_cfb()
826+#define KEY_HASH EVP_sha256()
827+#define DIGEST_HASH EVP_sha256()
828+#define PBKDF2_DIGEST_SIZE 64
829+#define PBKDF2_ITERATIONS 100000
830+
831 static SSL_CTX *ssl_ctx_SSLv23 = NULL;
832 static SSL_CTX *ssl_ctx_TLSv1 = NULL;
833
834@@ -402,4 +409,468 @@ void ssl_set_verify_func(SSLVerifyFunc func)
835 verify_ui_func = func;
836 }
837
838+/* master password related functions */
839+static gint secure_derive_key(guchar *key,
840+ gint length_key,
841+ const gchar *passphrase,
842+ const guchar *salt) {
843+
844+ guint length_buffer, length_hash;
845+ guchar *buffer, *ptr_hash;
846+
847+ EVP_MD_CTX *mdctx;
848+
849+ OPENSSL_cleanse(key, length_key);
850+
851+ length_buffer = SALT_SIZE + strlen(passphrase);
852+ buffer = OPENSSL_malloc(length_buffer);
853+ OPENSSL_cleanse(buffer, length_buffer);
854+
855+ memcpy(buffer, salt, SALT_SIZE);
856+ memcpy(buffer + SALT_SIZE, passphrase, strlen(passphrase));
857+
858+ mdctx = EVP_MD_CTX_create();
859+ EVP_DigestInit_ex(mdctx, KEY_HASH, NULL);
860+ EVP_DigestUpdate(mdctx, buffer, length_buffer);
861+ OPENSSL_cleanse(buffer, length_buffer);
862+
863+ ptr_hash = OPENSSL_malloc(EVP_MD_size(KEY_HASH));
864+ EVP_DigestFinal_ex(mdctx, ptr_hash, &length_hash);
865+
866+ memcpy(key,
867+ ptr_hash,
868+ (length_hash > length_key) ? length_key : length_hash);
869+
870+ OPENSSL_cleanse(ptr_hash, length_hash);
871+ OPENSSL_free(ptr_hash);
872+ OPENSSL_free(buffer);
873+ EVP_MD_CTX_destroy(mdctx);
874+
875+ return SSL_RC_OK;
876+
877+}
878+
879+gint encrypt_data(gchar **encrypted,
880+ gint *length_encrypted,
881+ const gchar *data,
882+ const gchar *passphrase,
883+ gint length_data,
884+ guint min_data_length,
885+ gboolean rnd_salt) {
886+
887+ gint crypt_buffer_cnt, rc;
888+ guint key_size, length_hash;
889+ guint length_cleartext, length_ciphertext, length_total;
890+ guchar salt[SALT_SIZE];
891+ guchar *ciphertext_buffer, *total_buffer;
892+ /* sensitive buffers and counters */
893+ guint length_data_payload, length_padding;
894+ gchar str_data_size[3];
895+ guchar *data_payload_buffer, *hash_buffer, *padding_buffer, *key;
896+ guchar *cleartext_buffer;
897+
898+ EVP_CIPHER_CTX *ctx;
899+ EVP_MD_CTX *mdctx;
900+
901+ rc = SSL_RC_ERROR;
902+
903+ if (length_data < 1) {
904+ return -1;
905+ }
906+ if (rnd_salt) {
907+ if (RAND_bytes(salt, SALT_SIZE) != 1) {
908+ debug_print("Random problems...\n");
909+ goto cleanup;
910+ }
911+ } else {
912+ strncpy((gchar *)salt, "FOR TESTING ONLY", SALT_SIZE);
913+ }
914+
915+ key_size = EVP_CIPHER_key_length(CIPHER);
916+ key = OPENSSL_malloc(key_size);
917+ OPENSSL_cleanse(key, key_size);
918+ if (secure_derive_key(key,
919+ key_size,
920+ passphrase,
921+ salt) != SSL_RC_OK) {
922+ OPENSSL_cleanse(key, key_size);
923+ debug_print("Could not generate secure key\n");
924+ goto cleanup;
925+ }
926+
927+ /* prepare the data-buffer */
928+ length_padding = 0;
929+ if (length_data < min_data_length) {
930+ g_snprintf(str_data_size, 3, "%02d", length_data);
931+ length_padding = min_data_length - length_data;
932+ padding_buffer = OPENSSL_malloc(length_padding);
933+ OPENSSL_cleanse(padding_buffer, length_padding);
934+ if (RAND_bytes(padding_buffer, length_padding) != 1) {
935+ debug_print("Random problems...\n");
936+ goto cleanup;
937+ }
938+ } else {
939+ g_snprintf(str_data_size, 3, "-1");
940+ }
941+
942+ length_data_payload = 2 + length_data + length_padding;
943+ data_payload_buffer = OPENSSL_malloc(length_data_payload);
944+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
945+
946+ memcpy(data_payload_buffer, str_data_size, 2);
947+ memcpy(data_payload_buffer + 2, data, length_data);
948+ if (length_padding > 0) {
949+ memcpy(data_payload_buffer + (2 + length_data),
950+ padding_buffer,
951+ length_padding);
952+ }
953+
954+ mdctx = EVP_MD_CTX_create();
955+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
956+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
957+
958+ length_hash = EVP_MD_size(DIGEST_HASH);
959+ hash_buffer = OPENSSL_malloc(length_hash);
960+ OPENSSL_cleanse(hash_buffer, length_hash);
961+ EVP_DigestFinal_ex(mdctx, hash_buffer, NULL);
962+
963+ length_cleartext = length_hash + length_data_payload;
964+
965+ cleartext_buffer = OPENSSL_malloc(length_cleartext);
966+ OPENSSL_cleanse(cleartext_buffer, length_cleartext);
967+
968+ /* assemble cleartext-buffer */
969+ memcpy(cleartext_buffer, hash_buffer, length_hash);
970+ memcpy(cleartext_buffer + length_hash,
971+ data_payload_buffer,
972+ length_data_payload);
973+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); /* sensitive */
974+
975+ /* encryption */
976+ if (!(ctx = EVP_CIPHER_CTX_new())) {
977+ debug_print("New ctx failed\n");
978+ goto cleanup;
979+ }
980+
981+ length_ciphertext = 0;
982+ if (EVP_EncryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
983+ debug_print("EVP_EncryptInit_ex failed\n");
984+ goto cleanup;
985+ }
986+
987+ ciphertext_buffer = OPENSSL_malloc(length_cleartext);
988+
989+ crypt_buffer_cnt = 0;
990+ while(1) {
991+ if (EVP_EncryptUpdate(ctx,
992+ ciphertext_buffer + length_ciphertext,
993+ &crypt_buffer_cnt,
994+ cleartext_buffer + length_ciphertext,
995+ 1) != 1) { /* one byte at a time */
996+ debug_print("EVP_EncryptUpdate failed\n");
997+ goto cleanup;
998+ }
999+
1000+ if (crypt_buffer_cnt != 1) { /* paranoia */
1001+ debug_print("The sizes of enc and dec text do not correspond\n");
1002+ goto cleanup;
1003+ }
1004+
1005+ ++length_ciphertext;
1006+
1007+ if (length_ciphertext >= length_cleartext) {
1008+ break;
1009+ }
1010+ }
1011+ /* No padding required for the CFB mode */
1012+
1013+ OPENSSL_cleanse(cleartext_buffer, length_cleartext); /* sensitive */
1014+ length_total = SALT_SIZE + length_ciphertext;
1015+ total_buffer = OPENSSL_malloc(length_total);
1016+
1017+ memcpy(total_buffer, salt, SALT_SIZE);
1018+ memcpy(total_buffer + SALT_SIZE, ciphertext_buffer, length_ciphertext);
1019+
1020+ *encrypted = g_base64_encode(total_buffer, length_total);
1021+ *length_encrypted = strlen(*encrypted);
1022+
1023+ rc = SSL_RC_OK;
1024+
1025+cleanup:
1026+ /* key */
1027+ OPENSSL_cleanse(key, key_size);
1028+ OPENSSL_free(key);
1029+ /* cleartext buffer */
1030+ OPENSSL_cleanse(cleartext_buffer, length_cleartext);
1031+ OPENSSL_free(cleartext_buffer);
1032+ /* payload buffer */
1033+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1034+ OPENSSL_free(data_payload_buffer);
1035+ /* hash */
1036+ OPENSSL_cleanse(hash_buffer, length_hash);
1037+ OPENSSL_free(hash_buffer);
1038+ /* padding */
1039+ if (length_padding > 0) {
1040+ OPENSSL_cleanse(padding_buffer, length_padding);
1041+ OPENSSL_free(padding_buffer);
1042+ }
1043+
1044+ OPENSSL_cleanse(str_data_size, 3); /* paranoia */
1045+
1046+ /* ciphertext buffer */
1047+ OPENSSL_free(ciphertext_buffer);
1048+
1049+ /* total buffer */
1050+ OPENSSL_free(total_buffer);
1051+
1052+ length_data_payload = 0;
1053+ length_padding = 0;
1054+
1055+ EVP_CIPHER_CTX_free(ctx);
1056+ EVP_MD_CTX_destroy(mdctx);
1057+
1058+ return rc;
1059+
1060+}
1061+
1062+gint decrypt_data(gchar **decrypted,
1063+ const gchar *data,
1064+ const gchar *passphrase,
1065+ gint length_data) {
1066+
1067+ gint rc; /* return code */
1068+ gint decrypt_buffer_cnt, length_ciphertext, length_decrypted;
1069+ guint key_size, length_hash, length_cleartext;
1070+ gsize length_total;
1071+ guchar salt[SALT_SIZE];
1072+ guchar *ciphertext_buffer, *total_buffer;
1073+ /* sensitive buffers and counters */
1074+ gint data_size;
1075+ guint length_data_payload;
1076+ gchar str_data_size[3];
1077+ guchar *data_payload_buffer, *hash_buffer, *key;
1078+ guchar *cleartext_buffer;
1079+
1080+ EVP_CIPHER_CTX *ctx;
1081+ EVP_MD_CTX *mdctx;
1082+
1083+ rc = -1;
1084+
1085+ if (length_data < 1) {
1086+ return -1;
1087+ }
1088+
1089+ total_buffer = g_base64_decode(data, &length_total);
1090+ length_ciphertext = length_total - SALT_SIZE;
1091+ ciphertext_buffer = OPENSSL_malloc(length_ciphertext);
1092+ OPENSSL_cleanse(ciphertext_buffer, length_ciphertext);
1093+
1094+ memcpy(salt, total_buffer, SALT_SIZE);
1095+ memcpy(ciphertext_buffer, total_buffer + SALT_SIZE, length_ciphertext);
1096+
1097+ key_size = EVP_CIPHER_key_length(CIPHER);
1098+
1099+ /* decryption */
1100+ if(!(ctx = EVP_CIPHER_CTX_new())) {
1101+ debug_print("New ctx failed\n");
1102+ goto cleanup;
1103+ }
1104+
1105+ key = OPENSSL_malloc(key_size);
1106+ OPENSSL_cleanse(key, key_size);
1107+ if (secure_derive_key(key,
1108+ key_size,
1109+ passphrase,
1110+ salt) != 0) {
1111+ OPENSSL_cleanse(key, key_size);
1112+ debug_print("Could not generate secure key\n");
1113+ goto cleanup;
1114+ }
1115+
1116+ if (EVP_DecryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
1117+ debug_print("EVP_DecryptInit_ex failed\n");
1118+ goto cleanup;
1119+ }
1120+ OPENSSL_cleanse(key, key_size); /* highly sensitive */
1121+ cleartext_buffer = OPENSSL_malloc(length_ciphertext);
1122+ OPENSSL_cleanse(cleartext_buffer, length_ciphertext);
1123+
1124+ length_cleartext = 0;
1125+ decrypt_buffer_cnt = 0;
1126+
1127+ while(1) {
1128+ if (EVP_DecryptUpdate(ctx,
1129+ cleartext_buffer + length_cleartext,
1130+ &decrypt_buffer_cnt,
1131+ ciphertext_buffer + length_cleartext,
1132+ 1) != 1) { /* one byte at a time */
1133+ debug_print("EVP_EncryptUpdate failed\n");
1134+ goto cleanup;
1135+ }
1136+
1137+ if (decrypt_buffer_cnt != 1) { /* paranoia */
1138+ debug_print("The sizes of enc and dec text do not correspond");
1139+ goto cleanup;
1140+ }
1141+
1142+ ++length_cleartext;
1143+
1144+ if (length_cleartext >= length_ciphertext) {
1145+ break;
1146+ }
1147+ }
1148+
1149+
1150+ length_hash = EVP_MD_size(DIGEST_HASH);
1151+ hash_buffer = OPENSSL_malloc(length_hash);
1152+ length_data_payload = length_cleartext - length_hash;
1153+ data_payload_buffer = OPENSSL_malloc(length_data_payload);
1154+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1155+
1156+ memcpy(data_payload_buffer,
1157+ cleartext_buffer + length_hash,
1158+ length_data_payload);
1159+
1160+ mdctx = EVP_MD_CTX_create();
1161+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
1162+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
1163+ EVP_DigestFinal_ex(mdctx, hash_buffer, &length_hash);
1164+
1165+ if (strncmp((const gchar*) hash_buffer,
1166+ (const gchar*) cleartext_buffer,
1167+ length_hash) != 0) {
1168+ debug_print("Invalid hash\n");
1169+ rc = SSL_RC_WRONG_HASH_OR_KEY;
1170+ goto cleanup;
1171+ }
1172+
1173+ memcpy(str_data_size, cleartext_buffer + length_hash, 2);
1174+ data_size = atoi(str_data_size);
1175+
1176+ if (data_size < 0) {
1177+ length_decrypted = length_data_payload - 2;
1178+ } else {
1179+ length_decrypted = data_size;
1180+ }
1181+ *decrypted = OPENSSL_malloc(length_decrypted);
1182+ memcpy(*decrypted, data_payload_buffer + 2, length_decrypted);
1183+
1184+ rc = SSL_RC_OK;
1185+
1186+cleanup:
1187+
1188+ /* key */
1189+ OPENSSL_cleanse(key, key_size);
1190+ OPENSSL_free(key);
1191+ /* payload buffer */
1192+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1193+ OPENSSL_free(data_payload_buffer);
1194+ /* hash */
1195+ OPENSSL_cleanse(hash_buffer, length_hash);
1196+ OPENSSL_free(hash_buffer);
1197+ /* ciphertext */
1198+ OPENSSL_free(ciphertext_buffer);
1199+ OPENSSL_free(total_buffer);
1200+
1201+ EVP_CIPHER_CTX_free(ctx);
1202+ EVP_MD_CTX_destroy(mdctx);
1203+
1204+ return rc;
1205+
1206+}
1207+
1208+gint generate_password_hash(gchar **password_hash,
1209+ const gchar *password,
1210+ const guchar *salt) {
1211+ /*
1212+ * Hashes 'password' using PKCS5_PBKDF2_HMAC with SHA512 and 'salt',
1213+ * and assigns a string to 'password_hash' with the following format:
1214+ * pbkdf2_sha512$iterations$base64(salt)$base64(password_hash)
1215+ */
1216+ guchar lsalt[SALT_SIZE];
1217+ gchar *PBKDF2_digest, *salt_b64, *digest_b64;
1218+
1219+ if (salt == NULL) {
1220+ if (RAND_bytes(lsalt, SALT_SIZE) != 1) {
1221+ debug_print("Random problems...\n");
1222+ return SSL_RC_ERROR;
1223+ }
1224+ } else {
1225+ memcpy(lsalt, salt, SALT_SIZE);
1226+ }
1227+
1228+ PBKDF2_digest = OPENSSL_malloc(PBKDF2_DIGEST_SIZE);
1229+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1230+
1231+ PKCS5_PBKDF2_HMAC(password,
1232+ strlen(password),
1233+ lsalt,
1234+ SALT_SIZE,
1235+ PBKDF2_ITERATIONS,
1236+ EVP_sha512(),
1237+ PBKDF2_DIGEST_SIZE,
1238+ (guchar *) PBKDF2_digest);
1239+ digest_b64 = g_base64_encode((guchar *) PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1240+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1241+ OPENSSL_free(PBKDF2_digest);
1242+
1243+ salt_b64 = g_base64_encode(lsalt, SALT_SIZE);
1244+
1245+ *password_hash = g_strdup_printf("pbkdf2_sha512$%d$%s$%s",
1246+ PBKDF2_ITERATIONS,
1247+ salt_b64,
1248+ digest_b64);
1249+
1250+ OPENSSL_cleanse(digest_b64, strlen(digest_b64));
1251+ OPENSSL_free(digest_b64);
1252+
1253+ OPENSSL_cleanse(salt_b64, strlen(salt_b64));
1254+ OPENSSL_free(salt_b64);
1255+
1256+ return SSL_RC_OK;
1257+
1258+}
1259+
1260+gint check_password(const gchar *password, const gchar *password_hash) {
1261+
1262+ gint token_counter, rc;
1263+ guchar *salt;
1264+ gchar **tokens, *new_hash;
1265+ gsize salt_length;
1266+
1267+ rc = SSL_RC_ERROR;
1268+ tokens = g_strsplit(password_hash,
1269+ "$",
1270+ -1);
1271+ token_counter = 0;
1272+ while (*(tokens + token_counter) != NULL) {
1273+ ++token_counter;
1274+ }
1275+
1276+ if (token_counter != 4) {
1277+ debug_print("Invalid password hash...\n");
1278+ goto cleanup;
1279+ }
1280+
1281+ salt = g_base64_decode(*(tokens + 2), &salt_length);
1282+ if (salt_length != SALT_SIZE) {
1283+ debug_print("Salt size does not match\n");
1284+ goto cleanup;
1285+ }
1286+
1287+ if (generate_password_hash(&new_hash, password, salt) != SSL_RC_OK) {
1288+ debug_print("Password hash generation failed\n");
1289+ goto cleanup;
1290+ }
1291+
1292+ rc = g_strcmp0(password_hash, new_hash);
1293+
1294+cleanup:
1295+ g_free(salt);
1296+ g_free(new_hash);
1297+ g_strfreev(tokens);
1298+ return rc;
1299+
1300+}
1301+
1302 #endif /* USE_SSL */
1303diff --git a/libsylph/ssl.h b/libsylph/ssl.h
1304index a9f690d..4c0ccd1 100644
1305--- a/libsylph/ssl.h
1306+++ b/libsylph/ssl.h
1307@@ -32,9 +32,15 @@
1308 #include <openssl/pem.h>
1309 #include <openssl/ssl.h>
1310 #include <openssl/err.h>
1311+#include <openssl/evp.h>
1312+#include <openssl/rand.h>
1313
1314 #include "socket.h"
1315
1316+#define SSL_RC_OK 0
1317+#define SSL_RC_ERROR -1
1318+#define SSL_RC_WRONG_HASH_OR_KEY 1
1319+
1320 typedef enum {
1321 SSL_METHOD_SSLv23,
1322 SSL_METHOD_TLSv1
1323@@ -60,6 +66,26 @@ void ssl_done_socket (SockInfo *sockinfo);
1324
1325 void ssl_set_verify_func (SSLVerifyFunc func);
1326
1327+/* master password related code */
1328+gint encrypt_data(gchar **encrypted,
1329+ gint *length_encrypted,
1330+ const gchar *data,
1331+ const gchar *passphrase,
1332+ gint length_data,
1333+ guint min_data_length,
1334+ gboolean rnd_salt);
1335+
1336+gint decrypt_data(gchar **decrypted,
1337+ const gchar *data,
1338+ const gchar *passphrase,
1339+ gint length_data);
1340+
1341+gint generate_password_hash(gchar **password_hash,
1342+ const gchar *password,
1343+ const guchar *salt);
1344+
1345+gint check_password(const gchar *password, const gchar *password_hash);
1346+/* ---------------------------- */
1347 #endif /* USE_SSL */
1348
1349 #endif /* __SSL_H__ */
1350diff --git a/libsylph/utils.c b/libsylph/utils.c
1351index aabce06..3bbcbcf 100644
1352--- a/libsylph/utils.c
1353+++ b/libsylph/utils.c
1354@@ -3306,10 +3306,10 @@ gint canonicalize_file(const gchar *src, const gchar *dest)
1355 }
1356 }
1357
1358- if (last_linebreak == TRUE) {
1359- if (fputs("\r\n", dest_fp) == EOF)
1360- err = TRUE;
1361- }
1362+ /* if (last_linebreak == TRUE) { */
1363+ /* if (fputs("\r\n", dest_fp) == EOF) */
1364+ /* err = TRUE; */
1365+ /* } */
1366
1367 if (ferror(src_fp)) {
1368 FILE_OP_ERROR(src, "fgets");
1369@@ -4598,6 +4598,36 @@ gchar *input_query_password(const gchar *server, const gchar *user)
1370 return NULL;
1371 }
1372
1373+static QueryMasterPasswordFunc query_master_password_func = NULL;
1374+
1375+void set_input_query_master_password_func(QueryMasterPasswordFunc func)
1376+{
1377+ query_master_password_func = func;
1378+}
1379+
1380+gchar *input_query_master_password(void)
1381+{
1382+ if (query_master_password_func)
1383+ return query_master_password_func();
1384+ else
1385+ return NULL;
1386+}
1387+
1388+static SetNewPasswordFunc set_new_password_func = NULL;
1389+
1390+void set_input_set_new_password_func(SetNewPasswordFunc func)
1391+{
1392+ set_new_password_func = func;
1393+}
1394+
1395+gchar *input_set_new_password(guint max_attempts)
1396+{
1397+ if (set_new_password_func)
1398+ return set_new_password_func(max_attempts);
1399+ else
1400+ return NULL;
1401+}
1402+
1403 /* logging */
1404
1405 static FILE *log_fp = NULL;
1406diff --git a/libsylph/utils.h b/libsylph/utils.h
1407index 9ac65cf..ede55ff 100644
1408--- a/libsylph/utils.h
1409+++ b/libsylph/utils.h
1410@@ -202,6 +202,8 @@ typedef void (*ProgressFunc) (gint cur,
1411 gint total);
1412 typedef gchar * (*QueryPasswordFunc) (const gchar *server,
1413 const gchar *user);
1414+typedef gchar * (*QueryMasterPasswordFunc) (void);
1415+typedef gchar * (*SetNewPasswordFunc) (guint max_attempts);
1416 typedef void (*LogFunc) (const gchar *str);
1417 typedef void (*LogFlushFunc) (void);
1418
1419@@ -560,9 +562,12 @@ void progress_show (gint cur,
1420
1421 /* user input */
1422 void set_input_query_password_func (QueryPasswordFunc func);
1423-
1424 gchar *input_query_password (const gchar *server,
1425 const gchar *user);
1426+void set_input_query_master_password_func(QueryMasterPasswordFunc func);
1427+gchar *input_query_master_password(void);
1428+void set_input_set_new_password_func(SetNewPasswordFunc func);
1429+gchar *input_set_new_password(guint max_attempts);
1430
1431 /* logging */
1432 void set_log_file (const gchar *filename);
1433diff --git a/src/inputdialog.c b/src/inputdialog.c
1434index a601085..cdfb997 100644
1435--- a/src/inputdialog.c
1436+++ b/src/inputdialog.c
1437@@ -44,6 +44,7 @@
1438 #include "filesel.h"
1439 #include "prefs_common.h"
1440 #include "gtkutils.h"
1441+#include "masterpassword.h"
1442 #include "utils.h"
1443
1444 #define DIALOG_WIDTH 420
1445@@ -156,6 +157,57 @@ gchar *input_dialog_query_password(const gchar *server, const gchar *user)
1446 return pass;
1447 }
1448
1449+gchar *input_dialog_query_master_password(void)
1450+{
1451+ gchar *mp_input;
1452+
1453+ mp_input = input_dialog_with_invisible(_("Input password"),
1454+ _("Master password"),
1455+ NULL);
1456+
1457+ if (prefs_common.use_master_password &&
1458+ prefs_common.auto_unload_master_password)
1459+ {
1460+ g_timeout_add(1000 * 30, unload_master_password, NULL);
1461+ debug_print("Master password to be unloaded in 30 seconds\n");
1462+ }
1463+
1464+ return mp_input;
1465+}
1466+
1467+gchar *input_dialog_set_new_password(guint max_attempts)
1468+{
1469+ guint cnt;
1470+ gchar *pass1, *pass2;
1471+
1472+ if (max_attempts < 1)
1473+ return NULL;
1474+
1475+ for (cnt = 0; cnt < max_attempts; ++cnt) {
1476+ pass1 = input_dialog_with_invisible(
1477+ _("Input password"),
1478+ _("New password"),
1479+ NULL);
1480+ pass2 = input_dialog_with_invisible(
1481+ _("Input password"),
1482+ _("Confirm new password"),
1483+ NULL);
1484+
1485+ if (pass1 != NULL && pass2 != NULL && strcmp(pass1, pass2) == 0) {
1486+ cleanse_buffer(pass2, strlen(pass2));
1487+ g_free(pass2);
1488+ break;
1489+ }
1490+ cleanse_buffer(pass1, strlen(pass1));
1491+ cleanse_buffer(pass2, strlen(pass2));
1492+ g_free(pass2);
1493+ g_free(pass1);
1494+ pass1 = NULL;
1495+ }
1496+
1497+ return pass1;
1498+}
1499+
1500 gchar *input_dialog_with_filesel(const gchar *title, const gchar *message,
1501 const gchar *default_string,
1502 GtkFileChooserAction action)
1503diff --git a/src/inputdialog.h b/src/inputdialog.h
1504index 02bdda3..5364d1a 100644
1505--- a/src/inputdialog.h
1506+++ b/src/inputdialog.h
1507@@ -36,7 +36,8 @@ gchar *input_dialog_combo (const gchar *title,
1508 gboolean case_sensitive);
1509 gchar *input_dialog_query_password (const gchar *server,
1510 const gchar *user);
1511-
1512+gchar *input_dialog_query_master_password(void);
1513+gchar *input_dialog_set_new_password(guint max_attempts);
1514 gchar *input_dialog_with_filesel (const gchar *title,
1515 const gchar *message,
1516 const gchar *default_string,
1517diff --git a/src/main.c b/src/main.c
1518index 77d8192..97a4bd0 100644
1519--- a/src/main.c
1520+++ b/src/main.c
1521@@ -87,6 +87,7 @@
1522 #include "foldersel.h"
1523 #include "update_check.h"
1524 #include "colorlabel.h"
1525+#include "masterpassword.h"
1526
1527 #if USE_GPGME
1528 # include "rfc2015.h"
1529@@ -265,14 +266,54 @@ int main(int argc, char *argv[])
1530 set_ui_update_func(gtkut_events_flush);
1531 set_progress_func(main_window_progress_show);
1532 set_input_query_password_func(input_dialog_query_password);
1533+ set_input_set_new_password_func(input_dialog_set_new_password);
1534 #if USE_SSL
1535 ssl_init();
1536 ssl_set_verify_func(ssl_manager_verify_cert);
1537+ set_input_query_master_password_func(input_dialog_query_master_password);
1538 #endif
1539
1540 CHDIR_EXIT_IF_FAIL(get_home_dir(), 1);
1541
1542 prefs_common_read_config();
1543+ set_master_password(NULL);
1544+#if USE_SSL
1545+ if (prefs_common.use_master_password) {
1546+ if (prefs_common.master_password_hash != NULL) {
1547+ if (check_master_password_interactively(3) != MP_RC_OK) {
1548+ if (alertpanel(_("Master password"),
1549+ _("Invalid master password"),
1550+ GTK_STOCK_DISCARD,
1551+ GTK_STOCK_QUIT,
1552+ NULL) != G_ALERTDEFAULT) {
1553+ exit(1);
1554+ }
1555+ }
1556+ } else {
1557+ alertpanel_notice(
1558+ _("Master password enabled but not set. Setting one now"));
1559+ if (set_master_password_interactively(3) != MP_RC_OK) {
1560+ if (alertpanel(_("Master password"),
1561+ _("Unable to set master password"),
1562+ GTK_STOCK_DISCARD,
1563+ GTK_STOCK_QUIT,
1564+ NULL) != G_ALERTDEFAULT) {
1565+ exit(1);
1566+ }
1567+ }
1568+ }
1569+ }
1570+ /* security goal:
1571+ * if the master password is enabled and loaded on init,
1572+ * an attacker can potentially set use_master_password - disabled
1573+ * afterwards and then force Sylpheed to save account data - the result
1574+ * being passwords saved to accountrc in plain-text.
1575+ * possible solution:
1576+ * Do not allow the passwords to be stored in plain-text if Sylpheed
1577+ * was started with use_master_password - enabled.
1578+ */
1579+ master_password_enabled_on_init = prefs_common.use_master_password;
1580+#endif
1581 filter_set_addressbook_func(addressbook_has_address);
1582 filter_read_config();
1583 prefs_actions_read_config();
1584@@ -381,6 +422,13 @@ int main(int argc, char *argv[])
1585
1586 remote_command_exec();
1587
1588+#if USE_SSL
1589+ if (prefs_common.auto_unload_master_password && master_password_active()) {
1590+ debug_print("Auto unloading master password\n");
1591+ unload_master_password();
1592+ }
1593+#endif
1594+
1595 #if USE_UPDATE_CHECK
1596 if (prefs_common.auto_update_check)
1597 update_check(FALSE);
1598@@ -993,6 +1041,7 @@ void app_will_exit(gboolean force)
1599
1600 /* remove temporary files, close log file, socket cleanup */
1601 #if USE_SSL
1602+ unload_master_password();
1603 ssl_done();
1604 #endif
1605 syl_cleanup();
1606diff --git a/src/prefs_account_dialog.c b/src/prefs_account_dialog.c
1607index e9cba13..e215b8e 100644
1608--- a/src/prefs_account_dialog.c
1609+++ b/src/prefs_account_dialog.c
1610@@ -267,7 +267,7 @@ static PrefsUIData ui_data[] = {
1611 {"user_id", &basic.uid_entry,
1612 prefs_set_data_from_entry, prefs_set_entry},
1613 {"password", &basic.pass_entry,
1614- prefs_set_data_from_entry, prefs_set_entry},
1615+ prefs_set_data_from_epass_entry, prefs_set_entry},
1616
1617 /* Receive */
1618 {"use_apop_auth", &receive.use_apop_chkbtn,
1619@@ -313,7 +313,7 @@ static PrefsUIData ui_data[] = {
1620 {"smtp_user_id", &p_send.smtp_uid_entry,
1621 prefs_set_data_from_entry, prefs_set_entry},
1622 {"smtp_password", &p_send.smtp_pass_entry,
1623- prefs_set_data_from_entry, prefs_set_entry},
1624+ prefs_set_data_from_epass_entry, prefs_set_entry},
1625
1626 {"pop_before_smtp", &p_send.pop_bfr_smtp_chkbtn,
1627 prefs_set_data_from_toggle, prefs_set_toggle},
1628diff --git a/src/prefs_common_dialog.c b/src/prefs_common_dialog.c
1629index b46c9b7..119a056 100644
1630--- a/src/prefs_common_dialog.c
1631+++ b/src/prefs_common_dialog.c
1632@@ -205,6 +205,7 @@ static struct JunkMail {
1633 static struct Privacy {
1634 GtkWidget *checkbtn_auto_check_signatures;
1635 GtkWidget *checkbtn_gpg_signature_popup;
1636+ GtkWidget *radiobtn_gpg_signature_all;
1637 GtkWidget *checkbtn_store_passphrase;
1638 GtkWidget *spinbtn_store_passphrase;
1639 GtkObject *spinbtn_store_passphrase_adj;
1640@@ -215,6 +216,14 @@ static struct Privacy {
1641 } privacy;
1642 #endif
1643
1644+#if USE_SSL
1645+static struct MasterPassword {
1646+ GtkWidget *checkbtn_use_master_password;
1647+ GtkWidget *checkbtn_auto_unload_master_password;
1648+ GtkWidget *spinbtn_encrypted_password_min_length;
1649+} master_password;
1650+#endif
1651+
1652 static struct Interface {
1653 GtkWidget *checkbtn_always_show_msg;
1654 GtkWidget *checkbtn_always_mark_read;
1655@@ -314,6 +323,13 @@ static void prefs_common_attach_toolbtn_pos_set_radiobtn (PrefParam *pparam);
1656 static void prefs_common_online_mode_set_data_from_radiobtn(PrefParam *pparam);
1657 static void prefs_common_online_mode_set_radiobtn (PrefParam *pparam);
1658
1659+#if USE_GPGME
1660+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn(
1661+ PrefParam *pparam);
1662+static void prefs_common_gpg_signature_popup_mode_set_radiobtn(
1663+ PrefParam *pparam);
1664+#endif
1665+
1666 static PrefsUIData ui_data[] = {
1667 /* Receive */
1668 {"autochk_newmail", &receive.checkbtn_autochk,
1669@@ -540,6 +556,9 @@ static PrefsUIData ui_data[] = {
1670 prefs_set_data_from_toggle, prefs_set_toggle},
1671 {"gpg_signature_popup", &privacy.checkbtn_gpg_signature_popup,
1672 prefs_set_data_from_toggle, prefs_set_toggle},
1673+ {"gpg_signature_popup_mode", &privacy.radiobtn_gpg_signature_all,
1674+ prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn,
1675+ prefs_common_gpg_signature_popup_mode_set_radiobtn},
1676 {"store_passphrase", &privacy.checkbtn_store_passphrase,
1677 prefs_set_data_from_toggle, prefs_set_toggle},
1678 {"store_passphrase_timeout", &privacy.spinbtn_store_passphrase,
1679@@ -552,6 +571,18 @@ static PrefsUIData ui_data[] = {
1680 prefs_set_data_from_toggle, prefs_set_toggle},
1681 #endif /* USE_GPGME */
1682
1683+#if USE_SSL
1684+ {"use_master_password", &master_password.checkbtn_use_master_password,
1685+ prefs_set_data_from_toggle, prefs_set_toggle},
1686+ {"auto_unload_master_password",
1687+ &master_password.checkbtn_auto_unload_master_password,
1688+ prefs_set_data_from_toggle, prefs_set_toggle},
1689+ {"encrypted_password_min_length",
1690+ &master_password.spinbtn_encrypted_password_min_length,
1691+ prefs_set_data_from_spinbtn,
1692+ prefs_set_spinbtn},
1693+#endif
1694+
1695 /* Interface */
1696 {"always_show_message_when_selected",
1697 &iface.checkbtn_always_show_msg,
1698@@ -682,6 +713,9 @@ static void prefs_junk_create (void);
1699 #if USE_GPGME
1700 static void prefs_privacy_create (void);
1701 #endif
1702+#if USE_SSL
1703+static void prefs_master_password_create (void);
1704+#endif
1705 static void prefs_details_create (void);
1706 static GtkWidget *prefs_other_create (void);
1707 static GtkWidget *prefs_extcmd_create (void);
1708@@ -841,6 +875,10 @@ static void prefs_common_create(void)
1709 #if USE_GPGME
1710 prefs_privacy_create();
1711 SET_NOTEBOOK_LABEL(dialog.notebook, _("Privacy"), page++);
1712+#endif
1713+#if USE_SSL
1714+ prefs_master_password_create();
1715+ SET_NOTEBOOK_LABEL(dialog.notebook, _("Master password"), page++);
1716 #endif
1717 prefs_details_create();
1718 SET_NOTEBOOK_LABEL(dialog.notebook, _("Details"), page++);
1719@@ -2478,10 +2516,14 @@ static void prefs_privacy_create(void)
1720 GtkWidget *vbox2;
1721 GtkWidget *vbox3;
1722 GtkWidget *hbox1;
1723+ GtkWidget *vbox_sign_popup_mode;
1724+ GtkWidget *hbox_sign_popup_mode;
1725 GtkWidget *hbox_spc;
1726 GtkWidget *label;
1727 GtkWidget *checkbtn_auto_check_signatures;
1728 GtkWidget *checkbtn_gpg_signature_popup;
1729+ GtkWidget *radiobtn_gpg_signature_all;
1730+ GtkWidget *radiobtn_gpg_signature_bad;
1731 GtkWidget *checkbtn_store_passphrase;
1732 GtkObject *spinbtn_store_passphrase_adj;
1733 GtkWidget *spinbtn_store_passphrase;
1734@@ -2505,6 +2547,56 @@ static void prefs_privacy_create(void)
1735 PACK_CHECK_BUTTON (vbox2, checkbtn_gpg_signature_popup,
1736 _("Show signature check result in a popup window"));
1737
1738+ vbox_sign_popup_mode = gtk_vbox_new (FALSE, VSPACING_NARROW);
1739+ gtk_widget_show (vbox_sign_popup_mode);
1740+ gtk_box_pack_start (GTK_BOX (vbox2), vbox_sign_popup_mode, FALSE, FALSE, 0);
1741+
1742+ hbox_sign_popup_mode = gtk_hbox_new (FALSE, 8);
1743+ gtk_widget_show (hbox_sign_popup_mode);
1744+ gtk_box_pack_start (GTK_BOX (vbox_sign_popup_mode),
1745+ hbox_sign_popup_mode,
1746+ FALSE,
1747+ FALSE,
1748+ 0);
1749+
1750+ hbox_spc = gtk_hbox_new (FALSE, 0);
1751+ gtk_widget_show (hbox_spc);
1752+ gtk_box_pack_start (GTK_BOX (hbox_sign_popup_mode),
1753+ hbox_spc,
1754+ FALSE,
1755+ FALSE,
1756+ 0);
1757+ gtk_widget_set_size_request (hbox_spc, 12, -1);
1758+
1759+ radiobtn_gpg_signature_all = gtk_radio_button_new_with_label(
1760+ NULL,
1761+ _("All signatures"));
1762+ gtk_widget_show(radiobtn_gpg_signature_all);
1763+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode),
1764+ radiobtn_gpg_signature_all,
1765+ FALSE,
1766+ FALSE,
1767+ 0);
1768+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_all),
1769+ MENU_VAL_ID,
1770+ GINT_TO_POINTER (1));
1771+
1772+ radiobtn_gpg_signature_bad = gtk_radio_button_new_with_label_from_widget(
1773+ GTK_RADIO_BUTTON (radiobtn_gpg_signature_all),
1774+ _("Bad signatures only"));
1775+ gtk_widget_show(radiobtn_gpg_signature_bad);
1776+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode),
1777+ radiobtn_gpg_signature_bad,
1778+ FALSE,
1779+ FALSE,
1780+ 0);
1781+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_bad),
1782+ MENU_VAL_ID,
1783+ GINT_TO_POINTER (0));
1784+
1785+ SET_TOGGLE_SENSITIVITY (checkbtn_gpg_signature_popup,
1786+ hbox_sign_popup_mode);
1787+
1788 PACK_CHECK_BUTTON (vbox2, checkbtn_store_passphrase,
1789 _("Store passphrase in memory temporarily"));
1790
1791@@ -2572,7 +2664,8 @@ static void prefs_privacy_create(void)
1792 = checkbtn_auto_check_signatures;
1793 privacy.checkbtn_gpg_signature_popup
1794 = checkbtn_gpg_signature_popup;
1795- privacy.checkbtn_store_passphrase = checkbtn_store_passphrase;
1796+ privacy.radiobtn_gpg_signature_all = radiobtn_gpg_signature_all;
1797+ privacy.checkbtn_store_passphrase = checkbtn_store_passphrase;
1798 privacy.spinbtn_store_passphrase = spinbtn_store_passphrase;
1799 privacy.spinbtn_store_passphrase_adj = spinbtn_store_passphrase_adj;
1800 #ifndef G_OS_WIN32
1801@@ -2582,6 +2675,93 @@ static void prefs_privacy_create(void)
1802 }
1803 #endif /* USE_GPGME */
1804
1805+#if USE_SSL
1806+static void prefs_master_password_create(void)
1807+{
1808+ GtkWidget *vbox_main;
1809+ GtkWidget *vbox_master_password_options;
1810+ GtkWidget *vbox_master_password_suboptions;
1811+ GtkWidget *hbox1;
1812+ GtkWidget *hbox_spc;
1813+ GtkWidget *label;
1814+ GtkWidget *checkbtn_use_master_password;
1815+ GtkWidget *checkbtn_auto_unload_master_password;
1816+ GtkWidget *spinbtn_encrypted_password_min_length;
1817+ GtkObject *spinbtn_encrypted_password_min_length_adj;
1818+
1819+ vbox_main = gtk_vbox_new (FALSE, VSPACING);
1820+ gtk_widget_show (vbox_main);
1821+ gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox_main);
1822+ gtk_container_set_border_width (GTK_CONTAINER (vbox_main), VBOX_BORDER);
1823+
1824+ vbox_master_password_options = gtk_vbox_new (FALSE, 0);
1825+ gtk_widget_show (vbox_master_password_options);
1826+ gtk_box_pack_start (
1827+ GTK_BOX (vbox_main), vbox_master_password_options, FALSE, FALSE, 0);
1828+
1829+ PACK_CHECK_BUTTON (vbox_master_password_options,
1830+ checkbtn_use_master_password,
1831+ _("Use master password"));
1832+
1833+ vbox_master_password_suboptions = gtk_vbox_new (FALSE, VSPACING_NARROW);
1834+ gtk_widget_show (vbox_master_password_suboptions);
1835+ gtk_box_pack_start (GTK_BOX (vbox_master_password_options),
1836+ vbox_master_password_suboptions,
1837+ FALSE,
1838+ FALSE,
1839+ 0);
1840+
1841+ PACK_CHECK_BUTTON (vbox_master_password_suboptions,
1842+ checkbtn_auto_unload_master_password,
1843+ _("Automatically unload master password "
1844+ "after session initialization"));
1845+
1846+ hbox1 = gtk_hbox_new (FALSE, 8);
1847+ gtk_widget_show (hbox1);
1848+ gtk_box_pack_start (GTK_BOX (vbox_master_password_suboptions),
1849+ hbox1,
1850+ FALSE,
1851+ FALSE,
1852+ 0);
1853+
1854+ hbox_spc = gtk_hbox_new (FALSE, 0);
1855+ gtk_widget_show (hbox_spc);
1856+ gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0);
1857+ gtk_widget_set_size_request (hbox_spc, 12, -1);
1858+
1859+ label = gtk_label_new (_("Min. password length"));
1860+ gtk_widget_show (label);
1861+ gtk_box_pack_start (
1862+ GTK_BOX (hbox1), label, FALSE, FALSE, 0);
1863+
1864+ spinbtn_encrypted_password_min_length_adj = gtk_adjustment_new (
1865+ 32, 0, 99, 1, 5, 0);
1866+ spinbtn_encrypted_password_min_length = gtk_spin_button_new(
1867+ GTK_ADJUSTMENT (spinbtn_encrypted_password_min_length_adj), 1, 0);
1868+ gtk_widget_show (spinbtn_encrypted_password_min_length);
1869+ gtk_box_pack_start (GTK_BOX (hbox1),
1870+ spinbtn_encrypted_password_min_length,
1871+ FALSE,
1872+ FALSE,
1873+ 0);
1874+ gtk_spin_button_set_numeric (
1875+ GTK_SPIN_BUTTON (spinbtn_encrypted_password_min_length), TRUE);
1876+ gtk_widget_set_size_request (spinbtn_encrypted_password_min_length,
1877+ 64,
1878+ -1);
1879+
1880+ SET_TOGGLE_SENSITIVITY (checkbtn_use_master_password,
1881+ vbox_master_password_suboptions);
1882+
1883+ master_password.checkbtn_use_master_password
1884+ = checkbtn_use_master_password;
1885+ master_password.checkbtn_auto_unload_master_password
1886+ = checkbtn_auto_unload_master_password;
1887+ master_password.spinbtn_encrypted_password_min_length
1888+ = spinbtn_encrypted_password_min_length;
1889+}
1890+#endif /* USE_SSL */
1891+
1892 static void prefs_details_create(void)
1893 {
1894 GtkWidget *vbox1;
1895@@ -4719,6 +4899,60 @@ static void prefs_common_online_mode_set_radiobtn(PrefParam *pparam)
1896 }
1897 }
1898
1899+#if USE_GPGME
1900+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn(
1901+ PrefParam *pparam)
1902+{
1903+ PrefsUIData *ui_data;
1904+ GtkRadioButton *radiobtn;
1905+ GSList *group;
1906+
1907+ ui_data = (PrefsUIData *)pparam->ui_data;
1908+ g_return_if_fail(ui_data != NULL);
1909+ g_return_if_fail(*ui_data->widget != NULL);
1910+
1911+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget);
1912+ group = gtk_radio_button_get_group(radiobtn);
1913+ while (group != NULL) {
1914+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data);
1915+
1916+ if (gtk_toggle_button_get_active(btn)) {
1917+ prefs_common.gpg_signature_popup_mode =
1918+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), MENU_VAL_ID));
1919+ break;
1920+ }
1921+ group = group->next;
1922+ }
1923+}
1924+
1925+static void prefs_common_gpg_signature_popup_mode_set_radiobtn(
1926+ PrefParam *pparam)
1927+{
1928+ PrefsUIData *ui_data;
1929+ GtkRadioButton *radiobtn;
1930+ GSList *group;
1931+
1932+ ui_data = (PrefsUIData *)pparam->ui_data;
1933+ g_return_if_fail(ui_data != NULL);
1934+ g_return_if_fail(*ui_data->widget != NULL);
1935+
1936+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget);
1937+ group = gtk_radio_button_get_group(radiobtn);
1938+ while (group != NULL) {
1939+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data);
1940+ gint data;
1941+
1942+ data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn),
1943+ MENU_VAL_ID));
1944+ if (data == prefs_common.gpg_signature_popup_mode) {
1945+ gtk_toggle_button_set_active(btn, TRUE);
1946+ break;
1947+ }
1948+ group = group->next;
1949+ }
1950+}
1951+#endif
1952+
1953 static void prefs_common_dispitem_clicked(void)
1954 {
1955 prefs_summary_column_open(FOLDER_ITEM_IS_SENT_FOLDER
1956diff --git a/src/prefs_ui.c b/src/prefs_ui.c
1957index a3a8f74..2ab1d45 100644
1958--- a/src/prefs_ui.c
1959+++ b/src/prefs_ui.c
1960@@ -31,11 +31,13 @@
1961 #include <errno.h>
1962
1963 #include "prefs.h"
1964+#include "prefs_common.h"
1965 #include "prefs_ui.h"
1966 #include "menu.h"
1967 #include "codeconv.h"
1968 #include "utils.h"
1969 #include "gtkutils.h"
1970+#include "masterpassword.h"
1971
1972 typedef enum
1973 {
1974@@ -268,6 +270,65 @@ void prefs_set_data_from_entry(PrefParam *pparam)
1975 }
1976 }
1977
1978+void prefs_set_data_from_epass_entry(PrefParam *pparam)
1979+{
1980+#if USE_SSL
1981+ PrefsUIData *ui_data;
1982+ gchar **str;
1983+ const gchar *entry_str;
1984+
1985+ /* This is where decrypted passwords are encrypted and stored again */
1986+
1987+ /* master_password == NULL for any of the following reasons:
1988+ * - use_master_password is not enabled (we just save without encrypting)
1989+ * - master_password is auto unloaded (prompt for the master password)
1990+ * - undefined reason (refure to store the password)
1991+ */
1992+ if (!prefs_common.use_master_password) {
1993+ /* check if use_master_password was enabled when Sylpheed started */
1994+ if (!master_password_enabled_on_init) {
1995+ prefs_set_data_from_entry(pparam);
1996+ }
1997+ return;
1998+ } else if (master_password == NULL) {
1999+ if (!prefs_common.auto_unload_master_password) {
2000+ debug_print("Master password enabled, but not loaded for no "
2001+ "apparent reason. Not storing\n");
2002+ return;
2003+ } else {
2004+ if (check_master_password_interactively(3) != MP_RC_OK) {
2005+ debug_print("Failed to reload the master password\n");
2006+ return;
2007+ }
2008+ }
2009+ }
2010+
2011+ ui_data = (PrefsUIData *)pparam->ui_data;
2012+ g_return_if_fail(ui_data != NULL);
2013+ g_return_if_fail(*ui_data->widget != NULL);
2014+
2015+ entry_str = gtk_entry_get_text(GTK_ENTRY(*ui_data->widget));
2016+
2017+ switch (pparam->type) {
2018+ case P_STRING:
2019+ str = (gchar **)pparam->data;
2020+ g_free(*str);
2021+ if ((entry_str != NULL) && (!mpes_string_prefix(entry_str))) {
2022+ debug_print("Encrypting GTK password entry\n");
2023+ *str = encrypt_with_master_password(entry_str);
2024+ } else {
2025+ *str = entry_str[0] ? g_strdup(entry_str) : NULL;
2026+ }
2027+ break;
2028+ default:
2029+ g_warning("Invalid PrefType for GtkEntry widget: %d\n",
2030+ pparam->type);
2031+ }
2032+#else
2033+ prefs_set_data_from_entry(pparam);
2034+#endif
2035+}
2036+
2037 void prefs_set_entry(PrefParam *pparam)
2038 {
2039 PrefsUIData *ui_data;
2040diff --git a/src/prefs_ui.h b/src/prefs_ui.h
2041index cfdf353..f7bd903 100644
2042--- a/src/prefs_ui.h
2043+++ b/src/prefs_ui.h
2044@@ -164,6 +164,7 @@ void prefs_set_data_from_dialog (PrefParam *param);
2045 void prefs_set_dialog_to_default(PrefParam *param);
2046
2047 void prefs_set_data_from_entry (PrefParam *pparam);
2048+void prefs_set_data_from_epass_entry(PrefParam *pparam);
2049 void prefs_set_entry (PrefParam *pparam);
2050 void prefs_set_data_from_text (PrefParam *pparam);
2051 void prefs_set_text (PrefParam *pparam);
2052diff --git a/src/rfc2015.c b/src/rfc2015.c
2053index ebfa96c..48825e2 100644
2054--- a/src/rfc2015.c
2055+++ b/src/rfc2015.c
2056@@ -210,8 +210,12 @@ static void check_signature(MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
2057 gchar *tmp_file;
2058 gint n_exclude_chars = 0;
2059
2060- if (prefs_common.gpg_signature_popup)
2061+ if (prefs_common.gpg_signature_popup &&
2062+ prefs_common.gpg_signature_popup_mode == 1)
2063+ {
2064+ /* FIXME: Perhaps some macro instead of 1 */
2065 statuswindow = gpgmegtk_sig_status_create();
2066+ }
2067
2068 err = gpgme_new(&ctx);
2069 if (err) {
2070@@ -309,8 +313,21 @@ leave:
2071 result = _("Error verifying the signature");
2072 }
2073 debug_print("verification status: %s\n", result);
2074- if (prefs_common.gpg_signature_popup)
2075+ if (prefs_common.gpg_signature_popup &&
2076+ prefs_common.gpg_signature_popup_mode == 1)
2077+ {
2078+ /* FIXME: Perhaps some macro instead of 1 */
2079+ gpgmegtk_sig_status_update(statuswindow, ctx);
2080+ } else if (prefs_common.gpg_signature_popup &&
2081+ verifyresult->signatures->status != GPG_ERR_NO_DATA &&
2082+ verifyresult->signatures->status != GPG_ERR_NO_ERROR)
2083+ {
2084+ /* prefs_common.gpg_signature_popup_mode == 0 is useless as long as
2085+ * there are only two modes (0 and 1)
2086+ */
2087+ statuswindow = gpgmegtk_sig_status_create();
2088 gpgmegtk_sig_status_update(statuswindow, ctx);
2089+ }
2090
2091 g_free (partinfo->sigstatus);
2092 partinfo->sigstatus = g_strdup (result);
2093@@ -319,8 +336,11 @@ leave:
2094 gpgme_data_release(text);
2095 if (ctx)
2096 gpgme_release(ctx);
2097- if (prefs_common.gpg_signature_popup)
2098+ if (prefs_common.gpg_signature_popup) {
2099+ /* gpgmegtk_sig_status_destroy handles NULL params so no complicated
2100+ check is needed */
2101 gpgmegtk_sig_status_destroy(statuswindow);
2102+ }
2103 }
2104
2105 /*
2106@@ -427,7 +447,11 @@ static gpgme_data_t pgp_decrypt(MsgInfo *msginfo, MimeInfo *partinfo, FILE *fp)
2107 debug_print("verification status: %s\n", result);
2108 debug_print("full status: %s\n",
2109 msginfo->encinfo->sigstatus_full);
2110- if (prefs_common.gpg_signature_popup) {
2111+ if (prefs_common.gpg_signature_popup &&
2112+ ((prefs_common.gpg_signature_popup_mode == 1) ||
2113+ (verifyresult->signatures->status != GPG_ERR_NO_DATA &&
2114+ verifyresult->signatures->status != GPG_ERR_NO_ERROR)))
2115+ {
2116 GpgmegtkSigStatus statuswindow;
2117 statuswindow = gpgmegtk_sig_status_create();
2118 gpgmegtk_sig_status_update(statuswindow, ctx);
2119diff --git a/src/send_message.c b/src/send_message.c
2120index a8c2c7a..537c3c8 100644
2121--- a/src/send_message.c
2122+++ b/src/send_message.c
2123@@ -58,6 +58,7 @@
2124 #include "inc.h"
2125 #include "mainwindow.h"
2126 #include "summaryview.h"
2127+#include "masterpassword.h"
2128
2129 #define SMTP_PORT 25
2130 #if USE_SSL
2131@@ -648,13 +649,13 @@ static gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp)
2132
2133 if (ac_prefs->smtp_userid) {
2134 smtp_session->user = g_strdup(ac_prefs->smtp_userid);
2135- if (ac_prefs->smtp_passwd)
2136- smtp_session->pass =
2137- g_strdup(ac_prefs->smtp_passwd);
2138- else if (ac_prefs->tmp_smtp_pass)
2139+ if (ac_prefs->smtp_passwd) {
2140+ smtp_session->pass = decrypt_with_master_password(
2141+ ac_prefs->smtp_passwd);
2142+ } else if (ac_prefs->tmp_smtp_pass) {
2143 smtp_session->pass =
2144 g_strdup(ac_prefs->tmp_smtp_pass);
2145- else {
2146+ } else {
2147 smtp_session->pass =
2148 input_query_password
2149 (ac_prefs->smtp_server,
diff --git a/mail-client/sylpheed/files/sylpheed-3.7.0-PGP-signature-fix.patch b/mail-client/sylpheed/files/sylpheed-3.7.0-PGP-signature-fix.patch
deleted file mode 100644
index f41a22f..0000000
--- a/mail-client/sylpheed/files/sylpheed-3.7.0-PGP-signature-fix.patch
+++ /dev/null
@@ -1,18 +0,0 @@
1diff -rupN sylpheed-3.7.0/libsylph/utils.c sylpheed-3.7.0.new/libsylph/utils.c
2--- sylpheed-3.7.0/libsylph/utils.c 2017-11-30 03:34:14.000000000 +0100
3+++ sylpheed-3.7.0.new/libsylph/utils.c 2018-02-08 08:39:06.586381290 +0100
4@@ -3306,10 +3306,10 @@ gint canonicalize_file(const gchar *src,
5 }
6 }
7
8- if (last_linebreak == TRUE) {
9- if (fputs("\r\n", dest_fp) == EOF)
10- err = TRUE;
11- }
12+ /* if (last_linebreak == TRUE) { */
13+ /* if (fputs("\r\n", dest_fp) == EOF) */
14+ /* err = TRUE; */
15+ /* } */
16
17 if (ferror(src_fp)) {
18 FILE_OP_ERROR(src, "fgets");
diff --git a/mail-client/sylpheed/files/sylpheed-3.7.0-master-password.patch b/mail-client/sylpheed/files/sylpheed-3.7.0-master-password.patch
deleted file mode 100644
index b7fc53a..0000000
--- a/mail-client/sylpheed/files/sylpheed-3.7.0-master-password.patch
+++ /dev/null
@@ -1,1850 +0,0 @@
1diff --git a/README.md b/README.md
2new file mode 100644
3index 0000000..1cf6d21
4--- /dev/null
5+++ b/README.md
6@@ -0,0 +1,219 @@
7+## Implemented features and fixes not present in the official Sylpheed release
8+
9+- (fix)
10+ PGP signature not verified properly when the message has no newline
11+ at the end. https://sylpheed.sraoss.jp/redmine/issues/288
12+
13+- (feature)
14+ Make it possible to select "Show signature check result in a popup window"
15+ only for bad signatures.
16+
17+- (feature)
18+ Support for encrypting and storing encrypted passwords using a master password.
19+ Read bellow for more details!
20+
21+
22+## Master password
23+
24+The master password feature is developed by Simeon Simeonov (sgs)
25+and is currently in an experimental state.
26+
27+
28+### Motivation
29+
30+Currently Sylpheed is storing passwords in plain-text. One can always refrain
31+from storing passwords and let Sylpheed prompt for them, but the more accounts
32+one has, the more annoying this becomes.
33+
34+The goal is to have the passwords stored in a secure way and let Sylpheed only
35+prompt for the master password.
36+
37+
38+### Security goals
39+
40+- attacker (A) should not be able to derive the password from the digest.
41+
42+- A should not be able to derive the master password even if she has
43+ read and write access to the storage.
44+
45+- A should not be able to determine the length of the encrypted password
46+ even if she has read and write access to the storage.
47+
48+- A should not be able to craft an edited password without obtaining the
49+ master password.
50+
51+- A should not be able to compromise the master password or force Sylpheed
52+ to store decrypted passwords even if she has access to a running Sylpheed.
53+
54+- a warning / prompt should be given if a user accidentally types in a "wrong"
55+ master password before decryption is initiated.
56+
57+
58+### Usage in Sylpheed
59+
60+- backup your Sylpheed profile (often $HOME/.sylpheed-2.0)!
61+
62+- start Sylpheed and open "Configuration" -> "Common preferences..."!
63+
64+- select the "Master password" tab, enable "Use master password" and
65+ apply the changes!
66+
67+- restart Sylpheed (exit and then start Sylpheed again)!
68+
69+- you will be asked to type and verify a new master password.
70+
71+- set your new passwords from the "Configuration" -> "Edit accounts..."!
72+
73+- select "Automatically unload master password after session initialization"
74+ for increased security and decreased convenience.
75+
76+Note:
77+Sylpheed will automatically convert existing stored passwords, but it will not
78+touch your backups. You will have to remove all remnants of plain-text
79+passwords manually.
80+
81+
82+### Choice of cryptographic primitives
83+
84+The primary concern when selecting cryptographic primitives was portability.
85+The desire was to go for primitives that are both strong and available in all
86+supported production distributions of OpenSSL and LibreSSL.
87+
88+
89+#### Cipher
90+
91+When it comes to implementation, there are several advantages in using stream
92+cipher or a block cipher that behaves like a stream cipher when used in a
93+certain mode of operation. One is avoiding to deal with padding.
94+
95+AES-256 operating in CFB was selected for these reasons.
96+ChaCha20 should be considered as a replacement in the future.
97+
98+
99+#### Hash-function
100+
101+Hash-functions are used for:
102+- key derivation
103+- plain-text digest
104+
105+Those operations do not have to use the same hash-function.
106+(See the "Encryption & decryption scheme" section for more details!)
107+
108+Key-derivation:
109+Since AES-256 uses a 256 bits key, we need a hash-function with at least
110+the same digest size or bigger.
111+Since the digest (which is the key itself) is considered confidential and is
112+stored only in memory for only a limited amount of time, SHA-256 is considered
113+sufficiently strong for that purpose.
114+
115+Size + plain-text + padding digest:
116+Since the digest is created of both the plain-text and the plain-text size,
117+as well as being encrypted, SHA-256 is considered sufficiently strong.
118+SHA-512 may increase security at the price of adding additional 32 bytes
119+to the encrypted password digest.
120+
121+Stronger hash-functions like SHA-3 or BLAKE2b can be considered as a
122+replacement in the future.
123+
124+
125+#### Master password digest
126+
127+In order to be able to decide whether the user typed a "wrong" master password,
128+before attempting to decrypt, Sylpheed stores a digest of the master password
129+in 'master_password_hash' in sylpheedrc.
130+100000 iterations of PBKDF2_HMAC with SHA-512 and 16 bytes salt is used.
131+Note that this digest is useless as a key and even if a plain-text that
132+produces the same digest is found, it will most probably be useless as a
133+master-password.
134+
135+
136+### Encryption & decryption scheme
137+
138+
139+#### Encryption
140+
141+
142+Input:
143+
144+- plain-text password to be encrypted (P)
145+
146+- plain-text master-password used for key derivation (M)
147+
148+- integer minimum password length (0 < L < 100)
149+
150+
151+Output:
152+
153+- an encrypted password digest (base64) (B)
154+
155+
156+Operation:
157+
158+- generate 16 bytes of random data to be used as a salt (S)
159+
160+- derive the key (K): K = SHA_256(S + M)
161+
162+- produce a 2 byte string (N) indicating the length of P
163+
164+- generate random padding and set N:
165+ - if the length of P < L, produce L - P bytes of random data (R), N = "%02d"
166+ - if the length of P >= L, N = "-1"
167+
168+- produce a hash digest (H): H = SHA_256(N + P + R (if the length of P < L))
169+
170+- encrypt (E): E = AES_256_CFB_ENCRYPT(H + N + P + R (if the length of P < L), K)
171+
172+- B = mpes1:BASE64_ENCODE(S + E)
173+
174+example:
175+mpes1:vo7lsIpD7i6byBA6+vlUoF4OVDfEe+aYRRk4FRtfJ2gMY8M43Kj6WfdfgbViIOl83bI4XEc96okhPW5Mla813aAR1gbPjDg0xmCyIbWOiUv/dg==
176+
177+
178+#### Decryption
179+
180+
181+Input:
182+
183+- encrypted password digest (base64) (B)
184+
185+- plain-text master-password used for key derivation (M)
186+
187+
188+Output:
189+
190+- plain-text password (P)
191+
192+
193+Operation:
194+
195+- remove the prefix (mpes1:) and base64-decode the rest of the digest: B = BASE64_DECODE(B)
196+
197+- fetch the first 16 bytes for the salt: S = B[0 : 15]
198+
199+- derive the key (K): K = SHA_256(S + M)
200+
201+- decrypt the rest of B (D): D = AES_256_CFB_DECRYPT(B[16 :], K)
202+
203+- extract the first 16 bytes for the hash digest (H): H = D[0 : 15]
204+
205+- in order to detect data-inconsistency, assert H == SHA_256(D[16 :])
206+
207+- extract the next 2 bytes for the length of P (N): N = D[16 : 17]
208+
209+- fetch the plain-text:
210+ - if N == "-1" the password is the remaining bytes of D: P = D[18 :]
211+ - if N != "-1", extract the next N-bytes from D: P = D[18 : (18 + N)]
212+
213+
214+### Limitations
215+
216+- currently only the 'password' and 'smtp_password' keys in accountrc
217+ are encrypted.
218+ A mechanism that allows for any key and even folders to be encrypted
219+ should be considered in the future.
220+
221+- currently it is not possible to select alternative ciphers, hash-functions
222+ and modes of operation (without editing the source code).
223+
224+- currently it is not possible to change your master password without having to
225+ (re)set your passwords manually.
226diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am
227index 8f2b76b..40d26a9 100644
228--- a/libsylph/Makefile.am
229+++ b/libsylph/Makefile.am
230@@ -19,6 +19,7 @@ libsylph_0_la_SOURCES = \
231 folder.c \
232 html.c \
233 imap.c \
234+ masterpassword.c \
235 mbox.c \
236 md5.c \
237 md5_hmac.c \
238@@ -62,6 +63,7 @@ libsylph_0include_HEADERS = \
239 folder.h \
240 html.h \
241 imap.h \
242+ masterpassword.h \
243 mbox.h \
244 md5.h \
245 md5_hmac.h \
246diff --git a/libsylph/Makefile.in b/libsylph/Makefile.in
247index 6e3c999..ac8fefd 100644
248--- a/libsylph/Makefile.in
249+++ b/libsylph/Makefile.in
250@@ -129,8 +129,8 @@ libsylph_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
251 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
252 am_libsylph_0_la_OBJECTS = account.lo base64.lo codeconv.lo \
253 customheader.lo displayheader.lo filter.lo folder.lo html.lo \
254- imap.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo nntp.lo \
255- pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \
256+ imap.lo masterpassword.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo \
257+ nntp.lo pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \
258 procmime.lo procmsg.lo quoted-printable.lo recv.lo session.lo \
259 smtp.lo socket.lo socks.lo ssl.lo ssl_hostname_validation.lo \
260 stringtable.lo sylmain.lo unmime.lo utils.lo uuencode.lo \
261@@ -402,6 +402,7 @@ libsylph_0_la_SOURCES = \
262 folder.c \
263 html.c \
264 imap.c \
265+ masterpassword.c \
266 mbox.c \
267 md5.c \
268 md5_hmac.c \
269@@ -445,6 +446,7 @@ libsylph_0include_HEADERS = \
270 folder.h \
271 html.h \
272 imap.h \
273+ masterpassword.h \
274 mbox.h \
275 md5.h \
276 md5_hmac.h \
277@@ -579,6 +581,7 @@ distclean-compile:
278 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folder.Plo@am__quote@
279 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Plo@am__quote@
280 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Plo@am__quote@
281+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/masterpassword.Plo@am__quote@
282 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox.Plo@am__quote@
283 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@
284 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_hmac.Plo@am__quote@
285diff --git a/libsylph/defs.h b/libsylph/defs.h
286index 9e3f82b..67325bf 100644
287--- a/libsylph/defs.h
288+++ b/libsylph/defs.h
289@@ -58,6 +58,9 @@
290 #define DISPLAY_HEADER_RC "dispheaderrc"
291 #define MENU_RC "menurc"
292 #define ACTIONS_RC "actionsrc"
293+#ifdef USE_SSL
294+#define SECURE_RC "securerc"
295+#endif
296 #define COMMAND_HISTORY "command_history"
297 #define TEMPLATE_DIR "templates"
298 #define TMP_DIR "tmp"
299diff --git a/libsylph/imap.c b/libsylph/imap.c
300index aa0d737..e1c7cfd 100644
301--- a/libsylph/imap.c
302+++ b/libsylph/imap.c
303@@ -52,6 +52,7 @@
304 #include "utils.h"
305 #include "prefs_common.h"
306 #include "virtual.h"
307+#include "masterpassword.h"
308
309 #define IMAP4_PORT 143
310 #if USE_SSL
311@@ -705,9 +706,13 @@ static gint imap_session_connect(IMAPSession *session)
312 account = (PrefsAccount *)(SESSION(session)->data);
313
314 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
315- SESSION(session)->server, SESSION(session)->port);
316-
317- pass = account->passwd;
318+ SESSION(session)->server, SESSION(session)->port);
319+ if (master_password_active()) {
320+ pass = decrypt_with_master_password(account->passwd);
321+ /* a new string is allocated. To be removed ... */
322+ } else {
323+ pass = account->passwd;
324+ }
325 if (!pass)
326 pass = account->tmp_pass;
327 if (!pass) {
328@@ -770,8 +775,11 @@ static gint imap_session_connect(IMAPSession *session)
329 #endif
330
331 if (!session->authenticated &&
332- imap_auth(session, account->userid, pass, account->imap_auth_type)
333- != IMAP_SUCCESS) {
334+ imap_auth(session, account->userid, pass, account->imap_auth_type)
335+ != IMAP_SUCCESS) {
336+ if (master_password_active()) {
337+ g_free(pass); /* remove the decrypted password */
338+ }
339 if (account->tmp_pass) {
340 g_free(account->tmp_pass);
341 account->tmp_pass = NULL;
342@@ -780,6 +788,10 @@ static gint imap_session_connect(IMAPSession *session)
343 return IMAP_AUTHFAIL;
344 }
345
346+ if (master_password_active()) {
347+ g_free(pass); /* remove the decrypted password */
348+ }
349+
350 return IMAP_SUCCESS;
351 }
352
353diff --git a/libsylph/masterpassword.c b/libsylph/masterpassword.c
354new file mode 100644
355index 0000000..3d7af94
356--- /dev/null
357+++ b/libsylph/masterpassword.c
358@@ -0,0 +1,216 @@
359+/*
360+ * LibSylph -- E-Mail client library
361+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto
362+ *
363+ * This library is free software; you can redistribute it and/or
364+ * modify it under the terms of the GNU Lesser General Public
365+ * License as published by the Free Software Foundation; either
366+ * version 2.1 of the License, or (at your option) any later version.
367+ *
368+ * This library is distributed in the hope that it will be useful,
369+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
370+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
371+ * Lesser General Public License for more details.
372+ *
373+ * You should have received a copy of the GNU Lesser General Public
374+ * License along with this library; if not, write to the Free Software
375+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
376+ */
377+
378+#ifdef HAVE_CONFIG_H
379+#include "config.h"
380+#endif
381+
382+#include <glib/gi18n.h>
383+
384+#include "prefs_common.h"
385+#include "ssl.h"
386+#include "utils.h"
387+#include "masterpassword.h"
388+
389+
390+gchar *master_password;
391+gboolean master_password_enabled_on_init;
392+
393+void set_master_password(const char *password) {
394+ master_password = password;
395+}
396+
397+gchar *get_master_password(void) {
398+ return master_password;
399+}
400+
401+void cleanse_buffer(void *buf, size_t len) {
402+#if USE_SSL
403+ OPENSSL_cleanse(buf, len);
404+#else
405+ memset(buf, 0, len); /* better than nothing */
406+#endif
407+}
408+
409+void unload_master_password(void) {
410+
411+ debug_print("Unloading master password\n");
412+ if (master_password == NULL) {
413+ /* not loaded / already unloaded */
414+ return;
415+ }
416+ cleanse_buffer(master_password, strlen(master_password));
417+ g_free(master_password);
418+ master_password = NULL;
419+ debug_print("Master password unloaded\n");
420+
421+}
422+
423+gint mpes_string_prefix(const gchar *str) {
424+
425+ /* this function will be expanded in time as the format changes */
426+ if (g_str_has_prefix(str, "mpes1:"))
427+ return 6;
428+ return 0;
429+
430+}
431+
432+gboolean master_password_active(void) {
433+
434+#if USE_SSL
435+ return ((master_password != NULL) &&
436+ prefs_common.use_master_password);
437+#else
438+ return FALSE;
439+#endif
440+
441+}
442+
443+gchar *decrypt_with_master_password(const gchar *str) {
444+
445+#if USE_SSL
446+ gchar *new_str;
447+ gint str_prefix;
448+
449+ if ((!str) || (!prefs_common.use_master_password))
450+ return g_strdup(str);
451+
452+ if (master_password == NULL) {
453+ /* we have empty or auto unloaded master password */
454+ if ((!prefs_common.auto_unload_master_password) ||
455+ (check_master_password_interactively(3) != MP_RC_OK)) {
456+ return g_strdup(str);
457+ }
458+ debug_print("Reloaded master password\n");
459+ }
460+
461+ str_prefix = mpes_string_prefix(str);
462+ if (!str_prefix)
463+ return g_strdup(str);
464+
465+ if (decrypt_data(&new_str,
466+ str + str_prefix,
467+ master_password,
468+ strlen(str) + 1 - str_prefix) != MP_RC_OK) {
469+ OPENSSL_cleanse(new_str, strlen(new_str));
470+ g_free(new_str);
471+ return g_strdup(str);
472+ }
473+
474+ return new_str;
475+#else
476+ return g_strdup(str);
477+#endif
478+
479+}
480+
481+gchar *encrypt_with_master_password(const gchar *str) {
482+
483+#if USE_SSL
484+ gchar *new_str, *mpes1_str;
485+ gint length_encrypted;
486+
487+ if ((!str) || (!master_password_active()))
488+ return NULL;
489+
490+ /*
491+ * unlike the decrypt function, here it is up to the caller
492+ * to make sure that auto unloaded master password is handled properly
493+ */
494+
495+ if (encrypt_data(&new_str,
496+ &length_encrypted,
497+ str,
498+ master_password,
499+ strlen(str) + 1,
500+ prefs_common.encrypted_password_min_length,
501+ TRUE) != MP_RC_OK) {
502+ g_free(new_str);
503+ return NULL;
504+ }
505+
506+ mpes1_str = g_strdup_printf("mpes1:%s", new_str);
507+ g_free(new_str);
508+
509+ return mpes1_str;
510+#else
511+ return NULL;
512+#endif
513+
514+}
515+
516+#if USE_SSL
517+gint set_master_password_interactively(guint max_attempts) {
518+
519+ if (master_password == NULL)
520+ master_password = input_set_new_password(max_attempts);
521+
522+ if (master_password == NULL)
523+ return 1;
524+
525+ if (generate_password_hash(
526+ &prefs_common.master_password_hash,
527+ master_password,
528+ NULL) != MP_RC_OK) {
529+ /* should not really happen unless buggy code / library */
530+ g_free(prefs_common.master_password_hash);
531+ prefs_common.master_password_hash = NULL;
532+ debug_print(_("Could not generate master password hash"));
533+ return 1;
534+ }
535+
536+ prefs_common_write_config();
537+ return 0;
538+
539+}
540+
541+gint check_master_password_interactively(guint max_attempts) {
542+
543+ guint cnt;
544+
545+ g_return_val_if_fail(max_attempts > 0, 1);
546+ g_return_val_if_fail(prefs_common.master_password_hash != NULL, 1);
547+
548+ if (master_password != NULL) {
549+ /* password already cached */
550+ debug_print("Master password already cached\n");
551+ return check_password(master_password,
552+ prefs_common.master_password_hash);
553+ }
554+
555+ for (cnt = 0; cnt < max_attempts; ++cnt) {
556+ master_password = input_query_master_password();
557+ if (master_password == NULL) {
558+ /* input canceled or query_master_password_func not set */
559+ continue;
560+ }
561+ if (check_password(master_password,
562+ prefs_common.master_password_hash) == MP_RC_OK) {
563+ return MP_RC_OK; /* match */
564+ }
565+ debug_print(_("Wrong master password entered (%d)\n"), cnt);
566+ OPENSSL_cleanse(master_password, strlen(master_password));
567+ g_free(master_password);
568+ master_password = NULL;
569+ }
570+
571+ return 1; /* no match */
572+
573+}
574+#endif /* USE_SSL */
575diff --git a/libsylph/masterpassword.h b/libsylph/masterpassword.h
576new file mode 100644
577index 0000000..7254643
578--- /dev/null
579+++ b/libsylph/masterpassword.h
580@@ -0,0 +1,47 @@
581+/*
582+ * LibSylph -- E-Mail client library
583+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto
584+ *
585+ * This library is free software; you can redistribute it and/or
586+ * modify it under the terms of the GNU Lesser General Public
587+ * License as published by the Free Software Foundation; either
588+ * version 2.1 of the License, or (at your option) any later version.
589+ *
590+ * This library is distributed in the hope that it will be useful,
591+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
592+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
593+ * Lesser General Public License for more details.
594+ *
595+ * You should have received a copy of the GNU Lesser General Public
596+ * License along with this library; if not, write to the Free Software
597+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
598+ */
599+
600+#ifndef __MASTERPASSWORD_H__
601+#define __MASTERPASSWORD_H__
602+
603+#ifdef HAVE_CONFIG_H
604+#include "config.h"
605+#endif
606+
607+#define MP_RC_OK 0
608+#define MP_RC_WRONG_HASH_OR_KEY 1
609+#define MP_RC_INVALID_FORMAT 2 /* invalid digest format */
610+
611+extern gchar *master_password;
612+extern gboolean master_password_enabled_on_init; /* m.p. enabled on init? */
613+void set_master_password(const char *password);
614+gchar *get_master_password(void);
615+void cleanse_buffer(void *buf, size_t len);
616+void unload_master_password(void);
617+gint mpes_string_prefix(const gchar *str);
618+gboolean master_password_active(void);
619+gchar *decrypt_with_master_password(const gchar *str);
620+gchar *encrypt_with_master_password(const gchar *str);
621+
622+#if USE_SSL
623+gint set_master_password_interactively(guint max_attempts);
624+gint check_master_password_interactively(guint max_attempts);
625+#endif /* USE_SSL */
626+
627+#endif /* __MASTERPASSWORD_H__ */
628diff --git a/libsylph/news.c b/libsylph/news.c
629index ffff9f9..36d3b10 100644
630--- a/libsylph/news.c
631+++ b/libsylph/news.c
632@@ -44,6 +44,7 @@
633 #include "utils.h"
634 #include "prefs_common.h"
635 #include "prefs_account.h"
636+#include "masterpassword.h"
637 #if USE_SSL
638 # include "ssl.h"
639 #endif
640@@ -249,10 +250,11 @@ static Session *news_session_new_for_folder(Folder *folder)
641 ac = folder->account;
642 if (ac->use_nntp_auth && ac->userid && ac->userid[0]) {
643 userid = ac->userid;
644- if (ac->passwd && ac->passwd[0])
645- passwd = g_strdup(ac->passwd);
646- else
647+ if (ac->passwd && ac->passwd[0]) {
648+ passwd = decrypt_with_master_password(ac->passwd);
649+ } else {
650 passwd = input_query_password(ac->nntp_server, userid);
651+ }
652 }
653
654 if (ac->use_socks && ac->use_socks_for_recv && ac->proxy_host) {
655diff --git a/libsylph/pop.c b/libsylph/pop.c
656index 8cb7f5c..85f3ed9 100644
657--- a/libsylph/pop.c
658+++ b/libsylph/pop.c
659@@ -39,6 +39,7 @@
660 #include "prefs_account.h"
661 #include "utils.h"
662 #include "recv.h"
663+#include "masterpassword.h"
664
665 gint pop3_greeting_recv (Pop3Session *session,
666 const gchar *msg);
667@@ -437,8 +438,9 @@ Session *pop3_session_new(PrefsAccount *account)
668 session->error_msg = NULL;
669
670 session->user = g_strdup(account->userid);
671- session->pass = account->passwd ? g_strdup(account->passwd) :
672- account->tmp_pass ? g_strdup(account->tmp_pass) : NULL;
673+ session->pass = account->passwd ? decrypt_with_master_password(
674+ account->passwd) : account->tmp_pass ? g_strdup(
675+ account->tmp_pass) : NULL;
676
677 SESSION(session)->server = g_strdup(account->recv_server);
678
679diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c
680index 1aecba9..54cbc89 100644
681--- a/libsylph/prefs_account.c
682+++ b/libsylph/prefs_account.c
683@@ -34,6 +34,7 @@
684 #include "customheader.h"
685 #include "account.h"
686 #include "utils.h"
687+#include "masterpassword.h"
688
689 static PrefsAccount tmp_ac_prefs;
690
691@@ -205,7 +206,7 @@ PrefsAccount *prefs_account_new(void)
692 void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label)
693 {
694 const gchar *p = label;
695- gchar *rcpath;
696+ gchar *rcpath, *tmp_str;
697 gint id;
698
699 g_return_if_fail(ac_prefs != NULL);
700@@ -229,6 +230,34 @@ void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label)
701 ac_prefs->use_apop_auth = TRUE;
702 }
703
704+ if (master_password_active()) {
705+ if ((ac_prefs->passwd != NULL) &&
706+ !mpes_string_prefix(ac_prefs->passwd)) {
707+ /* TODO: Perhaps some prompt? */
708+ debug_print(
709+ "%s -> converting passwd cleartext to encrypted\n",
710+ label);
711+ tmp_str = ac_prefs->passwd;
712+ ac_prefs->passwd = encrypt_with_master_password(ac_prefs->passwd);
713+ cleanse_buffer(tmp_str, strlen(tmp_str));
714+ g_free(tmp_str);
715+ }
716+
717+ if ((ac_prefs->smtp_passwd != NULL) &&
718+ !mpes_string_prefix(ac_prefs->smtp_passwd)) {
719+ /* TODO: Perhaps some prompt? */
720+ debug_print(
721+ "%s -> converting smtp_passwd from cleartext to encrypted\n",
722+ label);
723+ tmp_str = ac_prefs->smtp_passwd;
724+ ac_prefs->smtp_passwd = encrypt_with_master_password(
725+ ac_prefs->smtp_passwd);
726+ cleanse_buffer(tmp_str, strlen(tmp_str));
727+ g_free(tmp_str);
728+ }
729+
730+ }
731+
732 custom_header_read_config(ac_prefs);
733 }
734
735diff --git a/libsylph/prefs_common.c b/libsylph/prefs_common.c
736index 91814fd..192964e 100644
737--- a/libsylph/prefs_common.c
738+++ b/libsylph/prefs_common.c
739@@ -418,6 +418,17 @@ static PrefParam param[] = {
740 {"show_gpg_warning", "TRUE", &prefs_common.gpg_warning, P_BOOL},
741 #endif
742
743+ /* Master password */
744+ {"use_master_password", "FALSE", &prefs_common.use_master_password,
745+ P_BOOL},
746+ {"master_password_hash", NULL, &prefs_common.master_password_hash,
747+ P_STRING},
748+ {"encrypted_password_min_length", "32",
749+ &prefs_common.encrypted_password_min_length, P_INT},
750+ {"auto_unload_master_password", "FALSE",
751+ &prefs_common.auto_unload_master_password,
752+ P_BOOL},
753+
754 /* Interface */
755 {"separate_folder", "FALSE", &prefs_common.sep_folder, P_BOOL},
756 {"separate_message", "FALSE", &prefs_common.sep_msg, P_BOOL},
757diff --git a/libsylph/prefs_common.h b/libsylph/prefs_common.h
758index 910f370..6f9c364 100644
759--- a/libsylph/prefs_common.h
760+++ b/libsylph/prefs_common.h
761@@ -249,6 +249,12 @@ struct _PrefsCommon
762 gboolean passphrase_grab;
763 gboolean gpg_warning;
764
765+ /* Master password */
766+ gboolean use_master_password;
767+ gchar *master_password_hash;
768+ guint encrypted_password_min_length;
769+ gboolean auto_unload_master_password;
770+
771 /* Interface */
772 gboolean sep_folder;
773 gboolean sep_msg;
774diff --git a/libsylph/ssl.c b/libsylph/ssl.c
775index 8413925..e782b0e 100644
776--- a/libsylph/ssl.c
777+++ b/libsylph/ssl.c
778@@ -32,6 +32,13 @@
779 #include "ssl.h"
780 #include "ssl_hostname_validation.h"
781
782+#define SALT_SIZE 16
783+#define CIPHER EVP_aes_256_cfb()
784+#define KEY_HASH EVP_sha256()
785+#define DIGEST_HASH EVP_sha256()
786+#define PBKDF2_DIGEST_SIZE 64
787+#define PBKDF2_ITERATIONS 100000
788+
789 static SSL_CTX *ssl_ctx_SSLv23 = NULL;
790 static SSL_CTX *ssl_ctx_TLSv1 = NULL;
791
792@@ -402,4 +409,468 @@ void ssl_set_verify_func(SSLVerifyFunc func)
793 verify_ui_func = func;
794 }
795
796+/* master password related functions */
797+static gint secure_derive_key(guchar *key,
798+ gint length_key,
799+ const gchar *passphrase,
800+ const guchar *salt) {
801+
802+ guint length_buffer, length_hash;
803+ guchar *buffer, *ptr_hash;
804+
805+ EVP_MD_CTX *mdctx;
806+
807+ OPENSSL_cleanse(key, length_key);
808+
809+ length_buffer = SALT_SIZE + strlen(passphrase);
810+ buffer = OPENSSL_malloc(length_buffer);
811+ OPENSSL_cleanse(buffer, length_buffer);
812+
813+ memcpy(buffer, salt, SALT_SIZE);
814+ memcpy(buffer + SALT_SIZE, passphrase, strlen(passphrase));
815+
816+ mdctx = EVP_MD_CTX_create();
817+ EVP_DigestInit_ex(mdctx, KEY_HASH, NULL);
818+ EVP_DigestUpdate(mdctx, buffer, length_buffer);
819+ OPENSSL_cleanse(buffer, length_buffer);
820+
821+ ptr_hash = OPENSSL_malloc(EVP_MD_size(KEY_HASH));
822+ EVP_DigestFinal_ex(mdctx, ptr_hash, &length_hash);
823+
824+ memcpy(key,
825+ ptr_hash,
826+ (length_hash > length_key) ? length_key : length_hash);
827+
828+ OPENSSL_cleanse(ptr_hash, length_hash);
829+ OPENSSL_free(ptr_hash);
830+ OPENSSL_free(buffer);
831+ EVP_MD_CTX_destroy(mdctx);
832+
833+ return SSL_RC_OK;
834+
835+}
836+
837+gint encrypt_data(gchar **encrypted,
838+ gint *length_encrypted,
839+ const gchar *data,
840+ const gchar *passphrase,
841+ gint length_data,
842+ guint min_data_length,
843+ gboolean rnd_salt) {
844+
845+ gint crypt_buffer_cnt, rc;
846+ guint key_size, length_hash;
847+ guint length_cleartext, length_ciphertext, length_total;
848+ guchar salt[SALT_SIZE];
849+ guchar *ciphertext_buffer, *total_buffer;
850+ /* sensitive buffers and counters */
851+ guint length_data_payload, length_padding;
852+ gchar str_data_size[3];
853+ guchar *data_payload_buffer, *hash_buffer, *padding_buffer, *key;
854+ guchar *cleartext_buffer;
855+
856+ EVP_CIPHER_CTX *ctx;
857+ EVP_MD_CTX *mdctx;
858+
859+ rc = SSL_RC_ERROR;
860+
861+ if (length_data < 1) {
862+ return -1;
863+ }
864+ if (rnd_salt) {
865+ if (RAND_bytes(salt, SALT_SIZE) != 1) {
866+ debug_print("Random problems...\n");
867+ goto cleanup;
868+ }
869+ } else {
870+ strncpy((gchar *)salt, "FOR TESTING ONLY", SALT_SIZE);
871+ }
872+
873+ key_size = EVP_CIPHER_key_length(CIPHER);
874+ key = OPENSSL_malloc(key_size);
875+ OPENSSL_cleanse(key, key_size);
876+ if (secure_derive_key(key,
877+ key_size,
878+ passphrase,
879+ salt) != SSL_RC_OK) {
880+ OPENSSL_cleanse(key, key_size);
881+ debug_print("Could not generate secure key\n");
882+ goto cleanup;
883+ }
884+
885+ /* prepare the data-buffer */
886+ length_padding = 0;
887+ if (length_data < min_data_length) {
888+ g_snprintf(str_data_size, 3, "%02d", length_data);
889+ length_padding = min_data_length - length_data;
890+ padding_buffer = OPENSSL_malloc(length_padding);
891+ OPENSSL_cleanse(padding_buffer, length_padding);
892+ if (RAND_bytes(padding_buffer, length_padding) != 1) {
893+ debug_print("Random problems...\n");
894+ goto cleanup;
895+ }
896+ } else {
897+ g_snprintf(str_data_size, 3, "-1");
898+ }
899+
900+ length_data_payload = 2 + length_data + length_padding;
901+ data_payload_buffer = OPENSSL_malloc(length_data_payload);
902+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
903+
904+ memcpy(data_payload_buffer, str_data_size, 2);
905+ memcpy(data_payload_buffer + 2, data, length_data);
906+ if (length_padding > 0) {
907+ memcpy(data_payload_buffer + (2 + length_data),
908+ padding_buffer,
909+ length_padding);
910+ }
911+
912+ mdctx = EVP_MD_CTX_create();
913+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
914+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
915+
916+ length_hash = EVP_MD_size(DIGEST_HASH);
917+ hash_buffer = OPENSSL_malloc(length_hash);
918+ OPENSSL_cleanse(hash_buffer, length_hash);
919+ EVP_DigestFinal_ex(mdctx, hash_buffer, NULL);
920+
921+ length_cleartext = length_hash + length_data_payload;
922+
923+ cleartext_buffer = OPENSSL_malloc(length_cleartext);
924+ OPENSSL_cleanse(cleartext_buffer, length_cleartext);
925+
926+ /* assemble cleartext-buffer */
927+ memcpy(cleartext_buffer, hash_buffer, length_hash);
928+ memcpy(cleartext_buffer + length_hash,
929+ data_payload_buffer,
930+ length_data_payload);
931+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); /* sensitive */
932+
933+ /* encryption */
934+ if (!(ctx = EVP_CIPHER_CTX_new())) {
935+ debug_print("New ctx failed\n");
936+ goto cleanup;
937+ }
938+
939+ length_ciphertext = 0;
940+ if (EVP_EncryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
941+ debug_print("EVP_EncryptInit_ex failed\n");
942+ goto cleanup;
943+ }
944+
945+ ciphertext_buffer = OPENSSL_malloc(length_cleartext);
946+
947+ crypt_buffer_cnt = 0;
948+ while(1) {
949+ if (EVP_EncryptUpdate(ctx,
950+ ciphertext_buffer + length_ciphertext,
951+ &crypt_buffer_cnt,
952+ cleartext_buffer + length_ciphertext,
953+ 1) != 1) { /* one byte at a time */
954+ debug_print("EVP_EncryptUpdate failed\n");
955+ goto cleanup;
956+ }
957+
958+ if (crypt_buffer_cnt != 1) { /* paranoia */
959+ debug_print("The sizes of enc and dec text do not correspond\n");
960+ goto cleanup;
961+ }
962+
963+ ++length_ciphertext;
964+
965+ if (length_ciphertext >= length_cleartext) {
966+ break;
967+ }
968+ }
969+ /* No padding required for the CFB mode */
970+
971+ OPENSSL_cleanse(cleartext_buffer, length_cleartext); /* sensitive */
972+ length_total = SALT_SIZE + length_ciphertext;
973+ total_buffer = OPENSSL_malloc(length_total);
974+
975+ memcpy(total_buffer, salt, SALT_SIZE);
976+ memcpy(total_buffer + SALT_SIZE, ciphertext_buffer, length_ciphertext);
977+
978+ *encrypted = g_base64_encode(total_buffer, length_total);
979+ *length_encrypted = strlen(*encrypted);
980+
981+ rc = SSL_RC_OK;
982+
983+cleanup:
984+ /* key */
985+ OPENSSL_cleanse(key, key_size);
986+ OPENSSL_free(key);
987+ /* cleartext buffer */
988+ OPENSSL_cleanse(cleartext_buffer, length_cleartext);
989+ OPENSSL_free(cleartext_buffer);
990+ /* payload buffer */
991+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
992+ OPENSSL_free(data_payload_buffer);
993+ /* hash */
994+ OPENSSL_cleanse(hash_buffer, length_hash);
995+ OPENSSL_free(hash_buffer);
996+ /* padding */
997+ if (length_padding > 0) {
998+ OPENSSL_cleanse(padding_buffer, length_padding);
999+ OPENSSL_free(padding_buffer);
1000+ }
1001+
1002+ OPENSSL_cleanse(str_data_size, 3); /* paranoia */
1003+
1004+ /* ciphertext buffer */
1005+ OPENSSL_free(ciphertext_buffer);
1006+
1007+ /* total buffer */
1008+ OPENSSL_free(total_buffer);
1009+
1010+ length_data_payload = 0;
1011+ length_padding = 0;
1012+
1013+ EVP_CIPHER_CTX_free(ctx);
1014+ EVP_MD_CTX_destroy(mdctx);
1015+
1016+ return rc;
1017+
1018+}
1019+
1020+gint decrypt_data(gchar **decrypted,
1021+ const gchar *data,
1022+ const gchar *passphrase,
1023+ gint length_data) {
1024+
1025+ gint rc; /* return code */
1026+ gint decrypt_buffer_cnt, length_ciphertext, length_decrypted;
1027+ guint key_size, length_hash, length_cleartext;
1028+ gsize length_total;
1029+ guchar salt[SALT_SIZE];
1030+ guchar *ciphertext_buffer, *total_buffer;
1031+ /* sensitive buffers and counters */
1032+ gint data_size;
1033+ guint length_data_payload;
1034+ gchar str_data_size[3];
1035+ guchar *data_payload_buffer, *hash_buffer, *key;
1036+ guchar *cleartext_buffer;
1037+
1038+ EVP_CIPHER_CTX *ctx;
1039+ EVP_MD_CTX *mdctx;
1040+
1041+ rc = -1;
1042+
1043+ if (length_data < 1) {
1044+ return -1;
1045+ }
1046+
1047+ total_buffer = g_base64_decode(data, &length_total);
1048+ length_ciphertext = length_total - SALT_SIZE;
1049+ ciphertext_buffer = OPENSSL_malloc(length_ciphertext);
1050+ OPENSSL_cleanse(ciphertext_buffer, length_ciphertext);
1051+
1052+ memcpy(salt, total_buffer, SALT_SIZE);
1053+ memcpy(ciphertext_buffer, total_buffer + SALT_SIZE, length_ciphertext);
1054+
1055+ key_size = EVP_CIPHER_key_length(CIPHER);
1056+
1057+ /* decryption */
1058+ if(!(ctx = EVP_CIPHER_CTX_new())) {
1059+ debug_print("New ctx failed\n");
1060+ goto cleanup;
1061+ }
1062+
1063+ key = OPENSSL_malloc(key_size);
1064+ OPENSSL_cleanse(key, key_size);
1065+ if (secure_derive_key(key,
1066+ key_size,
1067+ passphrase,
1068+ salt) != 0) {
1069+ OPENSSL_cleanse(key, key_size);
1070+ debug_print("Could not generate secure key\n");
1071+ goto cleanup;
1072+ }
1073+
1074+ if (EVP_DecryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
1075+ debug_print("EVP_DecryptInit_ex failed\n");
1076+ goto cleanup;
1077+ }
1078+ OPENSSL_cleanse(key, key_size); /* highly sensitive */
1079+ cleartext_buffer = OPENSSL_malloc(length_ciphertext);
1080+ OPENSSL_cleanse(cleartext_buffer, length_ciphertext);
1081+
1082+ length_cleartext = 0;
1083+ decrypt_buffer_cnt = 0;
1084+
1085+ while(1) {
1086+ if (EVP_DecryptUpdate(ctx,
1087+ cleartext_buffer + length_cleartext,
1088+ &decrypt_buffer_cnt,
1089+ ciphertext_buffer + length_cleartext,
1090+ 1) != 1) { /* one byte at a time */
1091+ debug_print("EVP_EncryptUpdate failed\n");
1092+ goto cleanup;
1093+ }
1094+
1095+ if (decrypt_buffer_cnt != 1) { /* paranoia */
1096+ debug_print("The sizes of enc and dec text do not correspond");
1097+ goto cleanup;
1098+ }
1099+
1100+ ++length_cleartext;
1101+
1102+ if (length_cleartext >= length_ciphertext) {
1103+ break;
1104+ }
1105+ }
1106+
1107+
1108+ length_hash = EVP_MD_size(DIGEST_HASH);
1109+ hash_buffer = OPENSSL_malloc(length_hash);
1110+ length_data_payload = length_cleartext - length_hash;
1111+ data_payload_buffer = OPENSSL_malloc(length_data_payload);
1112+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1113+
1114+ memcpy(data_payload_buffer,
1115+ cleartext_buffer + length_hash,
1116+ length_data_payload);
1117+
1118+ mdctx = EVP_MD_CTX_create();
1119+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
1120+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
1121+ EVP_DigestFinal_ex(mdctx, hash_buffer, &length_hash);
1122+
1123+ if (strncmp((const gchar*) hash_buffer,
1124+ (const gchar*) cleartext_buffer,
1125+ length_hash) != 0) {
1126+ debug_print("Invalid hash\n");
1127+ rc = SSL_RC_WRONG_HASH_OR_KEY;
1128+ goto cleanup;
1129+ }
1130+
1131+ memcpy(str_data_size, cleartext_buffer + length_hash, 2);
1132+ data_size = atoi(str_data_size);
1133+
1134+ if (data_size < 0) {
1135+ length_decrypted = length_data_payload - 2;
1136+ } else {
1137+ length_decrypted = data_size;
1138+ }
1139+ *decrypted = OPENSSL_malloc(length_decrypted);
1140+ memcpy(*decrypted, data_payload_buffer + 2, length_decrypted);
1141+
1142+ rc = SSL_RC_OK;
1143+
1144+cleanup:
1145+
1146+ /* key */
1147+ OPENSSL_cleanse(key, key_size);
1148+ OPENSSL_free(key);
1149+ /* payload buffer */
1150+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1151+ OPENSSL_free(data_payload_buffer);
1152+ /* hash */
1153+ OPENSSL_cleanse(hash_buffer, length_hash);
1154+ OPENSSL_free(hash_buffer);
1155+ /* ciphertext */
1156+ OPENSSL_free(ciphertext_buffer);
1157+ OPENSSL_free(total_buffer);
1158+
1159+ EVP_CIPHER_CTX_free(ctx);
1160+ EVP_MD_CTX_destroy(mdctx);
1161+
1162+ return rc;
1163+
1164+}
1165+
1166+gint generate_password_hash(gchar **password_hash,
1167+ const gchar *password,
1168+ const guchar *salt) {
1169+ /*
1170+ * Hashes 'password' using PKCS5_PBKDF2_HMAC with SHA512 and 'salt',
1171+ * and assigns a string to 'password_hash' with the following format:
1172+ * pbkdf2_sha512$iterations$base64(salt)$base64(password_hash)
1173+ */
1174+ guchar lsalt[SALT_SIZE];
1175+ gchar *PBKDF2_digest, *salt_b64, *digest_b64;
1176+
1177+ if (salt == NULL) {
1178+ if (RAND_bytes(lsalt, SALT_SIZE) != 1) {
1179+ debug_print("Random problems...\n");
1180+ return SSL_RC_ERROR;
1181+ }
1182+ } else {
1183+ memcpy(lsalt, salt, SALT_SIZE);
1184+ }
1185+
1186+ PBKDF2_digest = OPENSSL_malloc(PBKDF2_DIGEST_SIZE);
1187+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1188+
1189+ PKCS5_PBKDF2_HMAC(password,
1190+ strlen(password),
1191+ lsalt,
1192+ SALT_SIZE,
1193+ PBKDF2_ITERATIONS,
1194+ EVP_sha512(),
1195+ PBKDF2_DIGEST_SIZE,
1196+ (guchar *) PBKDF2_digest);
1197+ digest_b64 = g_base64_encode((guchar *) PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1198+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1199+ OPENSSL_free(PBKDF2_digest);
1200+
1201+ salt_b64 = g_base64_encode(lsalt, SALT_SIZE);
1202+
1203+ *password_hash = g_strdup_printf("pbkdf2_sha512$%d$%s$%s",
1204+ PBKDF2_ITERATIONS,
1205+ salt_b64,
1206+ digest_b64);
1207+
1208+ OPENSSL_cleanse(digest_b64, strlen(digest_b64));
1209+ OPENSSL_free(digest_b64);
1210+
1211+ OPENSSL_cleanse(salt_b64, strlen(salt_b64));
1212+ OPENSSL_free(salt_b64);
1213+
1214+ return SSL_RC_OK;
1215+
1216+}
1217+
1218+gint check_password(const gchar *password, const gchar *password_hash) {
1219+
1220+ gint token_counter, rc;
1221+ guchar *salt;
1222+ gchar **tokens, *new_hash;
1223+ gsize salt_length;
1224+
1225+ rc = SSL_RC_ERROR;
1226+ tokens = g_strsplit(password_hash,
1227+ "$",
1228+ -1);
1229+ token_counter = 0;
1230+ while (*(tokens + token_counter) != NULL) {
1231+ ++token_counter;
1232+ }
1233+
1234+ if (token_counter != 4) {
1235+ debug_print("Invalid password hash...\n");
1236+ goto cleanup;
1237+ }
1238+
1239+ salt = g_base64_decode(*(tokens + 2), &salt_length);
1240+ if (salt_length != SALT_SIZE) {
1241+ debug_print("Salt size does not match\n");
1242+ goto cleanup;
1243+ }
1244+
1245+ if (generate_password_hash(&new_hash, password, salt) != SSL_RC_OK) {
1246+ debug_print("Password hash generation failed\n");
1247+ goto cleanup;
1248+ }
1249+
1250+ rc = g_strcmp0(password_hash, new_hash);
1251+
1252+cleanup:
1253+ g_free(salt);
1254+ g_free(new_hash);
1255+ g_strfreev(tokens);
1256+ return rc;
1257+
1258+}
1259+
1260 #endif /* USE_SSL */
1261diff --git a/libsylph/ssl.h b/libsylph/ssl.h
1262index a9f690d..4c0ccd1 100644
1263--- a/libsylph/ssl.h
1264+++ b/libsylph/ssl.h
1265@@ -32,9 +32,15 @@
1266 #include <openssl/pem.h>
1267 #include <openssl/ssl.h>
1268 #include <openssl/err.h>
1269+#include <openssl/evp.h>
1270+#include <openssl/rand.h>
1271
1272 #include "socket.h"
1273
1274+#define SSL_RC_OK 0
1275+#define SSL_RC_ERROR -1
1276+#define SSL_RC_WRONG_HASH_OR_KEY 1
1277+
1278 typedef enum {
1279 SSL_METHOD_SSLv23,
1280 SSL_METHOD_TLSv1
1281@@ -60,6 +66,26 @@ void ssl_done_socket (SockInfo *sockinfo);
1282
1283 void ssl_set_verify_func (SSLVerifyFunc func);
1284
1285+/* master password related code */
1286+gint encrypt_data(gchar **encrypted,
1287+ gint *length_encrypted,
1288+ const gchar *data,
1289+ const gchar *passphrase,
1290+ gint length_data,
1291+ guint min_data_length,
1292+ gboolean rnd_salt);
1293+
1294+gint decrypt_data(gchar **decrypted,
1295+ const gchar *data,
1296+ const gchar *passphrase,
1297+ gint length_data);
1298+
1299+gint generate_password_hash(gchar **password_hash,
1300+ const gchar *password,
1301+ const guchar *salt);
1302+
1303+gint check_password(const gchar *password, const gchar *password_hash);
1304+/* ---------------------------- */
1305 #endif /* USE_SSL */
1306
1307 #endif /* __SSL_H__ */
1308diff --git a/libsylph/utils.c b/libsylph/utils.c
1309index 6ecf328..3bbcbcf 100644
1310--- a/libsylph/utils.c
1311+++ b/libsylph/utils.c
1312@@ -4598,6 +4598,36 @@ gchar *input_query_password(const gchar *server, const gchar *user)
1313 return NULL;
1314 }
1315
1316+static QueryMasterPasswordFunc query_master_password_func = NULL;
1317+
1318+void set_input_query_master_password_func(QueryMasterPasswordFunc func)
1319+{
1320+ query_master_password_func = func;
1321+}
1322+
1323+gchar *input_query_master_password(void)
1324+{
1325+ if (query_master_password_func)
1326+ return query_master_password_func();
1327+ else
1328+ return NULL;
1329+}
1330+
1331+static SetNewPasswordFunc set_new_password_func = NULL;
1332+
1333+void set_input_set_new_password_func(SetNewPasswordFunc func)
1334+{
1335+ set_new_password_func = func;
1336+}
1337+
1338+gchar *input_set_new_password(guint max_attempts)
1339+{
1340+ if (set_new_password_func)
1341+ return set_new_password_func(max_attempts);
1342+ else
1343+ return NULL;
1344+}
1345+
1346 /* logging */
1347
1348 static FILE *log_fp = NULL;
1349diff --git a/libsylph/utils.h b/libsylph/utils.h
1350index 9ac65cf..ede55ff 100644
1351--- a/libsylph/utils.h
1352+++ b/libsylph/utils.h
1353@@ -202,6 +202,8 @@ typedef void (*ProgressFunc) (gint cur,
1354 gint total);
1355 typedef gchar * (*QueryPasswordFunc) (const gchar *server,
1356 const gchar *user);
1357+typedef gchar * (*QueryMasterPasswordFunc) (void);
1358+typedef gchar * (*SetNewPasswordFunc) (guint max_attempts);
1359 typedef void (*LogFunc) (const gchar *str);
1360 typedef void (*LogFlushFunc) (void);
1361
1362@@ -560,9 +562,12 @@ void progress_show (gint cur,
1363
1364 /* user input */
1365 void set_input_query_password_func (QueryPasswordFunc func);
1366-
1367 gchar *input_query_password (const gchar *server,
1368 const gchar *user);
1369+void set_input_query_master_password_func(QueryMasterPasswordFunc func);
1370+gchar *input_query_master_password(void);
1371+void set_input_set_new_password_func(SetNewPasswordFunc func);
1372+gchar *input_set_new_password(guint max_attempts);
1373
1374 /* logging */
1375 void set_log_file (const gchar *filename);
1376diff --git a/src/inputdialog.c b/src/inputdialog.c
1377index a601085..cdfb997 100644
1378--- a/src/inputdialog.c
1379+++ b/src/inputdialog.c
1380@@ -44,6 +44,7 @@
1381 #include "filesel.h"
1382 #include "prefs_common.h"
1383 #include "gtkutils.h"
1384+#include "masterpassword.h"
1385 #include "utils.h"
1386
1387 #define DIALOG_WIDTH 420
1388@@ -156,6 +157,57 @@ gchar *input_dialog_query_password(const gchar *server, const gchar *user)
1389 return pass;
1390 }
1391
1392+gchar *input_dialog_query_master_password(void)
1393+{
1394+ gchar *mp_input;
1395+
1396+ mp_input = input_dialog_with_invisible(_("Input password"),
1397+ _("Master password"),
1398+ NULL);
1399+
1400+ if (prefs_common.use_master_password &&
1401+ prefs_common.auto_unload_master_password)
1402+ {
1403+ g_timeout_add(1000 * 30, unload_master_password, NULL);
1404+ debug_print("Master password to be unloaded in 30 seconds\n");
1405+ }
1406+
1407+ return mp_input;
1408+}
1409+
1410+gchar *input_dialog_set_new_password(guint max_attempts)
1411+{
1412+ guint cnt;
1413+ gchar *pass1, *pass2;
1414+
1415+ if (max_attempts < 1)
1416+ return NULL;
1417+
1418+ for (cnt = 0; cnt < max_attempts; ++cnt) {
1419+ pass1 = input_dialog_with_invisible(
1420+ _("Input password"),
1421+ _("New password"),
1422+ NULL);
1423+ pass2 = input_dialog_with_invisible(
1424+ _("Input password"),
1425+ _("Confirm new password"),
1426+ NULL);
1427+
1428+ if (pass1 != NULL && pass2 != NULL && strcmp(pass1, pass2) == 0) {
1429+ cleanse_buffer(pass2, strlen(pass2));
1430+ g_free(pass2);
1431+ break;
1432+ }
1433+ cleanse_buffer(pass1, strlen(pass1));
1434+ cleanse_buffer(pass2, strlen(pass2));
1435+ g_free(pass2);
1436+ g_free(pass1);
1437+ pass1 = NULL;
1438+ }
1439+
1440+ return pass1;
1441+}
1442+
1443 gchar *input_dialog_with_filesel(const gchar *title, const gchar *message,
1444 const gchar *default_string,
1445 GtkFileChooserAction action)
1446diff --git a/src/inputdialog.h b/src/inputdialog.h
1447index 02bdda3..5364d1a 100644
1448--- a/src/inputdialog.h
1449+++ b/src/inputdialog.h
1450@@ -36,7 +36,8 @@ gchar *input_dialog_combo (const gchar *title,
1451 gboolean case_sensitive);
1452 gchar *input_dialog_query_password (const gchar *server,
1453 const gchar *user);
1454-
1455+gchar *input_dialog_query_master_password(void);
1456+gchar *input_dialog_set_new_password(guint max_attempts);
1457 gchar *input_dialog_with_filesel (const gchar *title,
1458 const gchar *message,
1459 const gchar *default_string,
1460diff --git a/src/main.c b/src/main.c
1461index 77d8192..97a4bd0 100644
1462--- a/src/main.c
1463+++ b/src/main.c
1464@@ -87,6 +87,7 @@
1465 #include "foldersel.h"
1466 #include "update_check.h"
1467 #include "colorlabel.h"
1468+#include "masterpassword.h"
1469
1470 #if USE_GPGME
1471 # include "rfc2015.h"
1472@@ -265,14 +266,54 @@ int main(int argc, char *argv[])
1473 set_ui_update_func(gtkut_events_flush);
1474 set_progress_func(main_window_progress_show);
1475 set_input_query_password_func(input_dialog_query_password);
1476+ set_input_set_new_password_func(input_dialog_set_new_password);
1477 #if USE_SSL
1478 ssl_init();
1479 ssl_set_verify_func(ssl_manager_verify_cert);
1480+ set_input_query_master_password_func(input_dialog_query_master_password);
1481 #endif
1482
1483 CHDIR_EXIT_IF_FAIL(get_home_dir(), 1);
1484
1485 prefs_common_read_config();
1486+ set_master_password(NULL);
1487+#if USE_SSL
1488+ if (prefs_common.use_master_password) {
1489+ if (prefs_common.master_password_hash != NULL) {
1490+ if (check_master_password_interactively(3) != MP_RC_OK) {
1491+ if (alertpanel(_("Master password"),
1492+ _("Invalid master password"),
1493+ GTK_STOCK_DISCARD,
1494+ GTK_STOCK_QUIT,
1495+ NULL) != G_ALERTDEFAULT) {
1496+ exit(1);
1497+ }
1498+ }
1499+ } else {
1500+ alertpanel_notice(
1501+ _("Master password enabled but not set. Setting one now"));
1502+ if (set_master_password_interactively(3) != MP_RC_OK) {
1503+ if (alertpanel(_("Master password"),
1504+ _("Unable to set master password"),
1505+ GTK_STOCK_DISCARD,
1506+ GTK_STOCK_QUIT,
1507+ NULL) != G_ALERTDEFAULT) {
1508+ exit(1);
1509+ }
1510+ }
1511+ }
1512+ }
1513+ /* security goal:
1514+ * if the master password is enabled and loaded on init,
1515+ * an attacker can potentially set use_master_password - disabled
1516+ * afterwards and then force Sylpheed to save account data - the result
1517+ * being passwords saved to accountrc in plain-text.
1518+ * possible solution:
1519+ * Do not allow the passwords to be stored in plain-text if Sylpheed
1520+ * was started with use_master_password - enabled.
1521+ */
1522+ master_password_enabled_on_init = prefs_common.use_master_password;
1523+#endif
1524 filter_set_addressbook_func(addressbook_has_address);
1525 filter_read_config();
1526 prefs_actions_read_config();
1527@@ -381,6 +422,13 @@ int main(int argc, char *argv[])
1528
1529 remote_command_exec();
1530
1531+#if USE_SSL
1532+ if (prefs_common.auto_unload_master_password && master_password_active()) {
1533+ debug_print("Auto unloading master password\n");
1534+ unload_master_password();
1535+ }
1536+#endif
1537+
1538 #if USE_UPDATE_CHECK
1539 if (prefs_common.auto_update_check)
1540 update_check(FALSE);
1541@@ -993,6 +1041,7 @@ void app_will_exit(gboolean force)
1542
1543 /* remove temporary files, close log file, socket cleanup */
1544 #if USE_SSL
1545+ unload_master_password();
1546 ssl_done();
1547 #endif
1548 syl_cleanup();
1549diff --git a/src/prefs_account_dialog.c b/src/prefs_account_dialog.c
1550index e9cba13..e215b8e 100644
1551--- a/src/prefs_account_dialog.c
1552+++ b/src/prefs_account_dialog.c
1553@@ -267,7 +267,7 @@ static PrefsUIData ui_data[] = {
1554 {"user_id", &basic.uid_entry,
1555 prefs_set_data_from_entry, prefs_set_entry},
1556 {"password", &basic.pass_entry,
1557- prefs_set_data_from_entry, prefs_set_entry},
1558+ prefs_set_data_from_epass_entry, prefs_set_entry},
1559
1560 /* Receive */
1561 {"use_apop_auth", &receive.use_apop_chkbtn,
1562@@ -313,7 +313,7 @@ static PrefsUIData ui_data[] = {
1563 {"smtp_user_id", &p_send.smtp_uid_entry,
1564 prefs_set_data_from_entry, prefs_set_entry},
1565 {"smtp_password", &p_send.smtp_pass_entry,
1566- prefs_set_data_from_entry, prefs_set_entry},
1567+ prefs_set_data_from_epass_entry, prefs_set_entry},
1568
1569 {"pop_before_smtp", &p_send.pop_bfr_smtp_chkbtn,
1570 prefs_set_data_from_toggle, prefs_set_toggle},
1571diff --git a/src/prefs_common_dialog.c b/src/prefs_common_dialog.c
1572index fa4be4e..119a056 100644
1573--- a/src/prefs_common_dialog.c
1574+++ b/src/prefs_common_dialog.c
1575@@ -216,6 +216,14 @@ static struct Privacy {
1576 } privacy;
1577 #endif
1578
1579+#if USE_SSL
1580+static struct MasterPassword {
1581+ GtkWidget *checkbtn_use_master_password;
1582+ GtkWidget *checkbtn_auto_unload_master_password;
1583+ GtkWidget *spinbtn_encrypted_password_min_length;
1584+} master_password;
1585+#endif
1586+
1587 static struct Interface {
1588 GtkWidget *checkbtn_always_show_msg;
1589 GtkWidget *checkbtn_always_mark_read;
1590@@ -563,6 +571,18 @@ static PrefsUIData ui_data[] = {
1591 prefs_set_data_from_toggle, prefs_set_toggle},
1592 #endif /* USE_GPGME */
1593
1594+#if USE_SSL
1595+ {"use_master_password", &master_password.checkbtn_use_master_password,
1596+ prefs_set_data_from_toggle, prefs_set_toggle},
1597+ {"auto_unload_master_password",
1598+ &master_password.checkbtn_auto_unload_master_password,
1599+ prefs_set_data_from_toggle, prefs_set_toggle},
1600+ {"encrypted_password_min_length",
1601+ &master_password.spinbtn_encrypted_password_min_length,
1602+ prefs_set_data_from_spinbtn,
1603+ prefs_set_spinbtn},
1604+#endif
1605+
1606 /* Interface */
1607 {"always_show_message_when_selected",
1608 &iface.checkbtn_always_show_msg,
1609@@ -693,6 +713,9 @@ static void prefs_junk_create (void);
1610 #if USE_GPGME
1611 static void prefs_privacy_create (void);
1612 #endif
1613+#if USE_SSL
1614+static void prefs_master_password_create (void);
1615+#endif
1616 static void prefs_details_create (void);
1617 static GtkWidget *prefs_other_create (void);
1618 static GtkWidget *prefs_extcmd_create (void);
1619@@ -852,6 +875,10 @@ static void prefs_common_create(void)
1620 #if USE_GPGME
1621 prefs_privacy_create();
1622 SET_NOTEBOOK_LABEL(dialog.notebook, _("Privacy"), page++);
1623+#endif
1624+#if USE_SSL
1625+ prefs_master_password_create();
1626+ SET_NOTEBOOK_LABEL(dialog.notebook, _("Master password"), page++);
1627 #endif
1628 prefs_details_create();
1629 SET_NOTEBOOK_LABEL(dialog.notebook, _("Details"), page++);
1630@@ -2648,6 +2675,93 @@ static void prefs_privacy_create(void)
1631 }
1632 #endif /* USE_GPGME */
1633
1634+#if USE_SSL
1635+static void prefs_master_password_create(void)
1636+{
1637+ GtkWidget *vbox_main;
1638+ GtkWidget *vbox_master_password_options;
1639+ GtkWidget *vbox_master_password_suboptions;
1640+ GtkWidget *hbox1;
1641+ GtkWidget *hbox_spc;
1642+ GtkWidget *label;
1643+ GtkWidget *checkbtn_use_master_password;
1644+ GtkWidget *checkbtn_auto_unload_master_password;
1645+ GtkWidget *spinbtn_encrypted_password_min_length;
1646+ GtkObject *spinbtn_encrypted_password_min_length_adj;
1647+
1648+ vbox_main = gtk_vbox_new (FALSE, VSPACING);
1649+ gtk_widget_show (vbox_main);
1650+ gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox_main);
1651+ gtk_container_set_border_width (GTK_CONTAINER (vbox_main), VBOX_BORDER);
1652+
1653+ vbox_master_password_options = gtk_vbox_new (FALSE, 0);
1654+ gtk_widget_show (vbox_master_password_options);
1655+ gtk_box_pack_start (
1656+ GTK_BOX (vbox_main), vbox_master_password_options, FALSE, FALSE, 0);
1657+
1658+ PACK_CHECK_BUTTON (vbox_master_password_options,
1659+ checkbtn_use_master_password,
1660+ _("Use master password"));
1661+
1662+ vbox_master_password_suboptions = gtk_vbox_new (FALSE, VSPACING_NARROW);
1663+ gtk_widget_show (vbox_master_password_suboptions);
1664+ gtk_box_pack_start (GTK_BOX (vbox_master_password_options),
1665+ vbox_master_password_suboptions,
1666+ FALSE,
1667+ FALSE,
1668+ 0);
1669+
1670+ PACK_CHECK_BUTTON (vbox_master_password_suboptions,
1671+ checkbtn_auto_unload_master_password,
1672+ _("Automatically unload master password "
1673+ "after session initialization"));
1674+
1675+ hbox1 = gtk_hbox_new (FALSE, 8);
1676+ gtk_widget_show (hbox1);
1677+ gtk_box_pack_start (GTK_BOX (vbox_master_password_suboptions),
1678+ hbox1,
1679+ FALSE,
1680+ FALSE,
1681+ 0);
1682+
1683+ hbox_spc = gtk_hbox_new (FALSE, 0);
1684+ gtk_widget_show (hbox_spc);
1685+ gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0);
1686+ gtk_widget_set_size_request (hbox_spc, 12, -1);
1687+
1688+ label = gtk_label_new (_("Min. password length"));
1689+ gtk_widget_show (label);
1690+ gtk_box_pack_start (
1691+ GTK_BOX (hbox1), label, FALSE, FALSE, 0);
1692+
1693+ spinbtn_encrypted_password_min_length_adj = gtk_adjustment_new (
1694+ 32, 0, 99, 1, 5, 0);
1695+ spinbtn_encrypted_password_min_length = gtk_spin_button_new(
1696+ GTK_ADJUSTMENT (spinbtn_encrypted_password_min_length_adj), 1, 0);
1697+ gtk_widget_show (spinbtn_encrypted_password_min_length);
1698+ gtk_box_pack_start (GTK_BOX (hbox1),
1699+ spinbtn_encrypted_password_min_length,
1700+ FALSE,
1701+ FALSE,
1702+ 0);
1703+ gtk_spin_button_set_numeric (
1704+ GTK_SPIN_BUTTON (spinbtn_encrypted_password_min_length), TRUE);
1705+ gtk_widget_set_size_request (spinbtn_encrypted_password_min_length,
1706+ 64,
1707+ -1);
1708+
1709+ SET_TOGGLE_SENSITIVITY (checkbtn_use_master_password,
1710+ vbox_master_password_suboptions);
1711+
1712+ master_password.checkbtn_use_master_password
1713+ = checkbtn_use_master_password;
1714+ master_password.checkbtn_auto_unload_master_password
1715+ = checkbtn_auto_unload_master_password;
1716+ master_password.spinbtn_encrypted_password_min_length
1717+ = spinbtn_encrypted_password_min_length;
1718+}
1719+#endif /* USE_SSL */
1720+
1721 static void prefs_details_create(void)
1722 {
1723 GtkWidget *vbox1;
1724diff --git a/src/prefs_ui.c b/src/prefs_ui.c
1725index a3a8f74..2ab1d45 100644
1726--- a/src/prefs_ui.c
1727+++ b/src/prefs_ui.c
1728@@ -31,11 +31,13 @@
1729 #include <errno.h>
1730
1731 #include "prefs.h"
1732+#include "prefs_common.h"
1733 #include "prefs_ui.h"
1734 #include "menu.h"
1735 #include "codeconv.h"
1736 #include "utils.h"
1737 #include "gtkutils.h"
1738+#include "masterpassword.h"
1739
1740 typedef enum
1741 {
1742@@ -268,6 +270,65 @@ void prefs_set_data_from_entry(PrefParam *pparam)
1743 }
1744 }
1745
1746+void prefs_set_data_from_epass_entry(PrefParam *pparam)
1747+{
1748+#if USE_SSL
1749+ PrefsUIData *ui_data;
1750+ gchar **str;
1751+ const gchar *entry_str;
1752+
1753+ /* This is where decrypted passwords are encrypted and stored again */
1754+
1755+ /* master_password == NULL for any of the following reasons:
1756+ * - use_master_password is not enabled (we just save without encrypting)
1757+ * - master_password is auto unloaded (prompt for the master password)
1758+ * - undefined reason (refure to store the password)
1759+ */
1760+ if (!prefs_common.use_master_password) {
1761+ /* check if use_master_password was enabled when Sylpheed started */
1762+ if (!master_password_enabled_on_init) {
1763+ prefs_set_data_from_entry(pparam);
1764+ }
1765+ return;
1766+ } else if (master_password == NULL) {
1767+ if (!prefs_common.auto_unload_master_password) {
1768+ debug_print("Master password enabled, but not loaded for no "
1769+ "apparent reason. Not storing\n");
1770+ return;
1771+ } else {
1772+ if (check_master_password_interactively(3) != MP_RC_OK) {
1773+ debug_print("Failed to reload the master password\n");
1774+ return;
1775+ }
1776+ }
1777+ }
1778+
1779+ ui_data = (PrefsUIData *)pparam->ui_data;
1780+ g_return_if_fail(ui_data != NULL);
1781+ g_return_if_fail(*ui_data->widget != NULL);
1782+
1783+ entry_str = gtk_entry_get_text(GTK_ENTRY(*ui_data->widget));
1784+
1785+ switch (pparam->type) {
1786+ case P_STRING:
1787+ str = (gchar **)pparam->data;
1788+ g_free(*str);
1789+ if ((entry_str != NULL) && (!mpes_string_prefix(entry_str))) {
1790+ debug_print("Encrypting GTK password entry\n");
1791+ *str = encrypt_with_master_password(entry_str);
1792+ } else {
1793+ *str = entry_str[0] ? g_strdup(entry_str) : NULL;
1794+ }
1795+ break;
1796+ default:
1797+ g_warning("Invalid PrefType for GtkEntry widget: %d\n",
1798+ pparam->type);
1799+ }
1800+#else
1801+ prefs_set_data_from_entry(pparam);
1802+#endif
1803+}
1804+
1805 void prefs_set_entry(PrefParam *pparam)
1806 {
1807 PrefsUIData *ui_data;
1808diff --git a/src/prefs_ui.h b/src/prefs_ui.h
1809index cfdf353..f7bd903 100644
1810--- a/src/prefs_ui.h
1811+++ b/src/prefs_ui.h
1812@@ -164,6 +164,7 @@ void prefs_set_data_from_dialog (PrefParam *param);
1813 void prefs_set_dialog_to_default(PrefParam *param);
1814
1815 void prefs_set_data_from_entry (PrefParam *pparam);
1816+void prefs_set_data_from_epass_entry(PrefParam *pparam);
1817 void prefs_set_entry (PrefParam *pparam);
1818 void prefs_set_data_from_text (PrefParam *pparam);
1819 void prefs_set_text (PrefParam *pparam);
1820diff --git a/src/send_message.c b/src/send_message.c
1821index a8c2c7a..537c3c8 100644
1822--- a/src/send_message.c
1823+++ b/src/send_message.c
1824@@ -58,6 +58,7 @@
1825 #include "inc.h"
1826 #include "mainwindow.h"
1827 #include "summaryview.h"
1828+#include "masterpassword.h"
1829
1830 #define SMTP_PORT 25
1831 #if USE_SSL
1832@@ -648,13 +649,13 @@ static gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp)
1833
1834 if (ac_prefs->smtp_userid) {
1835 smtp_session->user = g_strdup(ac_prefs->smtp_userid);
1836- if (ac_prefs->smtp_passwd)
1837- smtp_session->pass =
1838- g_strdup(ac_prefs->smtp_passwd);
1839- else if (ac_prefs->tmp_smtp_pass)
1840+ if (ac_prefs->smtp_passwd) {
1841+ smtp_session->pass = decrypt_with_master_password(
1842+ ac_prefs->smtp_passwd);
1843+ } else if (ac_prefs->tmp_smtp_pass) {
1844 smtp_session->pass =
1845 g_strdup(ac_prefs->tmp_smtp_pass);
1846- else {
1847+ } else {
1848 smtp_session->pass =
1849 input_query_password
1850 (ac_prefs->smtp_server,
diff --git a/mail-client/sylpheed/files/sylpheed-3.7.0-signature-box.patch b/mail-client/sylpheed/files/sylpheed-3.7.0-signature-box.patch
deleted file mode 100644
index af8cdaa..0000000
--- a/mail-client/sylpheed/files/sylpheed-3.7.0-signature-box.patch
+++ /dev/null
@@ -1,267 +0,0 @@
1diff -x .git -rupN sylpheed-3.7.0/libsylph/prefs_common.c sylpheed-3.7.0.new/libsylph/prefs_common.c
2--- sylpheed-3.7.0/libsylph/prefs_common.c 2017-11-24 05:17:23.000000000 +0100
3+++ sylpheed-3.7.0.new/libsylph/prefs_common.c 2018-01-31 10:52:57.094885505 +0100
4@@ -406,6 +406,8 @@ static PrefParam param[] = {
5 P_BOOL},
6 {"gpg_signature_popup", "FALSE", &prefs_common.gpg_signature_popup,
7 P_BOOL},
8+ {"gpg_signature_popup_mode", "1", &prefs_common.gpg_signature_popup_mode,
9+ P_INT},
10 {"store_passphrase", "FALSE", &prefs_common.store_passphrase, P_BOOL},
11 {"store_passphrase_timeout", "0",
12 &prefs_common.store_passphrase_timeout, P_INT},
13diff -x .git -rupN sylpheed-3.7.0/libsylph/prefs_common.h sylpheed-3.7.0.new/libsylph/prefs_common.h
14--- sylpheed-3.7.0/libsylph/prefs_common.h 2017-11-24 05:16:18.000000000 +0100
15+++ sylpheed-3.7.0.new/libsylph/prefs_common.h 2018-01-31 10:52:57.052886536 +0100
16@@ -243,6 +243,7 @@ struct _PrefsCommon
17 /* Privacy */
18 gboolean auto_check_signatures;
19 gboolean gpg_signature_popup;
20+ gint gpg_signature_popup_mode;
21 gboolean store_passphrase;
22 gint store_passphrase_timeout;
23 gboolean passphrase_grab;
24diff -x .git -rupN sylpheed-3.7.0/src/prefs_common_dialog.c sylpheed-3.7.0.new/src/prefs_common_dialog.c
25--- sylpheed-3.7.0/src/prefs_common_dialog.c 2014-11-05 08:28:13.000000000 +0100
26+++ sylpheed-3.7.0.new/src/prefs_common_dialog.c 2018-01-31 10:52:57.184883295 +0100
27@@ -205,6 +205,7 @@ static struct JunkMail {
28 static struct Privacy {
29 GtkWidget *checkbtn_auto_check_signatures;
30 GtkWidget *checkbtn_gpg_signature_popup;
31+ GtkWidget *radiobtn_gpg_signature_all;
32 GtkWidget *checkbtn_store_passphrase;
33 GtkWidget *spinbtn_store_passphrase;
34 GtkObject *spinbtn_store_passphrase_adj;
35@@ -314,6 +315,13 @@ static void prefs_common_attach_toolbtn_
36 static void prefs_common_online_mode_set_data_from_radiobtn(PrefParam *pparam);
37 static void prefs_common_online_mode_set_radiobtn (PrefParam *pparam);
38
39+#if USE_GPGME
40+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn(
41+ PrefParam *pparam);
42+static void prefs_common_gpg_signature_popup_mode_set_radiobtn(
43+ PrefParam *pparam);
44+#endif
45+
46 static PrefsUIData ui_data[] = {
47 /* Receive */
48 {"autochk_newmail", &receive.checkbtn_autochk,
49@@ -540,6 +548,9 @@ static PrefsUIData ui_data[] = {
50 prefs_set_data_from_toggle, prefs_set_toggle},
51 {"gpg_signature_popup", &privacy.checkbtn_gpg_signature_popup,
52 prefs_set_data_from_toggle, prefs_set_toggle},
53+ {"gpg_signature_popup_mode", &privacy.radiobtn_gpg_signature_all,
54+ prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn,
55+ prefs_common_gpg_signature_popup_mode_set_radiobtn},
56 {"store_passphrase", &privacy.checkbtn_store_passphrase,
57 prefs_set_data_from_toggle, prefs_set_toggle},
58 {"store_passphrase_timeout", &privacy.spinbtn_store_passphrase,
59@@ -2478,10 +2489,14 @@ static void prefs_privacy_create(void)
60 GtkWidget *vbox2;
61 GtkWidget *vbox3;
62 GtkWidget *hbox1;
63+ GtkWidget *vbox_sign_popup_mode;
64+ GtkWidget *hbox_sign_popup_mode;
65 GtkWidget *hbox_spc;
66 GtkWidget *label;
67 GtkWidget *checkbtn_auto_check_signatures;
68 GtkWidget *checkbtn_gpg_signature_popup;
69+ GtkWidget *radiobtn_gpg_signature_all;
70+ GtkWidget *radiobtn_gpg_signature_bad;
71 GtkWidget *checkbtn_store_passphrase;
72 GtkObject *spinbtn_store_passphrase_adj;
73 GtkWidget *spinbtn_store_passphrase;
74@@ -2505,6 +2520,56 @@ static void prefs_privacy_create(void)
75 PACK_CHECK_BUTTON (vbox2, checkbtn_gpg_signature_popup,
76 _("Show signature check result in a popup window"));
77
78+ vbox_sign_popup_mode = gtk_vbox_new (FALSE, VSPACING_NARROW);
79+ gtk_widget_show (vbox_sign_popup_mode);
80+ gtk_box_pack_start (GTK_BOX (vbox2), vbox_sign_popup_mode, FALSE, FALSE, 0);
81+
82+ hbox_sign_popup_mode = gtk_hbox_new (FALSE, 8);
83+ gtk_widget_show (hbox_sign_popup_mode);
84+ gtk_box_pack_start (GTK_BOX (vbox_sign_popup_mode),
85+ hbox_sign_popup_mode,
86+ FALSE,
87+ FALSE,
88+ 0);
89+
90+ hbox_spc = gtk_hbox_new (FALSE, 0);
91+ gtk_widget_show (hbox_spc);
92+ gtk_box_pack_start (GTK_BOX (hbox_sign_popup_mode),
93+ hbox_spc,
94+ FALSE,
95+ FALSE,
96+ 0);
97+ gtk_widget_set_size_request (hbox_spc, 12, -1);
98+
99+ radiobtn_gpg_signature_all = gtk_radio_button_new_with_label(
100+ NULL,
101+ _("All signatures"));
102+ gtk_widget_show(radiobtn_gpg_signature_all);
103+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode),
104+ radiobtn_gpg_signature_all,
105+ FALSE,
106+ FALSE,
107+ 0);
108+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_all),
109+ MENU_VAL_ID,
110+ GINT_TO_POINTER (1));
111+
112+ radiobtn_gpg_signature_bad = gtk_radio_button_new_with_label_from_widget(
113+ GTK_RADIO_BUTTON (radiobtn_gpg_signature_all),
114+ _("Bad signatures only"));
115+ gtk_widget_show(radiobtn_gpg_signature_bad);
116+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode),
117+ radiobtn_gpg_signature_bad,
118+ FALSE,
119+ FALSE,
120+ 0);
121+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_bad),
122+ MENU_VAL_ID,
123+ GINT_TO_POINTER (0));
124+
125+ SET_TOGGLE_SENSITIVITY (checkbtn_gpg_signature_popup,
126+ hbox_sign_popup_mode);
127+
128 PACK_CHECK_BUTTON (vbox2, checkbtn_store_passphrase,
129 _("Store passphrase in memory temporarily"));
130
131@@ -2572,7 +2637,8 @@ static void prefs_privacy_create(void)
132 = checkbtn_auto_check_signatures;
133 privacy.checkbtn_gpg_signature_popup
134 = checkbtn_gpg_signature_popup;
135- privacy.checkbtn_store_passphrase = checkbtn_store_passphrase;
136+ privacy.radiobtn_gpg_signature_all = radiobtn_gpg_signature_all;
137+ privacy.checkbtn_store_passphrase = checkbtn_store_passphrase;
138 privacy.spinbtn_store_passphrase = spinbtn_store_passphrase;
139 privacy.spinbtn_store_passphrase_adj = spinbtn_store_passphrase_adj;
140 #ifndef G_OS_WIN32
141@@ -4719,6 +4785,60 @@ static void prefs_common_online_mode_set
142 }
143 }
144
145+#if USE_GPGME
146+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn(
147+ PrefParam *pparam)
148+{
149+ PrefsUIData *ui_data;
150+ GtkRadioButton *radiobtn;
151+ GSList *group;
152+
153+ ui_data = (PrefsUIData *)pparam->ui_data;
154+ g_return_if_fail(ui_data != NULL);
155+ g_return_if_fail(*ui_data->widget != NULL);
156+
157+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget);
158+ group = gtk_radio_button_get_group(radiobtn);
159+ while (group != NULL) {
160+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data);
161+
162+ if (gtk_toggle_button_get_active(btn)) {
163+ prefs_common.gpg_signature_popup_mode =
164+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), MENU_VAL_ID));
165+ break;
166+ }
167+ group = group->next;
168+ }
169+}
170+
171+static void prefs_common_gpg_signature_popup_mode_set_radiobtn(
172+ PrefParam *pparam)
173+{
174+ PrefsUIData *ui_data;
175+ GtkRadioButton *radiobtn;
176+ GSList *group;
177+
178+ ui_data = (PrefsUIData *)pparam->ui_data;
179+ g_return_if_fail(ui_data != NULL);
180+ g_return_if_fail(*ui_data->widget != NULL);
181+
182+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget);
183+ group = gtk_radio_button_get_group(radiobtn);
184+ while (group != NULL) {
185+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data);
186+ gint data;
187+
188+ data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn),
189+ MENU_VAL_ID));
190+ if (data == prefs_common.gpg_signature_popup_mode) {
191+ gtk_toggle_button_set_active(btn, TRUE);
192+ break;
193+ }
194+ group = group->next;
195+ }
196+}
197+#endif
198+
199 static void prefs_common_dispitem_clicked(void)
200 {
201 prefs_summary_column_open(FOLDER_ITEM_IS_SENT_FOLDER
202diff -x .git -rupN sylpheed-3.7.0/src/rfc2015.c sylpheed-3.7.0.new/src/rfc2015.c
203--- sylpheed-3.7.0/src/rfc2015.c 2014-03-26 06:55:35.000000000 +0100
204+++ sylpheed-3.7.0.new/src/rfc2015.c 2018-01-31 11:10:44.946662147 +0100
205@@ -210,8 +210,12 @@ static void check_signature(MimeInfo *mi
206 gchar *tmp_file;
207 gint n_exclude_chars = 0;
208
209- if (prefs_common.gpg_signature_popup)
210+ if (prefs_common.gpg_signature_popup &&
211+ prefs_common.gpg_signature_popup_mode == 1)
212+ {
213+ /* FIXME: Perhaps some macro instead of 1 */
214 statuswindow = gpgmegtk_sig_status_create();
215+ }
216
217 err = gpgme_new(&ctx);
218 if (err) {
219@@ -309,8 +313,21 @@ leave:
220 result = _("Error verifying the signature");
221 }
222 debug_print("verification status: %s\n", result);
223- if (prefs_common.gpg_signature_popup)
224+ if (prefs_common.gpg_signature_popup &&
225+ prefs_common.gpg_signature_popup_mode == 1)
226+ {
227+ /* FIXME: Perhaps some macro instead of 1 */
228+ gpgmegtk_sig_status_update(statuswindow, ctx);
229+ } else if (prefs_common.gpg_signature_popup &&
230+ verifyresult->signatures->status != GPG_ERR_NO_DATA &&
231+ verifyresult->signatures->status != GPG_ERR_NO_ERROR)
232+ {
233+ /* prefs_common.gpg_signature_popup_mode == 0 is useless as long as
234+ * there are only two modes (0 and 1)
235+ */
236+ statuswindow = gpgmegtk_sig_status_create();
237 gpgmegtk_sig_status_update(statuswindow, ctx);
238+ }
239
240 g_free (partinfo->sigstatus);
241 partinfo->sigstatus = g_strdup (result);
242@@ -319,8 +336,11 @@ leave:
243 gpgme_data_release(text);
244 if (ctx)
245 gpgme_release(ctx);
246- if (prefs_common.gpg_signature_popup)
247+ if (prefs_common.gpg_signature_popup) {
248+ /* gpgmegtk_sig_status_destroy handles NULL params so no complicated
249+ check is needed */
250 gpgmegtk_sig_status_destroy(statuswindow);
251+ }
252 }
253
254 /*
255@@ -427,7 +447,11 @@ static gpgme_data_t pgp_decrypt(MsgInfo
256 debug_print("verification status: %s\n", result);
257 debug_print("full status: %s\n",
258 msginfo->encinfo->sigstatus_full);
259- if (prefs_common.gpg_signature_popup) {
260+ if (prefs_common.gpg_signature_popup &&
261+ ((prefs_common.gpg_signature_popup_mode == 1) ||
262+ (verifyresult->signatures->status != GPG_ERR_NO_DATA &&
263+ verifyresult->signatures->status != GPG_ERR_NO_ERROR)))
264+ {
265 GpgmegtkSigStatus statuswindow;
266 statuswindow = gpgmegtk_sig_status_create();
267 gpgmegtk_sig_status_update(statuswindow, ctx);
diff --git a/mail-client/sylpheed/files/sylpheed-3.7.0-version.patch b/mail-client/sylpheed/files/sylpheed-3.7.0-version.patch
deleted file mode 100644
index 65d560f..0000000
--- a/mail-client/sylpheed/files/sylpheed-3.7.0-version.patch
+++ /dev/null
@@ -1,26 +0,0 @@
1diff --git a/configure b/configure
2index 6984cce..cc2b770 100755
3--- a/configure
4+++ b/configure
5@@ -2581,7 +2581,7 @@ MINOR_VERSION=7
6 MICRO_VERSION=0
7 INTERFACE_AGE=0
8 BINARY_AGE=0
9-EXTRA_VERSION=
10+EXTRA_VERSION=-sgs
11 BUILD_REVISION=1185
12 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
13
14diff --git a/configure.ac b/configure.ac
15index 090177d..ae65343 100644
16--- a/configure.ac
17+++ b/configure.ac
18@@ -9,7 +9,7 @@ MINOR_VERSION=7
19 MICRO_VERSION=0
20 INTERFACE_AGE=0
21 BINARY_AGE=0
22-EXTRA_VERSION=
23+EXTRA_VERSION=-sgs
24 BUILD_REVISION=1185
25 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
26
diff --git a/mail-client/sylpheed/metadata.xml b/mail-client/sylpheed/metadata.xml
deleted file mode 100644
index 2facbf5..0000000
--- a/mail-client/sylpheed/metadata.xml
+++ /dev/null
@@ -1,10 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
3<pkgmetadata>
4 <maintainer type="person">
5 <email>blackmore@pichove.org</email>
6 </maintainer>
7 <use>
8 <flag name="oniguruma">Use <pkg>dev-libs/oniguruma</pkg> for regular expression</flag>
9 </use>
10</pkgmetadata>
diff --git a/mail-client/sylpheed/sylpheed-3.7.0-r1.ebuild b/mail-client/sylpheed/sylpheed-3.7.0-r1.ebuild
deleted file mode 100644
index f94d992..0000000
--- a/mail-client/sylpheed/sylpheed-3.7.0-r1.ebuild
+++ /dev/null
@@ -1,76 +0,0 @@
1# Copyright 2018 Simeon Simeonov
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI="6"
5
6inherit eutils
7
8DESCRIPTION="A lightweight email client and newsreader (patched by sgs)"
9HOMEPAGE="http://sylpheed.sraoss.jp/"
10SRC_URI="http://${PN}.sraoss.jp/${PN}/v${PV%.*}/${P}.tar.bz2"
11
12LICENSE="GPL-2 LGPL-2.1"
13SLOT="0"
14KEYWORDS="amd64 x86"
15IUSE="crypt ipv6 ldap libressl nls oniguruma pda spell ssl xface"
16
17CDEPEND="net-libs/liblockfile
18 x11-libs/gtk+:2
19 crypt? ( app-crypt/gpgme )
20 ldap? ( net-nds/openldap )
21 nls? ( sys-devel/gettext )
22 oniguruma? ( dev-libs/oniguruma:= )
23 pda? ( app-pda/jpilot )
24 spell? (
25 app-text/gtkspell:2
26 dev-libs/dbus-glib
27 )
28 ssl? (
29 !libressl? ( dev-libs/openssl:0 )
30 libressl? ( dev-libs/libressl )
31 )"
32RDEPEND="${CDEPEND}
33 app-misc/mime-types
34 net-misc/curl"
35DEPEND="${CDEPEND}
36 virtual/pkgconfig
37 xface? ( media-libs/compface )"
38
39PATCHES=(
40 "${FILESDIR}/${P}-signature-box.patch"
41 "${FILESDIR}/${P}-PGP-signature-fix.patch"
42 "${FILESDIR}/${P}-master-password.patch"
43 "${FILESDIR}/${P}-version.patch"
44)
45
46DOCS="AUTHORS ChangeLog* NEW* PLUGIN* README* TODO*"
47
48src_configure() {
49 local htmldir="${EPREFIX}"/usr/share/doc/${PF}/html
50 econf \
51 $(use_enable crypt gpgme) \
52 $(use_enable ipv6) \
53 $(use_enable ldap) \
54 $(use_enable oniguruma) \
55 $(use_enable pda jpilot) \
56 $(use_enable spell gtkspell) \
57 $(use_enable ssl) \
58 $(use_enable xface compface) \
59 --with-plugindir="${EPREFIX}"/usr/$(get_libdir)/${PN}/plugins \
60 --with-manualdir="${htmldir}"/manual \
61 --with-faqdir="${htmldir}"/faq \
62 --disable-updatecheckplugin \
63 --disable-updatecheck
64}
65
66src_install() {
67 default
68
69 doicon *.png
70 domenu *.desktop
71
72 cd plugin/attachment_tool
73 emake DESTDIR="${D}" install-plugin
74 docinto plugin/attachment_tool
75 dodoc README
76}
diff --git a/metadata/layout.conf b/metadata/layout.conf
deleted file mode 100644
index 94f37c2..0000000
--- a/metadata/layout.conf
+++ /dev/null
@@ -1,9 +0,0 @@
1masters = gentoo
2
3manifest-hashes = BLAKE2B SHA512
4manifest-required-hashes = BLAKE2B
5
6thin-manifests = false
7
8sign-commits = true
9sign-manifests = false
diff --git a/sec-keys/openpgp-keys-simeonsimeonov/Manifest b/sec-keys/openpgp-keys-simeonsimeonov/Manifest
index cb9c2a1..5078a88 100644
--- a/sec-keys/openpgp-keys-simeonsimeonov/Manifest
+++ b/sec-keys/openpgp-keys-simeonsimeonov/Manifest
@@ -1,2 +1,2 @@
1DIST sgs.gpg 4000 BLAKE2B 5c1cf202593d82efe6347bae11e11d35f6366572855f1ed1de6ae79ec9598aa74f1232599a1a9deff0fad48a6a473b316f4b73c91f3d5364d25be882caef8238 SHA512 5f6463ce735d6c486925da7bb6e0532bd4c622fb9668160b7867819ddefd00459dae30eb28b10dc4a7e391cda00ec6dce66d5b93a063349cd0f4bd312bf8c28f 1DIST sgs.gpg 4000 BLAKE2B 5c1cf202593d82efe6347bae11e11d35f6366572855f1ed1de6ae79ec9598aa74f1232599a1a9deff0fad48a6a473b316f4b73c91f3d5364d25be882caef8238 SHA512 5f6463ce735d6c486925da7bb6e0532bd4c622fb9668160b7867819ddefd00459dae30eb28b10dc4a7e391cda00ec6dce66d5b93a063349cd0f4bd312bf8c28f
2EBUILD openpgp-keys-simeonsimeonov-20220409.ebuild 531 BLAKE2B 627328cb3a7d377de1b7109aff35cae1f964eef1ee9f7acfd5360cd7c8509db8f82d605c82706dfef086968d77016fa4ac2ca0aa54aa4f601a7644b4e2163cff SHA512 b74cc0158cc3c351c4c2ca35ecfa380d1ba1dc6af19e7c6e5653bf6a27aecac8e4e790499287f9699eea96ed73944c48cb6c37b113fd99489d28945ff18db61e 2EBUILD openpgp-keys-simeonsimeonov-20220409.ebuild 525 BLAKE2B 9e230be9b62c2e9cda9d931e08f9864849ffc63ff5d339656b16ab249cdac08d3ef2cbaaa35847bf770c6a596f6d1f12644bc570f7b41e10619d7bb93bb6bf1a SHA512 b6ec0aaa60b71e844eba0a57bb3b2bb931cce75b981443a3ad84f3a46972f202dc16bb998bb02764e2da029f5b7d618ceacdab6561e9761b5a2802f34690c388
diff --git a/sec-keys/openpgp-keys-simeonsimeonov/openpgp-keys-simeonsimeonov-20220409.ebuild b/sec-keys/openpgp-keys-simeonsimeonov/openpgp-keys-simeonsimeonov-20220409.ebuild
index fe75635..46ae077 100644
--- a/sec-keys/openpgp-keys-simeonsimeonov/openpgp-keys-simeonsimeonov-20220409.ebuild
+++ b/sec-keys/openpgp-keys-simeonsimeonov/openpgp-keys-simeonsimeonov-20220409.ebuild
@@ -7,11 +7,11 @@ DESCRIPTION="OpenPGP keys used by Simeon Simeonov"
7HOMEPAGE="https://github.com/blackm0re" 7HOMEPAGE="https://github.com/blackm0re"
8SRC_URI="https://simeon.simeonov.no/sgs.gpg" 8SRC_URI="https://simeon.simeonov.no/sgs.gpg"
9 9
10S=${WORKDIR}
11
10LICENSE="public-domain" 12LICENSE="public-domain"
11SLOT="0" 13SLOT="0"
12KEYWORDS="~alpha amd64 arm arm64 ~hppa ~ia64 ~m68k ~mips ppc ppc64 ~riscv ~s390 sparc x86" 14KEYWORDS="alpha amd64 arm arm64 hppa ia64 m68k ~mips ppc ppc64 riscv s390 sparc x86"
13
14S=${WORKDIR}
15 15
16src_install() { 16src_install() {
17 local files=( ${A} ) 17 local files=( ${A} )
diff --git a/www-servers/ngus/Manifest b/www-servers/ngus/Manifest
deleted file mode 100644
index af4ccb8..0000000
--- a/www-servers/ngus/Manifest
+++ /dev/null
@@ -1,3 +0,0 @@
1DIST ngus-1.0.tar.gz 29302 BLAKE2B f961644a0cf36d33c607d9b2b4d3b63e3d4807ef7bf656bc5bbe605607130ce41d1a892a38d61780ab293f8ec753572b01caff0d014ec4217783f074a2317f69 SHA512 874f9b8cd3edf19af6fa82e292ea6ebe84af70ac99a2989694d8e0ed18336fb6bc1c3de1a309aedb59e089294bf211401736ef2b829e59fac7f0d2cd254bed73
2EBUILD ngus-1.0.ebuild 730 BLAKE2B 4fe6731b298f65558f6108253ff3774c97a50e5a5349e9af45f46bfd5bd5dca3eb88b6fc06d2901d9cd2ca6dfaf0b86ee29a895b125f9e35beb80bc1a2f5b363 SHA512 ed07c2b80b032ff31435cce0c4b7dd1db442ff625871e5949043335615e399434e5a9a663f69067aa2049d6a20dff4e92a015632e31f42b271e63d74732a90a3
3MISC metadata.xml 392 BLAKE2B cfd3ab10c8740ba18245a2afac812a9110e172afe546da24a3862b4ce53d25df8e27190c42554f6a1e7a6e028c76959a2823c56f47748c9221e1510624f215d3 SHA512 f0d9c4ab885fde2662687a0d3d7b88d7564db91344b9e09141ff4d6d1bd5160fb9810305275c8a190997f3f90841e7e34b35c0574faaf54fadf7dc8679a37d57
diff --git a/www-servers/ngus/metadata.xml b/www-servers/ngus/metadata.xml
deleted file mode 100644
index 29dfc1c..0000000
--- a/www-servers/ngus/metadata.xml
+++ /dev/null
@@ -1,13 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
3<pkgmetadata>
4 <maintainer type="project">
5 <email>sgs@pichove.org</email>
6 <name>Simeon Simeonov</name>
7 </maintainer>
8 <stabilize-allarches/>
9 <upstream>
10 <remote-id type="pypi">ngus</remote-id>
11 <remote-id type="github">blackm0re/ngus</remote-id>
12 </upstream>
13</pkgmetadata>
diff --git a/www-servers/ngus/ngus-1.0.ebuild b/www-servers/ngus/ngus-1.0.ebuild
deleted file mode 100644
index 7c4f0cc..0000000
--- a/www-servers/ngus/ngus-1.0.ebuild
+++ /dev/null
@@ -1,31 +0,0 @@
1# Copyright 1999-2022 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=7
5
6DISTUTILS_USE_SETUPTOOLS=rdepend
7PYTHON_COMPAT=( python3_{8..11} )
8DISTUTILS_USE_SETUPTOOLS=rdepend
9
10inherit distutils-r1
11
12DESCRIPTION="A minimalist HTTP server written in pure Python and intended for receiving file uploads"
13HOMEPAGE="https://github.com/blackm0re/ngus"
14MY_PN="ngus"
15MY_P="${MY_PN}-${PV}"
16SRC_URI="mirror://pypi/${MY_P:0:1}/${MY_PN}/${MY_P}.tar.gz"
17KEYWORDS="amd64 arm arm64 ppc ppc64 x86 amd64-linux x86-linux"
18S="${WORKDIR}/${MY_P}"
19
20RESTRICT="test"
21
22LICENSE="GPL-3+"
23SLOT="0"
24
25# DEPEND="
26# dev-python/setuptools[${PYTHON_USEDEP}]
27# "
28
29python_install_all() {
30 distutils-r1_python_install_all
31}
diff --git a/x11-misc/i3lock-extended/Manifest b/x11-misc/i3lock-extended/Manifest
index 2ecfd67..ae446fe 100644
--- a/x11-misc/i3lock-extended/Manifest
+++ b/x11-misc/i3lock-extended/Manifest
@@ -1,9 +1,4 @@
1DIST i3lock-extended-2.2.13a.tar.xz 154048 BLAKE2B 457993cededd810091840142c350423848a6a6a3cd558a89a1b66a5af221e85928ecaba9e6d9106a592e09b2a5229a10e9ba912dcf1e5d248bf040c26f4f63e4 SHA512 281d4efb08660ddf195ad59b0e90c1fb2044a19fb9e5900fcab0cd5229c1ace6c2dbc04e4492c5730404c4534463776f6b7a1a7e31d7b48606864e917d3e7a66
2DIST i3lock-extended-2.2.14.tar.xz 42296 BLAKE2B 5a2f52fc23d8d9f803ef82a74b11a9b9a2821bb28171843a52c60acb14bfe0af29bad42a7a27b2d4183b02b2c1df45ac2025d6513adb32f19b31fbf1cdb8ce04 SHA512 576bc73544477b931e91cd1dc5f4bf2d75b79649b62fde42922e81cdd660af0d41f1dd50f7f568d17a53edbf1a063a8405c4c9c3449fd363543798e8d39183aa
3DIST i3lock-extended-2.2.14.tar.xz.asc 858 BLAKE2B aada92ade52e9ba5ce8d141d2c33d201f8356d9bf76af169cae5197ad055d517078123e315ccebc063e7b3da1022e78b1176b00b9ed4a9edc205fa4035f61787 SHA512 603ce859c4c9bdb440076552c1996a88423b7edc7caef58540beb25cc843587b103a0294d23b73e3547a38803d864bd616dc031238a6cd0040658316487ac80b
4DIST i3lock-extended-2.2.15.tar.xz 42724 BLAKE2B 73880b89c55baf7b78dff45be1fd93c8070400e1ed05e596a5a6edd2aeb190aa28b3351215f8f911287efbc326ea37906eb0b1fbe55c8550b5a85d56b406e381 SHA512 3cd4030bbccc04b86e38a6616f8c6d30f9a4eaa24b64aa1b344a76acfc5560afa5ab79847c7114aa35969c80c6af44fa7f58761029dc490ea989898d5001e298 1DIST i3lock-extended-2.2.15.tar.xz 42724 BLAKE2B 73880b89c55baf7b78dff45be1fd93c8070400e1ed05e596a5a6edd2aeb190aa28b3351215f8f911287efbc326ea37906eb0b1fbe55c8550b5a85d56b406e381 SHA512 3cd4030bbccc04b86e38a6616f8c6d30f9a4eaa24b64aa1b344a76acfc5560afa5ab79847c7114aa35969c80c6af44fa7f58761029dc490ea989898d5001e298
5DIST i3lock-extended-2.2.15.tar.xz.asc 858 BLAKE2B 6ffa2cd98a9708d372ee293860bcb09959457c890d72e4ad50f0af849560ddf61396575da9059d033be6568c5330512891b1959f295422c2663d46ea940898cd SHA512 cc3f58e543878267f7b6a58e6ff8a9626881327b7b7deaf69b7aadbee45f0ecbae8d19ece77b5b4f9e7d290e3a75ef3fecfcace2d050a321b60c2fe41e96e483 2DIST i3lock-extended-2.2.15.tar.xz.asc 858 BLAKE2B 6ffa2cd98a9708d372ee293860bcb09959457c890d72e4ad50f0af849560ddf61396575da9059d033be6568c5330512891b1959f295422c2663d46ea940898cd SHA512 cc3f58e543878267f7b6a58e6ff8a9626881327b7b7deaf69b7aadbee45f0ecbae8d19ece77b5b4f9e7d290e3a75ef3fecfcace2d050a321b60c2fe41e96e483
6EBUILD i3lock-extended-2.2.13a.ebuild 811 BLAKE2B 0e84bdae98489dd04fbf5e029f517b7aa66c55882ad47c3769dc6a1df4430f1f3b90ff857fa5428e403278536fc87e2b657a6ea490111e9f594eac98c48aa18f SHA512 ab558600f95af7c06cc5a887e3bb9c8012c635f2f09dec99437d73668570c2b379485dc7fd55f9fa91d10109ffd6b16cb376205a1621cdc2a4d9bbc5cbf2d9fb 3EBUILD i3lock-extended-2.2.15.ebuild 980 BLAKE2B 52fb78df2af318045cc68830773e1d87737695a4b79b72ee0059e15a73f20381834cf867fcc424ee01606b7a7b0c1254b7de915e3f2919bbf93522c3da49c43d SHA512 45febb7e74d5f22ab01e51369c8ed13932d94190643043fb86d152198fa04f003b6ca5574bf88a31657f23f74141d9b62a952e038fd47325ef4b9afa2db9a502
7EBUILD i3lock-extended-2.2.14.ebuild 987 BLAKE2B 1d6d159e9f70ccb960f77f72c0ab98d582d33b9da32ae28b82b3217e975d28e0b862a26b944b95f0a8ea4c941263446910e296c63c395a6f48511885bacb19af SHA512 3d3b58bef58dbe9881348ee8d822eef549c5d6e74fc3d06d4926998f206ddb23c4fe535f72ec47d51c43f814e69f354b272bc8b58ad0d6c97c2751ca1370127f
8EBUILD i3lock-extended-2.2.15.ebuild 988 BLAKE2B 5b33147edb4bd214e07a2b658f48ec8291069de2bf1fef99ce29d7e1ed18d8e3b41d0837305eb0495d7f2b944075afc5f46a00cfa379ae43916ac3897389c1f1 SHA512 f3b731ab988c4d400b32441d34df13484ab92cb8674885608959049c79019470c2df0e1154156371fcaea6e7d7ec3d5bde207731529e5154f0c2acb564dad08e
9MISC metadata.xml 355 BLAKE2B bba87c29271fc80bfbf932888a2415e122f58cac4f91e057af50cb73209c84b0f2783117d76505f8a13b8475ec7c6dc2816e78d4cadfed231b60860739de70ca SHA512 b6587d7c1f830f8b6f2658a31a608875d1e1b2aa792137e87fe87228a8e89ccc0bd8ecc33269a628e72b24e21773be1ca90aa604d0ac3e280e38a50240a869cf 4MISC metadata.xml 355 BLAKE2B bba87c29271fc80bfbf932888a2415e122f58cac4f91e057af50cb73209c84b0f2783117d76505f8a13b8475ec7c6dc2816e78d4cadfed231b60860739de70ca SHA512 b6587d7c1f830f8b6f2658a31a608875d1e1b2aa792137e87fe87228a8e89ccc0bd8ecc33269a628e72b24e21773be1ca90aa604d0ac3e280e38a50240a869cf
diff --git a/x11-misc/i3lock-extended/i3lock-extended-2.2.13a.ebuild b/x11-misc/i3lock-extended/i3lock-extended-2.2.13a.ebuild
deleted file mode 100644
index e3f728e..0000000
--- a/x11-misc/i3lock-extended/i3lock-extended-2.2.13a.ebuild
+++ /dev/null
@@ -1,44 +0,0 @@
1# Copyright 1999-2020 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=7
5inherit toolchain-funcs
6
7DESCRIPTION="i3lock-extended - i3lock fork with additional functionality"
8HOMEPAGE="https://simeon.simeonov.no"
9SRC_URI="https://simeon.simeonov.no/programs/i3lock_extended/downloads/${P}.tar.xz"
10
11LICENSE="GPL-3+ BSD"
12SLOT="0"
13KEYWORDS="amd64 ~arm ~arm64 ~ppc64 ~x86"
14
15RDEPEND="
16 >=x11-libs/libxkbcommon-0.5.0[X]
17 dev-libs/libev
18 sys-libs/pam
19 x11-libs/cairo[X,xcb(+)]
20 x11-libs/libxcb[xkb]
21 x11-libs/xcb-util
22 x11-libs/xcb-util-xrm
23"
24DEPEND="
25 ${RDEPEND}
26 virtual/pkgconfig
27"
28DOCS=( CHANGELOG README.md )
29
30src_prepare() {
31 default
32
33 sed -i -e 's:login:system-auth:g' pam/${PN} || die
34}
35
36src_configure() {
37 tc-export CC
38 default
39}
40
41src_install() {
42 default
43 doman ${PN}.1
44}
diff --git a/x11-misc/i3lock-extended/i3lock-extended-2.2.14.ebuild b/x11-misc/i3lock-extended/i3lock-extended-2.2.14.ebuild
deleted file mode 100644
index 5eb179f..0000000
--- a/x11-misc/i3lock-extended/i3lock-extended-2.2.14.ebuild
+++ /dev/null
@@ -1,40 +0,0 @@
1# Copyright 1999-2023 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=8
5
6inherit meson verify-sig
7
8DESCRIPTION="i3lock-extended - i3lock fork with additional functionality"
9HOMEPAGE="https://github.com/blackm0re/i3lock-extended"
10SRC_URI="
11 https://github.com/blackm0re/i3lock-extended/releases/download/${PV}/${P}.tar.xz
12 verify-sig? ( https://github.com/blackm0re/i3lock-extended/releases/download/${PV}/${P}.tar.xz.asc )
13"
14
15LICENSE="GPL-3+ BSD"
16SLOT="0"
17KEYWORDS="amd64 ~arm ~arm64 ~ppc64 ~riscv x86"
18
19RDEPEND="
20 dev-libs/libev
21 sys-libs/pam
22 x11-libs/cairo[X,xcb(+)]
23 x11-libs/libxcb
24 x11-libs/libxkbcommon[X]
25 x11-libs/xcb-util
26 x11-libs/xcb-util-image
27 x11-libs/xcb-util-xrm"
28DEPEND="${RDEPEND}"
29BDEPEND="
30 virtual/pkgconfig
31 verify-sig? ( sec-keys/openpgp-keys-simeonsimeonov )
32"
33
34VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/simeonsimeonov.asc
35
36src_prepare() {
37 default
38
39 sed -i -e 's:login:system-auth:g' pam/${PN} || die
40}
diff --git a/x11-misc/i3lock-extended/i3lock-extended-2.2.15.ebuild b/x11-misc/i3lock-extended/i3lock-extended-2.2.15.ebuild
index 9d7bfe2..3e06fa8 100644
--- a/x11-misc/i3lock-extended/i3lock-extended-2.2.15.ebuild
+++ b/x11-misc/i3lock-extended/i3lock-extended-2.2.15.ebuild
@@ -31,7 +31,7 @@ BDEPEND="
31 verify-sig? ( sec-keys/openpgp-keys-simeonsimeonov ) 31 verify-sig? ( sec-keys/openpgp-keys-simeonsimeonov )
32" 32"
33 33
34VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/simeonsimeonov.asc 34VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/simeonsimeonov.asc
35 35
36src_prepare() { 36src_prepare() {
37 default 37 default
diff --git a/x11-terms/terminator/Manifest b/x11-terms/terminator/Manifest
deleted file mode 100644
index 6800b52..0000000
--- a/x11-terms/terminator/Manifest
+++ /dev/null
@@ -1,10 +0,0 @@
1AUX terminator-1.91-desktop.patch 355 BLAKE2B c8c1488779116eee3e1c41440e04409381c6b66389e1ca8e82ebd961d970b84543133b098166008310fc035a591eb26e93c10fcc1bbb02ebc66752bb26145cbd SHA512 8944e27a6ab20f7f74c7dac9e2e93877a156ee5082c617dc7de84298bec303c7a0cf57fc1dd3cad4ae76ce2e15c50aacb8fe5587fe977727c10a6a3c48cca880
2AUX terminator-1.91-without-icon-cache.patch 567 BLAKE2B 72ebaa0eaee27491d0f93982b84c56009fd327108cb8e0b3a8e88a95a781dc324f11f6f8290e7082919a7753a1419777e7e028bafcc1437cc62e51eda465f3f3 SHA512 36f5f08f1724b6cd4bd43d37a27e5161feec66f03a9dd1de973e50eb557f5fe171329903a8074343b5fc816bcaa0fec2642060001c727c35792680a8007a7c50
3AUX terminator-1.92-single-tab.patch 1048 BLAKE2B 079e819356ed0570acb0cbae6ca8aa8f604b83f3cd976395aa53f3ea1937e8950060f3670138cecd33433d553d47f48035a7e63b1de4f8fb8f27043e4139fae3 SHA512 bc0b177de91f4ffc6f34c5eccb468a4c32e52acb25536f10cea5216890352a3e4af8386bae5d9bc6817e58d1f1259f395747374936e618e56515be3d957d231d
4DIST terminator-2.1.2.tar.gz 1039160 BLAKE2B 2ca98c1caeadfffd2c7f737947b216a1db438a426a95cb9871504b827c3e85384df157d96a47c5f3d2cdc33e5e6b0e35cb8019b7a4bbc92fe59603379610e2e0 SHA512 070bf49979dd93e694b536877bac2d6ad274a1e961ef74df90348da6520fc411e7d0a73cd0d0c8fa2989c94ea75758fd15c09357f4f2ca8e7f6a4ab6c1236748
5DIST terminator-2.1.2.tar.gz.asc 866 BLAKE2B 3d998365e039935742635f049513f5c43cd45c067d7d31914bf8284b79af3daccdbd524b35c37ca419b96083e5986d365a6e7654b847d460c85179ff9da1886e SHA512 422b81676c6a53f736d39b5ff771d87e18053c188e0e97807f199080523430ede1e6d90d3f001fb3654b4b6bc90da9cc5f874a2639fa87ef8848868caa71342e
6DIST terminator-2.1.3.tar.gz 1046741 BLAKE2B 39ec3ca049d5fb532e420ab48a609bd254d626e1949b7c659c85a451054ec4c6f59c89940b2a618131933090fa70aa4441ab88abf2a302d84f6c9b2c61bb22c6 SHA512 79bdf22f068e14cd63e527f3671cd7b06f2685ef12594870aee170f858a8eabb21e63e7a9ef41fcc664e9b34e8f24b08e998de686be7bf47e0d4315c1224bb8e
7DIST terminator-2.1.3.tar.gz.asc 866 BLAKE2B 02ab0c100924da02f76bbf557b99743dbd03af727f67e4824afb099e6760b1acac468c31d4f083efa698738d697d834707fbed105eb0caf7db96438bc4818feb SHA512 48e450d5aa3bef23ec3381c3e24e5ab60b35aa76758cc9d1894a68f04d0f9784fee79e03500cff1473b5f7468816604a0e62b7ee709273d25167e54a8be08279
8EBUILD terminator-2.1.2-r1.ebuild 1707 BLAKE2B f26e276c47bd3a94979cdd71adda607915465e8c4fbb3454fc14ad1259414ba49252f368d331167ff0705efba7daf71e36ff931c01d1e609c4347442a771d1bb SHA512 239cd1e913609fef483483f3e8a37b246031a9bd24eda542ab1f7f214427a466d0dc2746b03216e10631607f3b13223049f1e6685280e1fbca4b7f0c7d868f19
9EBUILD terminator-2.1.3-r1.ebuild 1684 BLAKE2B 1ae50c0d68d9d267f4203e16fd7bc39485ad0ed052ebfbfe32cc9a8878fdaa494a90cb0ea4efb4a46295610e531ba4fe4fc09d29bad517d41fdf3f5d13849749 SHA512 bc924fb0c574cbcdb89494450f2f5dd2e3b7de65bcf8b93a6b7ac9b33c553a7a442556ebae65f156e8a3b5a2ce1ae15991ccf2d417219c92fdbcbd88fb7025b7
10MISC metadata.xml 859 BLAKE2B 315ffd413597c4b0b5e847e4869c330be0f9092f3c272dcb362fdce1b89423a65a887a6e63b2770253aa2125cc3e6368adb058491f0fd420594d315047279c83 SHA512 2a56b28dcc37b9697c778de2482b7e191fc83907f1e8930a015b3e60a8a672faeec18a6602565ad60c0883f7857b956a1abccf06800f4a4276f72cd643449030
diff --git a/x11-terms/terminator/files/terminator-1.91-desktop.patch b/x11-terms/terminator/files/terminator-1.91-desktop.patch
deleted file mode 100644
index 4cbbfa4..0000000
--- a/x11-terms/terminator/files/terminator-1.91-desktop.patch
+++ /dev/null
@@ -1,12 +0,0 @@
1--- a/data/terminator.desktop.in
2+++ b/data/terminator.desktop.in
3@@ -9,8 +9,8 @@
4 StartupNotify=true
5 X-Ubuntu-Gettext-Domain=terminator
6 X-Ayatana-Desktop-Shortcuts=NewWindow;
7 Keywords=terminal;shell;prompt;command;commandline;
8-[NewWindow Shortcut Group]
9+[X-NewWindow Shortcut Group]
10 Name=Open a New Window
11 Exec=terminator
12 TargetEnvironment=Unity
diff --git a/x11-terms/terminator/files/terminator-1.91-without-icon-cache.patch b/x11-terms/terminator/files/terminator-1.91-without-icon-cache.patch
deleted file mode 100644
index c924de3..0000000
--- a/x11-terms/terminator/files/terminator-1.91-without-icon-cache.patch
+++ /dev/null
@@ -1,16 +0,0 @@
1Without this patch, terminator's build script runs gtk-update-icon-cache which
2causes terminator package to claim /usr/share/icons/hicolor/icon-theme.cache as
3its own. To avoid that, gtk-update-icon-cache is run later, as part of
4xdg_pkg_postinst in the ebuild.
5
6--- a/setup.py 2009-08-12 22:22:53.000000000 -0400
7+++ b/setup.py 2009-08-12 22:22:57.000000000 -0400
8@@ -25,7 +25,7 @@
9
10 def __init__ (self, *args):
11 self.without_gettext = False
12- self.without_icon_cache = False
13+ self.without_icon_cache = True
14 Distribution.__init__(self, *args)
15
16
diff --git a/x11-terms/terminator/files/terminator-1.92-single-tab.patch b/x11-terms/terminator/files/terminator-1.92-single-tab.patch
deleted file mode 100644
index 0535e00..0000000
--- a/x11-terms/terminator/files/terminator-1.92-single-tab.patch
+++ /dev/null
@@ -1,24 +0,0 @@
1diff -rupN terminator-2.1.2.old/terminatorlib/terminal.py terminator-2.1.2/terminatorlib/terminal.py
2--- terminator-2.1.2.old/terminatorlib/terminal.py 2022-10-19 16:22:21.000000000 +0200
3+++ terminator-2.1.2/terminatorlib/terminal.py 2023-01-13 08:16:57.109231720 +0100
4@@ -910,6 +910,20 @@ class Terminal(Gtk.VBox):
5 # FIXME: Does keybindings really want to live in Terminator()?
6 mapping = self.terminator.keybindings.lookup(event)
7
8+ # Just propagate tab-swictch events if there is only one tab
9+ if (
10+ mapping and (
11+ mapping.startswith('switch_to_tab')
12+ or mapping in ('next_tab', 'prev_tab')
13+ )
14+ ):
15+ window = self.get_toplevel()
16+ child = window.get_children()[0]
17+ if isinstance(child, Terminal):
18+ # not a Notebook instance => a single tab is used
19+ # .get_n_pages() can not be used
20+ return False
21+
22 if mapping == "hide_window":
23 return False
24
diff --git a/x11-terms/terminator/metadata.xml b/x11-terms/terminator/metadata.xml
deleted file mode 100644
index dbf43bc..0000000
--- a/x11-terms/terminator/metadata.xml
+++ /dev/null
@@ -1,22 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
3<pkgmetadata>
4 <maintainer type="person" proxied="yes">
5 <email>alexey+gentoo@asokolov.org</email>
6 <name>Alexey Sokolov</name>
7 </maintainer>
8 <maintainer type="project" proxied="proxy">
9 <email>proxy-maint@gentoo.org</email>
10 <name>Proxy Maintainers</name>
11 </maintainer>
12 <longdescription>
13 Much of the behaviour of Terminator is based on GNOME Terminal, and
14 we are adding more features from that as time goes by, but we also
15 want to extend out in different directions with useful features for
16 sysadmins and other users. If you have any suggestions, please file
17 wishlist bugs! (see below for the address)
18 </longdescription>
19 <upstream>
20 <remote-id type="github">gnome-terminator/terminator</remote-id>
21 </upstream>
22</pkgmetadata>
diff --git a/x11-terms/terminator/terminator-2.1.2-r1.ebuild b/x11-terms/terminator/terminator-2.1.2-r1.ebuild
deleted file mode 100644
index 35f8b0f..0000000
--- a/x11-terms/terminator/terminator-2.1.2-r1.ebuild
+++ /dev/null
@@ -1,66 +0,0 @@
1# Copyright 1999-2023 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=8
5
6DISTUTILS_USE_PEP517=setuptools
7PYTHON_COMPAT=( python3_{8..11} )
8inherit distutils-r1 optfeature verify-sig virtualx xdg
9
10DESCRIPTION="Multiple GNOME terminals in one window"
11HOMEPAGE="https://github.com/gnome-terminator/terminator"
12SRC_URI="
13 https://github.com/gnome-terminator/terminator/releases/download/v${PV}/${P}.tar.gz
14 verify-sig? ( https://github.com/gnome-terminator/terminator/releases/download/v${PV}/${P}.tar.gz.asc )
15"
16
17LICENSE="GPL-2"
18SLOT="0"
19KEYWORDS="~amd64 ~ppc ~riscv ~x86"
20IUSE="test"
21
22RDEPEND="
23 dev-libs/glib:2
24 dev-python/configobj[${PYTHON_USEDEP}]
25 dev-python/psutil[${PYTHON_USEDEP}]
26 dev-python/pycairo[${PYTHON_USEDEP}]
27 dev-python/pygobject:3[${PYTHON_USEDEP}]
28 gnome-base/gsettings-desktop-schemas[introspection]
29 x11-libs/gtk+:3
30 x11-libs/vte:2.91[introspection]
31"
32BDEPEND="
33 dev-util/intltool
34 sys-devel/gettext
35 test? (
36 dev-python/dbus-python[${PYTHON_USEDEP}]
37 x11-libs/libnotify[introspection]
38 )
39 verify-sig? ( sec-keys/openpgp-keys-terminator )
40"
41distutils_enable_tests pytest
42
43VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/terminator.asc
44
45PATCHES=(
46 "${FILESDIR}"/terminator-1.91-desktop.patch
47 "${FILESDIR}"/terminator-1.92-single-tab.patch
48)
49
50src_prepare() {
51 xdg_environment_reset
52 sed -i -e '/pytest-runner/d' setup.py || die
53 distutils-r1_src_prepare
54}
55
56src_test() {
57 virtx distutils-r1_src_test
58}
59
60pkg_postinst() {
61 xdg_pkg_postinst
62
63 optfeature "D-Bus" dev-python/dbus-python
64 optfeature "desktop notifications" "x11-libs/libnotify[introspection]"
65 optfeature "global keyboard shortcuts" "dev-libs/keybinder:3[introspection]"
66}
diff --git a/x11-terms/terminator/terminator-2.1.3-r1.ebuild b/x11-terms/terminator/terminator-2.1.3-r1.ebuild
deleted file mode 100644
index aefb275..0000000
--- a/x11-terms/terminator/terminator-2.1.3-r1.ebuild
+++ /dev/null
@@ -1,65 +0,0 @@
1# Copyright 1999-2023 Gentoo Authors
2# Distributed under the terms of the GNU General Public License v2
3
4EAPI=8
5
6DISTUTILS_USE_PEP517=setuptools
7PYTHON_COMPAT=( python3_{9..11} )
8inherit distutils-r1 optfeature verify-sig virtualx xdg
9
10DESCRIPTION="Multiple GNOME terminals in one window"
11HOMEPAGE="https://github.com/gnome-terminator/terminator"
12SRC_URI="
13 https://github.com/gnome-terminator/terminator/releases/download/v${PV}/${P}.tar.gz
14 verify-sig? ( https://github.com/gnome-terminator/terminator/releases/download/v${PV}/${P}.tar.gz.asc )
15"
16
17LICENSE="GPL-2"
18SLOT="0"
19KEYWORDS="~amd64 ~ppc ~riscv ~x86"
20IUSE="test"
21
22RDEPEND="
23 dev-libs/glib:2
24 dev-python/configobj[${PYTHON_USEDEP}]
25 dev-python/psutil[${PYTHON_USEDEP}]
26 dev-python/pycairo[${PYTHON_USEDEP}]
27 dev-python/pygobject:3[${PYTHON_USEDEP}]
28 gnome-base/gsettings-desktop-schemas[introspection]
29 x11-libs/gtk+:3
30 x11-libs/vte:2.91[introspection]
31"
32BDEPEND="
33 dev-util/intltool
34 sys-devel/gettext
35 test? (
36 dev-python/dbus-python[${PYTHON_USEDEP}]
37 x11-libs/libnotify[introspection]
38 )
39 verify-sig? ( sec-keys/openpgp-keys-terminator )
40"
41distutils_enable_tests pytest
42
43VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/terminator.asc
44
45PATCHES=(
46 "${FILESDIR}"/terminator-1.91-desktop.patch
47 "${FILESDIR}"/terminator-1.92-single-tab.patch
48)
49
50src_prepare() {
51 sed -i -e '/pytest-runner/d' setup.py || die
52 distutils-r1_src_prepare
53}
54
55src_test() {
56 virtx distutils-r1_src_test
57}
58
59pkg_postinst() {
60 xdg_pkg_postinst
61
62 optfeature "D-Bus" dev-python/dbus-python
63 optfeature "desktop notifications" "x11-libs/libnotify[introspection]"
64 optfeature "global keyboard shortcuts" "dev-libs/keybinder:3[introspection]"
65}