mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-05 07:27:04 +03:00
Merge branch 'master' into php81
This commit is contained in:
commit
0985df857c
81 changed files with 1015 additions and 741 deletions
47
CHANGELOG.md
47
CHANGELOG.md
|
|
@ -1,3 +1,50 @@
|
||||||
|
## 2.35.2 – 2024-02-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- GnuPG error handling
|
||||||
|
- Missing strings for localization inside identity popup (Cryptography > S/MIME)
|
||||||
|
[#1458](https://github.com/the-djmaze/snappymail/issues/1458)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Update Portuguese by @ner00
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Drop support for gnupg PECL extension as it fails with "no passphrase" issues
|
||||||
|
- Error 352 when getting Filters
|
||||||
|
[#1455](https://github.com/the-djmaze/snappymail/issues/1455)
|
||||||
|
- Nextcloud SetPassword(): Argument #1 must be of type SensitiveString, string given
|
||||||
|
[#1456](https://github.com/the-djmaze/snappymail/issues/1456)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.35.1 – 2024-02-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Search functionality in Admin -> Config
|
||||||
|
- Cache S/MIME passphrases when "remember" is checked
|
||||||
|
- Import S/MIME certificate popup
|
||||||
|
- pre-verify S/MIME opaque signed messages so we have a body to view
|
||||||
|
- Sort PGP keys and S/MIME certificates on email address
|
||||||
|
- Optionally use existing private key to generate S/MIME certificate
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Better handling to detect which PGP or S/MIME sign/encrypt to use
|
||||||
|
- Improved StorageType handling
|
||||||
|
- Cleanup and improved Capa handling
|
||||||
|
- OPEN_PGP should be OPENPGP as it is one word
|
||||||
|
- Use get_debug_type() instead of gettype()
|
||||||
|
- Require OpenSSL due to S/MIME
|
||||||
|
- AbstractProvider::IsActive() is now an abstract method and must be defined in child class
|
||||||
|
- Make better use of SnappyMail\SensitiveString
|
||||||
|
- Update Polish translation by @tinola
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Verify S/MIME signatures got broken allong the way while implementing this
|
||||||
|
- Generate S/MIME self-signed certificate failed to keep existing private key
|
||||||
|
- MIME parser RegExp didn't escape boundary which caused issues
|
||||||
|
- TypeError: b64Encode(...).match(...) is null on saving compose draft
|
||||||
|
- Fix timestampToString() for future dates
|
||||||
|
|
||||||
|
|
||||||
## 2.35.0 – 2024-02-20
|
## 2.35.0 – 2024-02-20
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
24
README.md
24
README.md
|
|
@ -140,26 +140,26 @@ RainLoop 1.17 vs SnappyMail
|
||||||
|
|
||||||
|js/* |RainLoop |Snappy |
|
|js/* |RainLoop |Snappy |
|
||||||
|--------------- |--------: |--------: |
|
|--------------- |--------: |--------: |
|
||||||
|admin.js |2.170.153 | 81.641 |
|
|admin.js |2.170.153 | 82.445 |
|
||||||
|app.js |4.207.787 | 426.852 |
|
|app.js |4.207.787 | 429.565 |
|
||||||
|boot.js | 868.735 | 4.142 |
|
|boot.js | 868.735 | 4.142 |
|
||||||
|libs.js | 658.812 | 192.625 |
|
|libs.js | 658.812 | 192.786 |
|
||||||
|sieve.js | 0 | 84.707 |
|
|sieve.js | 0 | 84.707 |
|
||||||
|polyfills.js | 334.608 | 0 |
|
|polyfills.js | 334.608 | 0 |
|
||||||
|serviceworker.js | 0 | 285 |
|
|serviceworker.js | 0 | 285 |
|
||||||
|TOTAL |8.240.095 | 790.252 |
|
|TOTAL |8.240.095 | 793.930 |
|
||||||
|
|
||||||
|js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|
|js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|
||||||
|--------------- |--------: |--------: |------: |------: |--------: |--------: |
|
|--------------- |--------: |--------: |------: |------: |--------: |--------: |
|
||||||
|admin.min.js | 256.831 | 40.142 | 73.606 | 13.445 | 60.877 | 12.047 |
|
|admin.min.js | 256.831 | 40.573 | 73.606 | 13.585 | 60.877 | 12.188 |
|
||||||
|app.min.js | 515.367 | 193.738 |139.456 | 65.570 |110.485 | 56.281 |
|
|app.min.js | 515.367 | 195.013 |139.456 | 66.135 |110.485 | 56.623 |
|
||||||
|boot.min.js | 84.659 | 2.084 | 26.998 | 1.202 | 23.643 | 1.003 |
|
|boot.min.js | 84.659 | 2.084 | 26.998 | 1.202 | 23.643 | 1.003 |
|
||||||
|libs.min.js | 584.772 | 92.664 |180.901 | 34.414 |155.182 | 30.850 |
|
|libs.min.js | 584.772 | 92.746 |180.901 | 34.452 |155.182 | 30.890 |
|
||||||
|sieve.min.js | 0 | 41.164 | 0 | 10.365 | 0 | 9.359 |
|
|sieve.min.js | 0 | 41.164 | 0 | 10.365 | 0 | 9.359 |
|
||||||
|polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 |
|
|polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 |
|
||||||
|TOTAL user |1.217.635 | 288.486 |358.761 |101.186 |299.485 | 88.134 |
|
|TOTAL user |1.217.635 | 289.843 |358.761 |101.789 |299.485 | 88.516 |
|
||||||
|TOTAL user+sieve |1.217.635 | 329.650 |358.761 |111.551 |299.485 | 97.493 |
|
|TOTAL user+sieve |1.217.635 | 331.007 |358.761 |112.154 |299.485 | 97.875 |
|
||||||
|TOTAL admin | 959.099 | 134.890 |292.911 | 49.061 |249.877 | 43.900 |
|
|TOTAL admin | 959.099 | 135.403 |292.911 | 49.239 |249.877 | 44.081 |
|
||||||
|
|
||||||
For a user it is around 69% smaller and faster than traditional RainLoop.
|
For a user it is around 69% smaller and faster than traditional RainLoop.
|
||||||
|
|
||||||
|
|
@ -188,8 +188,8 @@ For a user it is around 69% smaller and faster than traditional RainLoop.
|
||||||
|
|
||||||
|css/* |RainLoop |Snappy |RL gzip |SM gzip |SM brotli |
|
|css/* |RainLoop |Snappy |RL gzip |SM gzip |SM brotli |
|
||||||
|------------ |-------: |------: |------: |------: |--------: |
|
|------------ |-------: |------: |------: |------: |--------: |
|
||||||
|app.css | 340.331 | 84.435 | 46.946 | 17.612 | 15.091 |
|
|app.css | 340.331 | 84.472 | 46.946 | 17.622 | 15.112 |
|
||||||
|app.min.css | 274.947 | 67.825 | 39.647 | 15.526 | 13.540 |
|
|app.min.css | 274.947 | 67.857 | 39.647 | 15.537 | 13.549 |
|
||||||
|boot.css | | 1.326 | | 664 | 545 |
|
|boot.css | | 1.326 | | 664 | 545 |
|
||||||
|boot.min.css | | 1.071 | | 590 | 474 |
|
|boot.min.css | | 1.071 | | 590 | 474 |
|
||||||
|admin.css | | 30.576 | | 7.013 | 6.096 |
|
|admin.css | | 30.576 | | 7.013 | 6.096 |
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,10 @@ export const GnuPGUserStore = new class {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (isPrivate) {
|
if (isPrivate) {
|
||||||
key.password = async (btnTxt = 'SIGN') => {
|
key.password = async (btnTxt = 'CRYPTO/SIGN') => {
|
||||||
const pass = await Passphrases.ask(key,
|
const pass = await Passphrases.ask(key,
|
||||||
'GnuPG key<br>' + key.id + ' ' + key.emails[0],
|
'GnuPG key<br>' + key.id + ' ' + key.emails[0],
|
||||||
'CRYPTO/'+btnTxt
|
btnTxt
|
||||||
);
|
);
|
||||||
pass && pass.remember && Passphrases.set(key, pass.password);
|
pass && pass.remember && Passphrases.set(key, pass.password);
|
||||||
return pass?.password;
|
return pass?.password;
|
||||||
|
|
@ -82,8 +82,8 @@ export const GnuPGUserStore = new class {
|
||||||
if (key.armor) {
|
if (key.armor) {
|
||||||
callback && callback();
|
callback && callback();
|
||||||
} else {
|
} else {
|
||||||
let pass = isPrivate ? await key.password('POPUP_VIEW_TITLE') : '';
|
let pass = isPrivate ? await key.password('OPENPGP/POPUP_VIEW_TITLE') : '';
|
||||||
if (null != pass) {
|
if (null != pass) try {
|
||||||
const result = await Remote.post('GnupgExportKey', null, {
|
const result = await Remote.post('GnupgExportKey', null, {
|
||||||
keyId: key.id,
|
keyId: key.id,
|
||||||
isPrivate: isPrivate,
|
isPrivate: isPrivate,
|
||||||
|
|
@ -95,6 +95,8 @@ export const GnuPGUserStore = new class {
|
||||||
} else {
|
} else {
|
||||||
Passphrases.delete(key);
|
Passphrases.delete(key);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Passphrases.delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return key.armor;
|
return key.armor;
|
||||||
|
|
@ -182,15 +184,20 @@ export const GnuPGUserStore = new class {
|
||||||
uid: message.uid,
|
uid: message.uid,
|
||||||
partId: pgpInfo.partId,
|
partId: pgpInfo.partId,
|
||||||
keyId: key.id,
|
keyId: key.id,
|
||||||
passphrase: await key.password('DECRYPT'),
|
passphrase: await key.password('CRYPTO/DECRYPT'),
|
||||||
data: '' // message.plain() optional
|
data: '' // message.plain() optional
|
||||||
}
|
}
|
||||||
if (null !== params.passphrase) {
|
if (null != params.passphrase) {
|
||||||
const result = await Remote.post('GnupgDecrypt', null, params);
|
try {
|
||||||
if (result?.Result?.data) {
|
const response = await Remote.post('GnupgDecrypt', null, params);
|
||||||
return result.Result;
|
if (response?.Result?.data) {
|
||||||
|
return response.Result;
|
||||||
|
}
|
||||||
|
throw response;
|
||||||
|
} catch (e) {
|
||||||
|
Passphrases.delete(key);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
Passphrases.delete(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
color: var(--folders-color, #333);
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
|
|
@ -77,22 +78,18 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
line-height: 34px;
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
vertical-align: middle;
|
|
||||||
color: var(--folders-disabled-color, #666);
|
|
||||||
border-left: 3px solid transparent;
|
border-left: 3px solid transparent;
|
||||||
|
color: var(--folders-color, #333);
|
||||||
|
display: block;
|
||||||
|
line-height: 34px;
|
||||||
padding: 0 2em 0 @folderItemPadding;
|
padding: 0 2em 0 @folderItemPadding;
|
||||||
|
position: relative;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
&.selectable {
|
&.selectable {
|
||||||
|
|
||||||
color: var(--folders-color, #333);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
@ -115,6 +112,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:not(.selectable) {
|
&:not(.selectable) {
|
||||||
|
color: var(--folders-disabled-color, #666);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,13 @@ export class IdentityPopupView extends AbstractViewPopup {
|
||||||
if (oData.Result.x509) {
|
if (oData.Result.x509) {
|
||||||
identity.smimeKey(oData.Result.pkey);
|
identity.smimeKey(oData.Result.pkey);
|
||||||
identity.smimeCertificate(oData.Result.x509);
|
identity.smimeCertificate(oData.Result.x509);
|
||||||
|
} else {
|
||||||
|
this.submitError(oData.ErrorMessage);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
name: identity.name(),
|
name: identity.name(),
|
||||||
email: identity.email(),
|
email: identity.email(),
|
||||||
|
privateKey: identity.smimeKey(),
|
||||||
passphrase: pass.password
|
passphrase: pass.password
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -587,10 +587,13 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: translate
|
// TODO: translate
|
||||||
alert('Decryption failed, canceled or not possible');
|
throw 'Decryption failed, canceled or not possible';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(e => console.error(e));
|
.catch(e => {
|
||||||
|
console.error(e)
|
||||||
|
alert(e.message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pgpVerify(/*self, event*/) {
|
pgpVerify(/*self, event*/) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
This app packages SnappyMail <upstream>2.35.0</upstream>.
|
This app packages SnappyMail <upstream>2.35.2</upstream>.
|
||||||
|
|
||||||
SnappyMail is a simple, modern, lightweight & fast web-based email client.
|
SnappyMail is a simple, modern, lightweight & fast web-based email client.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ RUN mkdir -p /app/code
|
||||||
WORKDIR /app/code
|
WORKDIR /app/code
|
||||||
|
|
||||||
# If you change the extraction below, be sure to test on scaleway
|
# If you change the extraction below, be sure to test on scaleway
|
||||||
VERSION=2.35.0
|
VERSION=2.35.2
|
||||||
RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \
|
RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \
|
||||||
unzip /tmp/snappymail.zip -d /app/code && \
|
unzip /tmp/snappymail.zip -d /app/code && \
|
||||||
rm /tmp/snappymail.zip && \
|
rm /tmp/snappymail.zip && \
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<id>snappymail</id>
|
<id>snappymail</id>
|
||||||
<name>SnappyMail</name>
|
<name>SnappyMail</name>
|
||||||
<summary>SnappyMail Webmail</summary>
|
<summary>SnappyMail Webmail</summary>
|
||||||
<version>2.35.0</version>
|
<version>2.35.2</version>
|
||||||
<licence>agpl</licence>
|
<licence>agpl</licence>
|
||||||
<author>SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli</author>
|
<author>SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli</author>
|
||||||
<description><![CDATA[**Simple, modern, lightweight & fast web-based email client.**
|
<description><![CDATA[**Simple, modern, lightweight & fast web-based email client.**
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class InstallStep implements IRepairStep
|
||||||
if ('12345' == $sPassword || !$sPassword) {
|
if ('12345' == $sPassword || !$sPassword) {
|
||||||
$output->info('Generate admin password');
|
$output->info('Generate admin password');
|
||||||
$sPassword = \substr(\base64_encode(\random_bytes(16)), 0, 12);
|
$sPassword = \substr(\base64_encode(\random_bytes(16)), 0, 12);
|
||||||
$oConfig->SetPassword($sPassword);
|
$oConfig->SetPassword(new \SnappyMail\SensitiveString($sPassword));
|
||||||
\RainLoop\Utils::saveFile(APP_PRIVATE_DATA . 'admin_password.txt', $sPassword . "\n");
|
\RainLoop\Utils::saveFile(APP_PRIVATE_DATA . 'admin_password.txt', $sPassword . "\n");
|
||||||
$bSave = true;
|
$bSave = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,6 @@ class SnappyMailHelper
|
||||||
*/
|
*/
|
||||||
if ($doLogin && $aCredentials[1] && $aCredentials[2]) {
|
if ($doLogin && $aCredentials[1] && $aCredentials[2]) {
|
||||||
try {
|
try {
|
||||||
$oActions->Logger()->AddSecret($aCredentials[2]);
|
|
||||||
$oAccount = $oActions->LoginProcess($aCredentials[1], $aCredentials[2]);
|
$oAccount = $oActions->LoginProcess($aCredentials[1], $aCredentials[2]);
|
||||||
if ($oAccount) {
|
if ($oAccount) {
|
||||||
// Must be here due to bug #1241
|
// Must be here due to bug #1241
|
||||||
|
|
@ -202,9 +201,10 @@ class SnappyMailHelper
|
||||||
return \SnappyMail\Crypt::EncryptUrlSafe($sPassword, $sSalt);
|
return \SnappyMail\Crypt::EncryptUrlSafe($sPassword, $sSalt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function decodePassword(string $sPassword, string $sSalt)/* : mixed */
|
public static function decodePassword(string $sPassword, string $sSalt) : ?\SnappyMail\SensitiveString
|
||||||
{
|
{
|
||||||
static::loadApp();
|
static::loadApp();
|
||||||
return \SnappyMail\Crypt::DecryptUrlSafe($sPassword, $sSalt);
|
$result = \SnappyMail\Crypt::DecryptUrlSafe($sPassword, $sSalt);
|
||||||
|
return $result ? new \SnappyMail\SensitiveString($result) : $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ return "SnappyMail Webmail is a browser-based multilingual IMAP client with an a
|
||||||
# script_snappymail_versions()
|
# script_snappymail_versions()
|
||||||
sub script_snappymail_versions
|
sub script_snappymail_versions
|
||||||
{
|
{
|
||||||
return ( "2.35.0" );
|
return ( "2.35.2" );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub script_snappymail_version_desc
|
sub script_snappymail_version_desc
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"title": "SnappyMail",
|
"title": "SnappyMail",
|
||||||
"description": "Simple, modern & fast web-based email client",
|
"description": "Simple, modern & fast web-based email client",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.35.0",
|
"version": "2.35.2",
|
||||||
"homepage": "https://snappymail.eu",
|
"homepage": "https://snappymail.eu",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "DJ Maze",
|
"name": "DJ Maze",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ class LdapConfig
|
||||||
public const CONFIG_SERVER = "server";
|
public const CONFIG_SERVER = "server";
|
||||||
public const CONFIG_PROTOCOL_VERSION = "server_version";
|
public const CONFIG_PROTOCOL_VERSION = "server_version";
|
||||||
public const CONFIG_STARTTLS = "starttls";
|
public const CONFIG_STARTTLS = "starttls";
|
||||||
|
public const CONFIG_MAIL_PREFIX = "mail_prefix";
|
||||||
|
|
||||||
public const CONFIG_BIND_USER = "bind_user";
|
public const CONFIG_BIND_USER = "bind_user";
|
||||||
public const CONFIG_BIND_PASSWORD = "bind_password";
|
public const CONFIG_BIND_PASSWORD = "bind_password";
|
||||||
|
|
@ -30,6 +31,7 @@ class LdapConfig
|
||||||
public $server;
|
public $server;
|
||||||
public $protocol;
|
public $protocol;
|
||||||
public $starttls;
|
public $starttls;
|
||||||
|
public $mail_prefix;
|
||||||
public $bind_user;
|
public $bind_user;
|
||||||
public $bind_password;
|
public $bind_password;
|
||||||
public $user_base;
|
public $user_base;
|
||||||
|
|
@ -51,6 +53,7 @@ class LdapConfig
|
||||||
$ldap->server = trim($config->Get("plugin", self::CONFIG_SERVER));
|
$ldap->server = trim($config->Get("plugin", self::CONFIG_SERVER));
|
||||||
$ldap->protocol = (int)trim($config->Get("plugin", self::CONFIG_PROTOCOL_VERSION, 3));
|
$ldap->protocol = (int)trim($config->Get("plugin", self::CONFIG_PROTOCOL_VERSION, 3));
|
||||||
$ldap->starttls = (bool)trim($config->Get("plugin", self::CONFIG_STARTTLS));
|
$ldap->starttls = (bool)trim($config->Get("plugin", self::CONFIG_STARTTLS));
|
||||||
|
$ldap->mail_prefix = trim($config->Get("plugin", self::CONFIG_MAIL_PREFIX));
|
||||||
$ldap->bind_user = trim($config->Get("plugin", self::CONFIG_BIND_USER));
|
$ldap->bind_user = trim($config->Get("plugin", self::CONFIG_BIND_USER));
|
||||||
$ldap->bind_password = trim($config->Get("plugin", self::CONFIG_BIND_PASSWORD));
|
$ldap->bind_password = trim($config->Get("plugin", self::CONFIG_BIND_PASSWORD));
|
||||||
$ldap->user_base = trim($config->Get("plugin", self::CONFIG_USER_BASE));
|
$ldap->user_base = trim($config->Get("plugin", self::CONFIG_USER_BASE));
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@ class LdapIdentities implements IIdentities
|
||||||
$this->config->user_base,
|
$this->config->user_base,
|
||||||
$this->config->user_objectclass,
|
$this->config->user_objectclass,
|
||||||
$this->config->user_field_name,
|
$this->config->user_field_name,
|
||||||
$this->config->user_field_mail
|
$this->config->user_field_mail,
|
||||||
|
$this->config->mail_prefix
|
||||||
);
|
);
|
||||||
} catch (LdapException $e) {
|
} catch (LdapException $e) {
|
||||||
return []; // exceptions are only thrown from the handleerror function that does logging already
|
return []; // exceptions are only thrown from the handleerror function that does logging already
|
||||||
|
|
@ -104,7 +105,8 @@ class LdapIdentities implements IIdentities
|
||||||
$this->config->group_base,
|
$this->config->group_base,
|
||||||
$this->config->group_objectclass,
|
$this->config->group_objectclass,
|
||||||
$this->config->group_field_name,
|
$this->config->group_field_name,
|
||||||
$this->config->group_field_mail
|
$this->config->group_field_mail,
|
||||||
|
$this->config->mail_prefix
|
||||||
);
|
);
|
||||||
} catch (LdapException $e) {
|
} catch (LdapException $e) {
|
||||||
return []; // exceptions are only thrown from the handleerror function that does logging already
|
return []; // exceptions are only thrown from the handleerror function that does logging already
|
||||||
|
|
@ -241,7 +243,7 @@ class LdapIdentities implements IIdentities
|
||||||
* @return LdapResult[]
|
* @return LdapResult[]
|
||||||
* @throws LdapException
|
* @throws LdapException
|
||||||
*/
|
*/
|
||||||
private function FindLdapResults(string $searchField, string $searchValue, string $searchBase, string $objectClass, string $nameField, string $mailField): array
|
private function FindLdapResults(string $searchField, string $searchValue, string $searchBase, string $objectClass, string $nameField, string $mailField, string $mailPrefix): array
|
||||||
{
|
{
|
||||||
$this->EnsureBound();
|
$this->EnsureBound();
|
||||||
|
|
||||||
|
|
@ -261,6 +263,8 @@ class LdapIdentities implements IIdentities
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$entries = $this->CleanupMailAddresses($entries, $mailField, $mailPrefix);
|
||||||
|
|
||||||
$results = [];
|
$results = [];
|
||||||
for ($i = 0; $i < $entries["count"]; $i++) {
|
for ($i = 0; $i < $entries["count"]; $i++) {
|
||||||
$entry = $entries[$i];
|
$entry = $entries[$i];
|
||||||
|
|
@ -276,6 +280,49 @@ class LdapIdentities implements IIdentities
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function CleanupMailAddresses(): If a prefix is given this function removes addresses without / with the wrong prefix and then the prefix itself from all remaining values.
|
||||||
|
// This is usefull for example for importing Active Directory LDAP entry "proxyAddresses" which can hold different address types with prefixes like "X400:", "smtp:" "sip:" and others.
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param array $entries
|
||||||
|
@param string $mailField
|
||||||
|
@paraam string $mailPrefix
|
||||||
|
@return array
|
||||||
|
*/
|
||||||
|
private function CleanupMailAddresses(array $entries, string $mailField, string $mailPrefix)
|
||||||
|
{
|
||||||
|
if (!empty($mailPrefix)) {
|
||||||
|
for ($i = 0; $i < $entries["count"]; $i++) {
|
||||||
|
// Remove addresses without the given prefix
|
||||||
|
$entries[$i]["$mailField"] = array_filter($entries[$i]["$mailField"],
|
||||||
|
function($prefixMail)
|
||||||
|
{
|
||||||
|
// $mailPrefix can't be used here, because it's nailed to the CleanupMailAddresses function and can't be passed to the array_filter function afaik.
|
||||||
|
// Ideas to avoid this are welcome.
|
||||||
|
if (stripos($prefixMail, $this->config->mail_prefix) === 0) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Set "count" to new value
|
||||||
|
$newcount = count($entries[$i]["$mailField"]);
|
||||||
|
if (array_key_exists("count", $entries[$i]["$mailField"])) {
|
||||||
|
$newcount = $newcount - 1;
|
||||||
|
}
|
||||||
|
$entries[$i]["$mailField"]["count"] = $newcount;
|
||||||
|
|
||||||
|
// Remove the prefix
|
||||||
|
for ($j = 0; $j < $entries[$i]["$mailField"]["count"]; $j++) {
|
||||||
|
$mailPrefixLen = mb_strlen($mailPrefix);
|
||||||
|
$entries[$i]["$mailField"][$j] = substr($entries[$i]["$mailField"][$j], $mailPrefixLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $entry
|
* @param array $entry
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ class LdapIdentitiesPlugin extends AbstractPlugin
|
||||||
{
|
{
|
||||||
const
|
const
|
||||||
NAME = 'LDAP Identities',
|
NAME = 'LDAP Identities',
|
||||||
VERSION = '2.2',
|
VERSION = '2.3',
|
||||||
AUTHOR = 'FWest98',
|
AUTHOR = 'FWest98',
|
||||||
URL = 'https://github.com/FWest98',
|
URL = 'https://github.com/FWest98',
|
||||||
RELEASE = '2024-02-22',
|
RELEASE = '2024-02-27',
|
||||||
REQUIRED = '2.20.0',
|
REQUIRED = '2.20.0',
|
||||||
CATEGORY = 'Accounts',
|
CATEGORY = 'Accounts',
|
||||||
DESCRIPTION = 'Adds functionality to import account identities from LDAP.';
|
DESCRIPTION = 'Adds functionality to import account identities from LDAP.';
|
||||||
|
|
@ -64,6 +64,12 @@ class LdapIdentitiesPlugin extends AbstractPlugin
|
||||||
->SetDescription("Whether or not to use TLS encrypted connection")
|
->SetDescription("Whether or not to use TLS encrypted connection")
|
||||||
->SetDefaultValue(true),
|
->SetDefaultValue(true),
|
||||||
|
|
||||||
|
Property::NewInstance(LdapConfig::CONFIG_MAIL_PREFIX)
|
||||||
|
->SetLabel("Email prefix")
|
||||||
|
->SetType(PluginPropertyType::STRING)
|
||||||
|
->SetDescription("Only addresses with this prefix will be used as identity. The prefix is removed from the identity list.\nThis is useful for example to import identities from Exchange, which stores mail addresses in the ProxyAddresses attribut of Active Directory with \"smtp:\" as prefix. (e.g. \"smtp:john.doe@topsecret.info\")\n-> To use addresses set by Exchange use \"smtp:\" as prefix.")
|
||||||
|
->SetDefaultValue(""),
|
||||||
|
|
||||||
Property::NewInstance(LdapConfig::CONFIG_BIND_USER)
|
Property::NewInstance(LdapConfig::CONFIG_BIND_USER)
|
||||||
->SetLabel("Bind User DN")
|
->SetLabel("Bind User DN")
|
||||||
->SetDescription("The user to use for binding to the LDAP server. Should be a DN or RDN. Leave empty for anonymous bind")
|
->SetDescription("The user to use for binding to the LDAP server. Should be a DN or RDN. Leave empty for anonymous bind")
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
if ('storage' === $sName || 'storage-local' === $sName) {
|
if ($this->Config()->Get('plugin', 'storage', false) && ('storage' === $sName || 'storage-local' === $sName)) {
|
||||||
require_once __DIR__ . '/storage.php';
|
require_once __DIR__ . '/storage.php';
|
||||||
$oDriver = new \NextcloudStorage(APP_PRIVATE_DATA.'storage', $sName === 'storage-local');
|
$oDriver = new \NextcloudStorage(APP_PRIVATE_DATA.'storage', $sName === 'storage-local');
|
||||||
}
|
}
|
||||||
|
|
@ -346,6 +346,11 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
\RainLoop\Plugins\Property::NewInstance('ignoreSystemAddressbook')->SetLabel('Ignore system addressbook')
|
\RainLoop\Plugins\Property::NewInstance('ignoreSystemAddressbook')->SetLabel('Ignore system addressbook')
|
||||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||||
->SetDefaultValue(true),
|
->SetDefaultValue(true),
|
||||||
|
/*
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('storage')->SetLabel('Use Nextcloud user ID in config storage path')
|
||||||
|
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||||
|
->SetDefaultValue(false)
|
||||||
|
*/
|
||||||
\RainLoop\Plugins\Property::NewInstance('calendar')->SetLabel('Enable "Put ICS in calendar"')
|
\RainLoop\Plugins\Property::NewInstance('calendar')->SetLabel('Enable "Put ICS in calendar"')
|
||||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||||
->SetDefaultValue(false)
|
->SetDefaultValue(false)
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,12 @@ class SieveClient extends \MailSo\Net\NetClient
|
||||||
public function Logout() : void
|
public function Logout() : void
|
||||||
{
|
{
|
||||||
if ($this->bIsLoggined) {
|
if ($this->bIsLoggined) {
|
||||||
$this->sendRequestWithCheck('LOGOUT');
|
try {
|
||||||
|
$this->sendRequestWithCheck('LOGOUT');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// https://github.com/the-djmaze/snappymail/issues/1455
|
||||||
|
$this->writeLogException($e, \LOG_WARNING, false);
|
||||||
|
}
|
||||||
$this->bIsLoggined = false;
|
$this->bIsLoggined = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -408,6 +413,7 @@ class SieveClient extends \MailSo\Net\NetClient
|
||||||
if (null === $sResponseBuffer) {
|
if (null === $sResponseBuffer) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// \MailSo\Imap\Enumerations\ResponseStatus
|
||||||
$bEnd = \in_array(\substr($sResponseBuffer, 0, 2), array('OK', 'NO'));
|
$bEnd = \in_array(\substr($sResponseBuffer, 0, 2), array('OK', 'NO'));
|
||||||
// convertEndOfLine
|
// convertEndOfLine
|
||||||
$sLine = \trim($sResponseBuffer);
|
$sLine = \trim($sResponseBuffer);
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ trait Accounts
|
||||||
$aAccounts = $this->GetAccounts($oMainAccount);
|
$aAccounts = $this->GetAccounts($oMainAccount);
|
||||||
|
|
||||||
$sEmail = \trim($this->GetActionParam('email', ''));
|
$sEmail = \trim($this->GetActionParam('email', ''));
|
||||||
$sPassword = $this->GetActionParam('password', '');
|
$oPassword = new \SnappyMail\SensitiveString($this->GetActionParam('password', ''));
|
||||||
$sName = \trim($this->GetActionParam('name', ''));
|
$sName = \trim($this->GetActionParam('name', ''));
|
||||||
$bNew = !empty($this->GetActionParam('new', 1));
|
$bNew = !empty($this->GetActionParam('new', 1));
|
||||||
|
|
||||||
|
|
@ -92,8 +92,8 @@ trait Accounts
|
||||||
throw new ClientException(Notifications::AccountDoesNotExist);
|
throw new ClientException(Notifications::AccountDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bNew || $sPassword) {
|
if ($bNew || \strlen($oPassword)) {
|
||||||
$oNewAccount = $this->LoginProcess($sEmail, $sPassword, false);
|
$oNewAccount = $this->LoginProcess($sEmail, $oPassword, false);
|
||||||
$aAccounts[$sEmail] = $oNewAccount->asTokenArray($oMainAccount);
|
$aAccounts[$sEmail] = $oNewAccount->asTokenArray($oMainAccount);
|
||||||
} else {
|
} else {
|
||||||
$aAccounts[$sEmail] = \RainLoop\Model\AdditionalAccount::convertArray($aAccounts[$sEmail]);
|
$aAccounts[$sEmail] = \RainLoop\Model\AdditionalAccount::convertArray($aAccounts[$sEmail]);
|
||||||
|
|
@ -261,7 +261,11 @@ trait Accounts
|
||||||
if (!$oIdentity->FromJSON($this->GetActionParams(), true)) {
|
if (!$oIdentity->FromJSON($this->GetActionParams(), true)) {
|
||||||
throw new ClientException(Notifications::InvalidInputArgument);
|
throw new ClientException(Notifications::InvalidInputArgument);
|
||||||
}
|
}
|
||||||
|
/* // TODO: verify private key for certificate?
|
||||||
|
if ($oIdentity->smimeCertificate && $oIdentity->smimeKey) {
|
||||||
|
new \SnappyMail\SMime\Certificate($oIdentity->smimeCertificate, $oIdentity->smimeKey);
|
||||||
|
}
|
||||||
|
*/
|
||||||
$this->IdentitiesProvider()->UpdateIdentity($oAccount, $oIdentity);
|
$this->IdentitiesProvider()->UpdateIdentity($oAccount, $oIdentity);
|
||||||
return $this->TrueResponse();
|
return $this->TrueResponse();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,9 @@ trait AdminDomains
|
||||||
public function DoAdminDomainMatch() : array
|
public function DoAdminDomainMatch() : array
|
||||||
{
|
{
|
||||||
$sEmail = $this->GetActionParam('username');
|
$sEmail = $this->GetActionParam('username');
|
||||||
$sPassword = '********';
|
$oPassword = new \SnappyMail\SensitiveString('********');
|
||||||
$sLogin = '';
|
$sLogin = '';
|
||||||
$this->resolveLoginCredentials($sEmail, $sPassword, $sLogin);
|
$this->resolveLoginCredentials($sEmail, $oPassword, $sLogin);
|
||||||
$oDomain = \str_contains($sEmail, '@')
|
$oDomain = \str_contains($sEmail, '@')
|
||||||
? $this->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true)
|
? $this->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true)
|
||||||
: null;
|
: null;
|
||||||
|
|
|
||||||
|
|
@ -1065,8 +1065,7 @@ trait Messages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sPassphrase = $this->GetActionParam('signPassphrase', '');
|
$oPassphrase = new \SnappyMail\SensitiveString($this->GetActionParam('signPassphrase', ''));
|
||||||
$this->logMask($sPassphrase);
|
|
||||||
|
|
||||||
$sFingerprint = $this->GetActionParam('signFingerprint', '');
|
$sFingerprint = $this->GetActionParam('signFingerprint', '');
|
||||||
if ($sFingerprint) {
|
if ($sFingerprint) {
|
||||||
|
|
@ -1084,7 +1083,7 @@ trait Messages
|
||||||
$oMessage->SubParts->Clear();
|
$oMessage->SubParts->Clear();
|
||||||
$oMessage->Attachments()->Clear();
|
$oMessage->Attachments()->Clear();
|
||||||
|
|
||||||
$GPG->addSignKey($sFingerprint, $sPassphrase);
|
$GPG->addSignKey($sFingerprint, $oPassphrase);
|
||||||
$GPG->setsignmode(GNUPG_SIG_MODE_DETACH);
|
$GPG->setsignmode(GNUPG_SIG_MODE_DETACH);
|
||||||
$sSignature = $GPG->signStream($fp);
|
$sSignature = $GPG->signStream($fp);
|
||||||
if (!$sSignature) {
|
if (!$sSignature) {
|
||||||
|
|
@ -1127,7 +1126,7 @@ trait Messages
|
||||||
|
|
||||||
$SMIME = $this->SMIME();
|
$SMIME = $this->SMIME();
|
||||||
$SMIME->setCertificate($sCertificate);
|
$SMIME->setCertificate($sCertificate);
|
||||||
$SMIME->setPrivateKey($sPrivateKey, $sPassphrase);
|
$SMIME->setPrivateKey($sPrivateKey, $oPassphrase);
|
||||||
$sSignature = $SMIME->sign($tmp, $detached);
|
$sSignature = $SMIME->sign($tmp, $detached);
|
||||||
|
|
||||||
if (!$sSignature) {
|
if (!$sSignature) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ trait Pgp
|
||||||
|
|
||||||
$GPG = $this->GnuPG();
|
$GPG = $this->GnuPG();
|
||||||
if ($GPG) {
|
if ($GPG) {
|
||||||
$keys = $GPG->keyInfo('');
|
$keys = $GPG->allKeysInfo('');
|
||||||
foreach ($keys['public'] as $key) {
|
foreach ($keys['public'] as $key) {
|
||||||
$key = $GPG->export($key['subkeys'][0]['fingerprint'] ?: $key['subkeys'][0]['keyid']);
|
$key = $GPG->export($key['subkeys'][0]['fingerprint'] ?: $key['subkeys'][0]['keyid']);
|
||||||
if ($key) {
|
if ($key) {
|
||||||
|
|
@ -47,7 +47,7 @@ trait Pgp
|
||||||
/**
|
/**
|
||||||
* @throws \MailSo\RuntimeException
|
* @throws \MailSo\RuntimeException
|
||||||
*/
|
*/
|
||||||
public function GnuPG() : ?GnuPG
|
public function GnuPG() : ?\SnappyMail\PGP\PGPInterface
|
||||||
{
|
{
|
||||||
$oAccount = $this->getMainAccountFromToken();
|
$oAccount = $this->getMainAccountFromToken();
|
||||||
if (!$oAccount) {
|
if (!$oAccount) {
|
||||||
|
|
@ -81,7 +81,7 @@ trait Pgp
|
||||||
if (\is_link($link) || \symlink($homedir, $link)) {
|
if (\is_link($link) || \symlink($homedir, $link)) {
|
||||||
$homedir = $link;
|
$homedir = $link;
|
||||||
} else {
|
} else {
|
||||||
\error_log("symlink('{$homedir}', '{$link}') failed");
|
$this->logWrite("symlink('{$homedir}', '{$link}') failed", \LOG_WARNING, 'GnuPG');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Else try ~/.gnupg/ + hash(email address)
|
// Else try ~/.gnupg/ + hash(email address)
|
||||||
|
|
@ -97,10 +97,6 @@ trait Pgp
|
||||||
$homedir = $tmpdir;
|
$homedir = $tmpdir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
|
||||||
throw new \Exception("socket name for '{$homedir}/S.gpg-agent.extra' is too long");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return GnuPG::getInstance($homedir);
|
return GnuPG::getInstance($homedir);
|
||||||
|
|
@ -113,15 +109,14 @@ trait Pgp
|
||||||
return $this->FalseResponse();
|
return $this->FalseResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
$sPassphrase = $this->GetActionParam('passphrase', '');
|
$oPassphrase = new \SnappyMail\SensitiveString($this->GetActionParam('passphrase', ''));
|
||||||
$this->logMask($sPassphrase);
|
|
||||||
|
|
||||||
$GPG->addDecryptKey($this->GetActionParam('keyId', ''), $sPassphrase);
|
$GPG->addDecryptKey($this->GetActionParam('keyId', ''), $oPassphrase);
|
||||||
|
|
||||||
$sData = $this->GetActionParam('data', '');
|
$sData = $this->GetActionParam('data', '');
|
||||||
$oPart = null;
|
$oPart = null;
|
||||||
$result = [
|
$result = [
|
||||||
'data' => '',
|
'data' => null,
|
||||||
'signatures' => []
|
'signatures' => []
|
||||||
];
|
];
|
||||||
if ($sData) {
|
if ($sData) {
|
||||||
|
|
@ -131,11 +126,13 @@ trait Pgp
|
||||||
$this->initMailClientConnection();
|
$this->initMailClientConnection();
|
||||||
$this->MailClient()->MessageMimeStream(
|
$this->MailClient()->MessageMimeStream(
|
||||||
function ($rResource) use ($GPG, &$result, &$oPart) {
|
function ($rResource) use ($GPG, &$result, &$oPart) {
|
||||||
if (\is_resource($rResource)) {
|
if (\is_resource($rResource)) try {
|
||||||
$result['data'] = $GPG->decryptStream($rResource);
|
$result['data'] = $GPG->decryptStream($rResource);
|
||||||
// $oPart = \MailSo\Mime\Part::FromString($result);
|
// $oPart = \MailSo\Mime\Part::FromString($result);
|
||||||
// $GPG->decryptStream($rResource, $rStreamHandle);
|
// $GPG->decryptStream($rResource, $rStreamHandle);
|
||||||
// $oPart = \MailSo\Mime\Part::FromStream($rStreamHandle);
|
// $oPart = \MailSo\Mime\Part::FromStream($rStreamHandle);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$result = $e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
$this->GetActionParam('folder', ''),
|
$this->GetActionParam('folder', ''),
|
||||||
|
|
@ -149,23 +146,28 @@ trait Pgp
|
||||||
// $result['signatures'] = $oPart->SubParts[0];
|
// $result['signatures'] = $oPart->SubParts[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($result instanceof \Throwable) {
|
||||||
|
throw $result;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->DefaultResponse($result);
|
return $this->DefaultResponse($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DoGnupgGetKeys() : array
|
public function DoGnupgGetKeys() : array
|
||||||
{
|
{
|
||||||
$GPG = $this->GnuPG();
|
$GPG = $this->GnuPG();
|
||||||
return $this->DefaultResponse($GPG ? $GPG->keyInfo('') : false);
|
return $this->DefaultResponse($GPG ? $GPG->allKeysInfo('') : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DoGnupgExportKey() : array
|
public function DoGnupgExportKey() : array
|
||||||
{
|
{
|
||||||
$sPassphrase = $this->GetActionParam('passphrase', '');
|
$oPassphrase = $this->GetActionParam('isPrivate', '')
|
||||||
$this->logMask($sPassphrase);
|
? new \SnappyMail\SensitiveString($this->GetActionParam('passphrase', ''))
|
||||||
|
: null;
|
||||||
$GPG = $this->GnuPG();
|
$GPG = $this->GnuPG();
|
||||||
return $this->DefaultResponse($GPG ? $GPG->export(
|
return $this->DefaultResponse($GPG ? $GPG->export(
|
||||||
$this->GetActionParam('keyId', ''),
|
$this->GetActionParam('keyId', ''),
|
||||||
$sPassphrase
|
$oPassphrase
|
||||||
) : false);
|
) : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,11 +178,10 @@ trait Pgp
|
||||||
if ($GPG) {
|
if ($GPG) {
|
||||||
$sName = $this->GetActionParam('name', '');
|
$sName = $this->GetActionParam('name', '');
|
||||||
$sEmail = $this->GetActionParam('email', '');
|
$sEmail = $this->GetActionParam('email', '');
|
||||||
$sPassphrase = $this->GetActionParam('passphrase', '');
|
$oPassphrase = new \SnappyMail\SensitiveString($this->GetActionParam('passphrase', ''));
|
||||||
$this->logMask($sPassphrase);
|
|
||||||
$fingerprint = $GPG->generateKey(
|
$fingerprint = $GPG->generateKey(
|
||||||
$sName ? "{$sName} <{$sEmail}>" : $sEmail,
|
$sName ? "{$sName} <{$sEmail}>" : $sEmail,
|
||||||
$sPassphrase
|
$oPassphrase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $this->DefaultResponse($fingerprint);
|
return $this->DefaultResponse($fingerprint);
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,10 @@ trait Response
|
||||||
|
|
||||||
public function ExceptionResponse(\Throwable $oException) : array
|
public function ExceptionResponse(\Throwable $oException) : array
|
||||||
{
|
{
|
||||||
$iErrorCode = 0;
|
$iErrorCode = Notifications::UnknownError;
|
||||||
$sErrorMessage = '';
|
$sErrorMessage = $oException->getMessage();
|
||||||
$sErrorMessageAdditional = '';
|
$sErrorMessageAdditional = '';
|
||||||
|
$iExceptionCode = 0;
|
||||||
|
|
||||||
if ($oException instanceof \RainLoop\Exceptions\ClientException) {
|
if ($oException instanceof \RainLoop\Exceptions\ClientException) {
|
||||||
$iErrorCode = $oException->getCode();
|
$iErrorCode = $oException->getCode();
|
||||||
|
|
@ -56,8 +57,7 @@ trait Response
|
||||||
}
|
}
|
||||||
$sErrorMessageAdditional = $oException->getAdditionalMessage();
|
$sErrorMessageAdditional = $oException->getAdditionalMessage();
|
||||||
} else {
|
} else {
|
||||||
$iErrorCode = Notifications::UnknownError;
|
$iExceptionCode = $oException->getCode();
|
||||||
$sErrorMessage = $oException->getCode().' - '.$oException->getMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logException($oException->getPrevious() ?: $oException);
|
$this->logException($oException->getPrevious() ?: $oException);
|
||||||
|
|
@ -65,7 +65,8 @@ trait Response
|
||||||
return $this->DefaultResponse(false, [
|
return $this->DefaultResponse(false, [
|
||||||
'ErrorCode' => $iErrorCode,
|
'ErrorCode' => $iErrorCode,
|
||||||
'ErrorMessage' => $sErrorMessage,
|
'ErrorMessage' => $sErrorMessage,
|
||||||
'ErrorMessageAdditional' => $sErrorMessageAdditional
|
'ErrorMessageAdditional' => $sErrorMessageAdditional,
|
||||||
|
'ExceptionCode' => $iExceptionCode
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,21 +53,18 @@ trait SMime
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be use by Identity
|
* Can be used by Identity
|
||||||
*/
|
*/
|
||||||
public function DoSMimeCreateCertificate() : array
|
public function DoSMimeCreateCertificate() : array
|
||||||
{
|
{
|
||||||
$oAccount = $this->getAccountFromToken();
|
$oAccount = $this->getAccountFromToken();
|
||||||
|
|
||||||
$sName = $this->GetActionParam('name', '') ?: $oAccount->Name();
|
$oPassphrase = new \SnappyMail\SensitiveString($this->GetActionParam('passphrase', ''));
|
||||||
$sEmail = $this->GetActionParam('email', '') ?: $oAccount->Email();
|
|
||||||
$sPassphrase = $this->GetActionParam('passphrase', '');
|
|
||||||
$this->logMask($sPassphrase);
|
|
||||||
|
|
||||||
$cert = new Certificate();
|
$cert = new Certificate();
|
||||||
$cert->distinguishedName['commonName'] = $sName;
|
$cert->distinguishedName['commonName'] = $this->GetActionParam('name', '') ?: $oAccount->Name();
|
||||||
$cert->distinguishedName['emailAddress'] = $sEmail;
|
$cert->distinguishedName['emailAddress'] = $this->GetActionParam('email', '') ?: $oAccount->Email();
|
||||||
$result = $cert->createSelfSigned($sPassphrase);
|
$result = $cert->createSelfSigned($oPassphrase, $this->GetActionParam('privateKey', ''));
|
||||||
return $this->DefaultResponse($result ?: false);
|
return $this->DefaultResponse($result ?: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,8 +75,7 @@ trait SMime
|
||||||
$sPartId = $this->GetActionParam('partId', '');
|
$sPartId = $this->GetActionParam('partId', '');
|
||||||
$sCertificate = $this->GetActionParam('certificate', '');
|
$sCertificate = $this->GetActionParam('certificate', '');
|
||||||
$sPrivateKey = $this->GetActionParam('privateKey', '');
|
$sPrivateKey = $this->GetActionParam('privateKey', '');
|
||||||
$sPassphrase = $this->GetActionParam('passphrase', '');
|
$oPassphrase = new \SnappyMail\SensitiveString($this->GetActionParam('passphrase', ''));
|
||||||
$this->logMask($sPassphrase);
|
|
||||||
|
|
||||||
$this->initMailClientConnection();
|
$this->initMailClientConnection();
|
||||||
$oImapClient = $this->ImapClient();
|
$oImapClient = $this->ImapClient();
|
||||||
|
|
@ -106,7 +102,7 @@ trait SMime
|
||||||
|
|
||||||
$SMIME = $this->SMIME();
|
$SMIME = $this->SMIME();
|
||||||
$SMIME->setCertificate($sCertificate);
|
$SMIME->setCertificate($sCertificate);
|
||||||
$SMIME->setPrivateKey($sPrivateKey, $sPassphrase);
|
$SMIME->setPrivateKey($sPrivateKey, $oPassphrase);
|
||||||
$result = $SMIME->decrypt($sBody);
|
$result = $SMIME->decrypt($sBody);
|
||||||
|
|
||||||
return $this->DefaultResponse($result ?: false);
|
return $this->DefaultResponse($result ?: false);
|
||||||
|
|
@ -142,9 +138,9 @@ trait SMime
|
||||||
$sBody = \trim($sBody);
|
$sBody = \trim($sBody);
|
||||||
$certificates = [];
|
$certificates = [];
|
||||||
\openssl_pkcs7_read(
|
\openssl_pkcs7_read(
|
||||||
"-----BEGIN CERTIFICATE-----\n\n{$sBody}\n-----END CERTIFICATE-----",
|
"-----BEGIN PKCS7-----\n\n{$sBody}\n-----END PKCS7-----",
|
||||||
$certificates
|
$certificates
|
||||||
) || \error_log("OpenSSL openssl_pkcs7_read: " . \openssl_error_string());
|
) || $this->logWrite("openssl_pkcs7_read: " . \openssl_error_string(), \LOG_ERR, 'OpenSSL');
|
||||||
foreach ($certificates as $certificate) {
|
foreach ($certificates as $certificate) {
|
||||||
$this->SMIME()->storeCertificate($certificate);
|
$this->SMIME()->storeCertificate($certificate);
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +175,7 @@ trait SMime
|
||||||
$sBody = \trim($sBody);
|
$sBody = \trim($sBody);
|
||||||
$certificates = [];
|
$certificates = [];
|
||||||
\openssl_pkcs7_read(
|
\openssl_pkcs7_read(
|
||||||
"-----BEGIN CERTIFICATE-----\n\n{$sBody}\n-----END CERTIFICATE-----",
|
"-----BEGIN PKCS7-----\n\n{$sBody}\n-----END PKCS7-----",
|
||||||
$certificates
|
$certificates
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,10 @@ trait User
|
||||||
public function DoLogin() : array
|
public function DoLogin() : array
|
||||||
{
|
{
|
||||||
$sEmail = \MailSo\Base\Utils::Trim($this->GetActionParam('Email', ''));
|
$sEmail = \MailSo\Base\Utils::Trim($this->GetActionParam('Email', ''));
|
||||||
$sPassword = $this->GetActionParam('Password', '');
|
$oPassword = new \SnappyMail\SensitiveString($this->GetActionParam('Password', ''));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$oAccount = $this->LoginProcess($sEmail, $sPassword);
|
$oAccount = $this->LoginProcess($sEmail, $oPassword);
|
||||||
} catch (\Throwable $oException) {
|
} catch (\Throwable $oException) {
|
||||||
$this->loginErrorDelay();
|
$this->loginErrorDelay();
|
||||||
throw $oException;
|
throw $oException;
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,7 @@ trait UserAuth
|
||||||
/**
|
/**
|
||||||
* @throws \RainLoop\Exceptions\ClientException
|
* @throws \RainLoop\Exceptions\ClientException
|
||||||
*/
|
*/
|
||||||
public function resolveLoginCredentials(string &$sEmail,
|
public function resolveLoginCredentials(string &$sEmail, \SnappyMail\SensitiveString $oPassword, string &$sLogin): void
|
||||||
#[\SensitiveParameter]
|
|
||||||
string &$sPassword,
|
|
||||||
string &$sLogin): void
|
|
||||||
{
|
{
|
||||||
$sEmail = \MailSo\Base\Utils::Trim($sEmail);
|
$sEmail = \MailSo\Base\Utils::Trim($sEmail);
|
||||||
if ($this->Config()->Get('login', 'login_lowercase', true)) {
|
if ($this->Config()->Get('login', 'login_lowercase', true)) {
|
||||||
|
|
@ -101,43 +98,40 @@ trait UserAuth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sPassword = (string) $oPassword;
|
||||||
$this->Plugins()->RunHook('login.credentials.step-2', array(&$sEmail, &$sPassword));
|
$this->Plugins()->RunHook('login.credentials.step-2', array(&$sEmail, &$sPassword));
|
||||||
|
$this->logMask($sPassword);
|
||||||
|
|
||||||
$sLogin = $sEmail;
|
$sLogin = $sEmail;
|
||||||
if ($this->Config()->Get('login', 'login_lowercase', true)) {
|
if ($this->Config()->Get('login', 'login_lowercase', true)) {
|
||||||
$sLogin = \mb_strtolower($sLogin);
|
$sLogin = \mb_strtolower($sLogin);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logMask($sPassword);
|
|
||||||
$this->Plugins()->RunHook('login.credentials', array(&$sEmail, &$sLogin, &$sPassword));
|
$this->Plugins()->RunHook('login.credentials', array(&$sEmail, &$sLogin, &$sPassword));
|
||||||
$this->logMask($sPassword);
|
$this->logMask($sPassword);
|
||||||
|
|
||||||
|
$oPassword->setValue($sPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \RainLoop\Exceptions\ClientException
|
* @throws \RainLoop\Exceptions\ClientException
|
||||||
*/
|
*/
|
||||||
public function LoginProcess(string &$sEmail,
|
public function LoginProcess(string &$sEmail, \SnappyMail\SensitiveString $oPassword, bool $bMainAccount = true): Account
|
||||||
#[\SensitiveParameter]
|
|
||||||
string &$sPassword,
|
|
||||||
bool $bMainAccount = true
|
|
||||||
): Account
|
|
||||||
{
|
{
|
||||||
$sInputEmail = $sEmail;
|
$sInputEmail = $sEmail;
|
||||||
|
|
||||||
$this->logMask($sPassword);
|
|
||||||
|
|
||||||
$sLogin = '';
|
$sLogin = '';
|
||||||
$this->resolveLoginCredentials($sEmail, $sPassword, $sLogin);
|
$this->resolveLoginCredentials($sEmail, $oPassword, $sLogin);
|
||||||
|
|
||||||
if (!\str_contains($sEmail, '@') || !\strlen($sPassword)) {
|
if (!\str_contains($sEmail, '@') || !\strlen($oPassword)) {
|
||||||
throw new ClientException(Notifications::InvalidInputArgument);
|
throw new ClientException(Notifications::InvalidInputArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oAccount = null;
|
$oAccount = null;
|
||||||
try {
|
try {
|
||||||
$oAccount = $bMainAccount
|
$oAccount = $bMainAccount
|
||||||
? MainAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, true)
|
? MainAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $oPassword, true)
|
||||||
: AdditionalAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, true);
|
: AdditionalAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $oPassword, true);
|
||||||
if (!$oAccount) {
|
if (!$oAccount) {
|
||||||
throw new ClientException(Notifications::AuthError);
|
throw new ClientException(Notifications::AuthError);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,17 +111,15 @@ class ActionsAdmin extends Actions
|
||||||
public function DoAdminLogin() : array
|
public function DoAdminLogin() : array
|
||||||
{
|
{
|
||||||
$sLogin = trim($this->GetActionParam('Login', ''));
|
$sLogin = trim($this->GetActionParam('Login', ''));
|
||||||
$sPassword = $this->GetActionParam('Password', '');
|
$oPassword = new \SnappyMail\SensitiveString($this->GetActionParam('Password', ''));
|
||||||
|
|
||||||
$this->logMask($sPassword);
|
|
||||||
|
|
||||||
$totp = $this->Config()->Get('security', 'admin_totp', '');
|
$totp = $this->Config()->Get('security', 'admin_totp', '');
|
||||||
|
|
||||||
// \explode(':',`getent shadow root`)[1];
|
// \explode(':',`getent shadow root`)[1];
|
||||||
if (!\strlen($sLogin) || !\strlen($sPassword) ||
|
if (!\strlen($sLogin) || !\strlen($oPassword) ||
|
||||||
!$this->Config()->Get('security', 'allow_admin_panel', true) ||
|
!$this->Config()->Get('security', 'allow_admin_panel', true) ||
|
||||||
$sLogin !== $this->Config()->Get('security', 'admin_login', '') ||
|
$sLogin !== $this->Config()->Get('security', 'admin_login', '') ||
|
||||||
!$this->Config()->ValidatePassword($sPassword)
|
!$this->Config()->ValidatePassword($oPassword)
|
||||||
|| ($totp && !\SnappyMail\TOTP::Verify($totp, $this->GetActionParam('TOTP', ''))))
|
|| ($totp && !\SnappyMail\TOTP::Verify($totp, $this->GetActionParam('TOTP', ''))))
|
||||||
{
|
{
|
||||||
$this->LoggerAuthHelper(null, $this->getAdditionalLogParamsByUserLogin($sLogin, true), true);
|
$this->LoggerAuthHelper(null, $this->getAdditionalLogParamsByUserLogin($sLogin, true), true);
|
||||||
|
|
@ -183,17 +181,13 @@ class ActionsAdmin extends Actions
|
||||||
$bResult = false;
|
$bResult = false;
|
||||||
$oConfig = $this->Config();
|
$oConfig = $this->Config();
|
||||||
|
|
||||||
$sPassword = $this->GetActionParam('Password', '');
|
$oPassword = new \SnappyMail\SensitiveString($this->GetActionParam('Password', ''));
|
||||||
$this->logMask($sPassword);
|
|
||||||
|
|
||||||
$sNewPassword = $this->GetActionParam('newPassword', '');
|
$oNewPassword = new \SnappyMail\SensitiveString($this->GetActionParam('newPassword', ''));
|
||||||
if (\strlen($sNewPassword)) {
|
|
||||||
$this->logMask($sNewPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
$passfile = APP_PRIVATE_DATA.'admin_password.txt';
|
$passfile = APP_PRIVATE_DATA.'admin_password.txt';
|
||||||
|
|
||||||
if ($oConfig->ValidatePassword($sPassword)) {
|
if ($oConfig->ValidatePassword($oPassword)) {
|
||||||
$sLogin = \trim($this->GetActionParam('Login', ''));
|
$sLogin = \trim($this->GetActionParam('Login', ''));
|
||||||
if (\strlen($sLogin)) {
|
if (\strlen($sLogin)) {
|
||||||
$oConfig->Set('security', 'admin_login', $sLogin);
|
$oConfig->Set('security', 'admin_login', $sLogin);
|
||||||
|
|
@ -201,9 +195,9 @@ class ActionsAdmin extends Actions
|
||||||
|
|
||||||
$oConfig->Set('security', 'admin_totp', $this->GetActionParam('TOTP', ''));
|
$oConfig->Set('security', 'admin_totp', $this->GetActionParam('TOTP', ''));
|
||||||
|
|
||||||
if (\strlen($sNewPassword)) {
|
if (\strlen($oNewPassword)) {
|
||||||
$oConfig->SetPassword($sNewPassword);
|
$oConfig->SetPassword($oNewPassword);
|
||||||
if (\is_file($passfile) && \trim(\file_get_contents($passfile)) !== $sNewPassword) {
|
if (\is_file($passfile) && \trim(\file_get_contents($passfile)) !== (string) $oNewPassword) {
|
||||||
\unlink($passfile);
|
\unlink($passfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,25 +120,14 @@ class Application extends \RainLoop\Config\AbstractConfig
|
||||||
parent::Set($sSectionKey, $sParamKey, $mParamValue);
|
parent::Set($sSectionKey, $sParamKey, $mParamValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetPassword(
|
public function SetPassword(\SnappyMail\SensitiveString $oPassword) : void
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $sPassword
|
|
||||||
) : void
|
|
||||||
{
|
{
|
||||||
$this->Set('security', 'admin_password', \password_hash($sPassword, PASSWORD_DEFAULT));
|
$this->Set('security', 'admin_password', \password_hash($oPassword, PASSWORD_DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ValidatePassword(
|
public function ValidatePassword(\SnappyMail\SensitiveString $oPassword) : bool
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $sPassword
|
|
||||||
) : bool
|
|
||||||
{
|
{
|
||||||
$sConfigPassword = (string) $this->Get('security', 'admin_password', '');
|
return \strlen($oPassword) && \password_verify($oPassword, $this->Get('security', 'admin_password', ''));
|
||||||
if (32 == \strlen($sPassword) && \md5(APP_SALT.$sPassword.APP_SALT) === $sConfigPassword) {
|
|
||||||
$this->SetPassword($sPassword);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return \strlen($sPassword) && \password_verify($sPassword, $sConfigPassword);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Save() : bool
|
public function Save() : bool
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ abstract class Account implements \JsonSerializable
|
||||||
|
|
||||||
private string $sLogin = '';
|
private string $sLogin = '';
|
||||||
|
|
||||||
private ?SensitiveString $sPassword = null;
|
private ?SensitiveString $oPassword = null;
|
||||||
|
|
||||||
private string $sSmtpLogin = '';
|
private string $sSmtpLogin = '';
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ abstract class Account implements \JsonSerializable
|
||||||
|
|
||||||
public function IncPassword() : string
|
public function IncPassword() : string
|
||||||
{
|
{
|
||||||
return $this->sPassword ? $this->sPassword->getValue() : '';
|
return $this->oPassword ? $this->oPassword->getValue() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function OutLogin() : string
|
public function OutLogin() : string
|
||||||
|
|
@ -66,16 +66,13 @@ abstract class Account implements \JsonSerializable
|
||||||
$this->sEmail,
|
$this->sEmail,
|
||||||
$this->sLogin,
|
$this->sLogin,
|
||||||
// \json_encode($this->Domain()),
|
// \json_encode($this->Domain()),
|
||||||
// $this->sPassword
|
// $this->oPassword
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetPassword(
|
public function SetPassword(SensitiveString $oPassword) : void
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $sPassword
|
|
||||||
) : void
|
|
||||||
{
|
{
|
||||||
$this->sPassword = new SensitiveString($sPassword);
|
$this->oPassword = $oPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetSmtpPassword(
|
public function SetSmtpPassword(
|
||||||
|
|
@ -125,12 +122,11 @@ abstract class Account implements \JsonSerializable
|
||||||
|
|
||||||
public static function NewInstanceFromCredentials(\RainLoop\Actions $oActions,
|
public static function NewInstanceFromCredentials(\RainLoop\Actions $oActions,
|
||||||
string $sEmail, string $sLogin,
|
string $sEmail, string $sLogin,
|
||||||
#[\SensitiveParameter]
|
SensitiveString $oPassword,
|
||||||
string $sPassword,
|
|
||||||
bool $bThrowException = false): ?self
|
bool $bThrowException = false): ?self
|
||||||
{
|
{
|
||||||
$oAccount = null;
|
$oAccount = null;
|
||||||
if ($sEmail && $sLogin && $sPassword) {
|
if ($sEmail && $sLogin && \strlen($oPassword)) {
|
||||||
$oDomain = $oActions->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true);
|
$oDomain = $oActions->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true);
|
||||||
if ($oDomain) {
|
if ($oDomain) {
|
||||||
if ($oDomain->ValidateWhiteList($sEmail, $sLogin)) {
|
if ($oDomain->ValidateWhiteList($sEmail, $sLogin)) {
|
||||||
|
|
@ -138,7 +134,7 @@ abstract class Account implements \JsonSerializable
|
||||||
|
|
||||||
$oAccount->sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true);
|
$oAccount->sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true);
|
||||||
$oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin);
|
$oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin);
|
||||||
$oAccount->SetPassword($sPassword);
|
$oAccount->SetPassword($oPassword);
|
||||||
$oAccount->oDomain = $oDomain;
|
$oAccount->oDomain = $oDomain;
|
||||||
|
|
||||||
$oActions->Plugins()->RunHook('filter.account', array($oAccount));
|
$oActions->Plugins()->RunHook('filter.account', array($oAccount));
|
||||||
|
|
@ -194,25 +190,22 @@ abstract class Account implements \JsonSerializable
|
||||||
$oActions,
|
$oActions,
|
||||||
$aAccountHash['email'],
|
$aAccountHash['email'],
|
||||||
$aAccountHash['login'],
|
$aAccountHash['login'],
|
||||||
$aAccountHash['pass'],
|
new SensitiveString($aAccountHash['pass']),
|
||||||
$bThrowExceptionOnFalse
|
$bThrowExceptionOnFalse
|
||||||
);
|
);
|
||||||
if ($oAccount) {
|
if ($oAccount) {
|
||||||
if (isset($aAccountHash['name'])) {
|
if (isset($aAccountHash['name'])) {
|
||||||
$oAccount->sName = $aAccountHash['name'];
|
$oAccount->sName = $aAccountHash['name'];
|
||||||
}
|
}
|
||||||
$oActions->logMask($oAccount->IncPassword());
|
|
||||||
// init smtp user/password
|
// init smtp user/password
|
||||||
if (isset($aAccountHash['smtp'])) {
|
if (isset($aAccountHash['smtp'])) {
|
||||||
$oAccount->sSmtpLogin = $aAccountHash['smtp']['user'];
|
$oAccount->sSmtpLogin = $aAccountHash['smtp']['user'];
|
||||||
$oAccount->SetSmtpPassword($aAccountHash['smtp']['pass']);
|
$oAccount->SetSmtpPassword($aAccountHash['smtp']['pass']);
|
||||||
$oActions->logMask($oAccount->sSmtpPassword);
|
|
||||||
}
|
}
|
||||||
// init proxy user/password
|
// init proxy user/password
|
||||||
if (isset($aAccountHash['proxy'])) {
|
if (isset($aAccountHash['proxy'])) {
|
||||||
$oAccount->sProxyAuthUser = $aAccountHash['proxy']['user'];
|
$oAccount->sProxyAuthUser = $aAccountHash['proxy']['user'];
|
||||||
$oAccount->SetProxyAuthPassword($aAccountHash['proxy']['pass']);
|
$oAccount->SetProxyAuthPassword($aAccountHash['proxy']['pass']);
|
||||||
$oActions->logMask($oAccount->sProxyAuthPassword);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +233,7 @@ abstract class Account implements \JsonSerializable
|
||||||
$oImapClient->Connect($oSettings);
|
$oImapClient->Connect($oSettings);
|
||||||
$oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $oSettings));
|
$oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $oSettings));
|
||||||
|
|
||||||
$oSettings->Password = $this->sPassword;
|
$oSettings->Password = $this->oPassword;
|
||||||
return $this->netClientLogin($oImapClient, $oPlugins);
|
return $this->netClientLogin($oImapClient, $oPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -264,7 +257,7 @@ abstract class Account implements \JsonSerializable
|
||||||
throw new RequireCredentialsException
|
throw new RequireCredentialsException
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
$oSettings->Password = $this->sSmtpPassword ?: $this->sPassword;
|
$oSettings->Password = $this->sSmtpPassword ?: $this->oPassword;
|
||||||
return $this->netClientLogin($oSmtpClient, $oPlugins);
|
return $this->netClientLogin($oSmtpClient, $oPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,7 +272,7 @@ abstract class Account implements \JsonSerializable
|
||||||
$oSieveClient->Connect($oSettings);
|
$oSieveClient->Connect($oSettings);
|
||||||
$oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $oSettings));
|
$oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $oSettings));
|
||||||
|
|
||||||
$oSettings->Password = $this->sPassword;
|
$oSettings->Password = $this->oPassword;
|
||||||
return $this->netClientLogin($oSieveClient, $oPlugins);
|
return $this->netClientLogin($oSieveClient, $oPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,16 @@ class Identity implements \JsonSerializable
|
||||||
$this->sEmail = $sEmail;
|
$this->sEmail = $sEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function __get(string $name)
|
||||||
|
{
|
||||||
|
if (!\property_exists($this, $name)) {
|
||||||
|
$name = \substr($name, 1);
|
||||||
|
}
|
||||||
|
if (\property_exists($this, $name)) {
|
||||||
|
return $this->$name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function toMime() : \MailSo\Mime\Email
|
function toMime() : \MailSo\Mime\Email
|
||||||
{
|
{
|
||||||
return new \MailSo\Mime\Email($this->sEmail, $this->sName);
|
return new \MailSo\Mime\Email($this->sEmail, $this->sName);
|
||||||
|
|
|
||||||
|
|
@ -112,24 +112,16 @@ class ServiceActions
|
||||||
|
|
||||||
$this->oActions->logWrite('Action: '.$sMethodName, \LOG_INFO, 'JSON');
|
$this->oActions->logWrite('Action: '.$sMethodName, \LOG_INFO, 'JSON');
|
||||||
|
|
||||||
$aPost = $_POST ?? null;
|
if ($_POST) {
|
||||||
if ($aPost) {
|
$this->oActions->SetActionParams($_POST, $sMethodName);
|
||||||
$this->oActions->SetActionParams($aPost, $sMethodName);
|
$aPost = $_POST;
|
||||||
foreach ($aPost as $key => $value) {
|
foreach ($aPost as $key => $value) {
|
||||||
if (false !== \stripos($key, 'Password')) {
|
// password & passphrase
|
||||||
|
if (false !== \stripos($key, 'pass')) {
|
||||||
$aPost[$key] = '*******';
|
$aPost[$key] = '*******';
|
||||||
|
// $this->oActions->logMask($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
switch ($sMethodName)
|
|
||||||
{
|
|
||||||
case 'DoLogin':
|
|
||||||
case 'DoAdminLogin':
|
|
||||||
case 'DoAccountAdd':
|
|
||||||
$this->oActions->logMask($this->oActions->GetActionParam('Password', ''));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
$this->oActions->logWrite(Utils::jsonEncode($aPost), \LOG_INFO, 'POST');
|
$this->oActions->logWrite(Utils::jsonEncode($aPost), \LOG_INFO, 'POST');
|
||||||
} else if (3 < \count($this->aPaths) && $this->oHttp->IsGet()) {
|
} else if (3 < \count($this->aPaths) && $this->oHttp->IsGet()) {
|
||||||
$this->oActions->SetActionParams(array(
|
$this->oActions->SetActionParams(array(
|
||||||
|
|
@ -561,14 +553,14 @@ class ServiceActions
|
||||||
(0 === $aData['Time'] || \time() - 10 < $aData['Time']))
|
(0 === $aData['Time'] || \time() - 10 < $aData['Time']))
|
||||||
{
|
{
|
||||||
$sEmail = \trim($aData['Email']);
|
$sEmail = \trim($aData['Email']);
|
||||||
$sPassword = $aData['Password'];
|
$oPassword = new \SnappyMail\SensitiveString($aData['Password']);
|
||||||
|
|
||||||
$aAdditionalOptions = (isset($aData['AdditionalOptions']) && \is_array($aData['AdditionalOptions']))
|
$aAdditionalOptions = (isset($aData['AdditionalOptions']) && \is_array($aData['AdditionalOptions']))
|
||||||
? $aData['AdditionalOptions'] : [];
|
? $aData['AdditionalOptions'] : [];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$oAccount = $this->oActions->LoginProcess($sEmail, $sPassword);
|
$oAccount = $this->oActions->LoginProcess($sEmail, $oPassword);
|
||||||
|
|
||||||
if ($aAdditionalOptions) {
|
if ($aAdditionalOptions) {
|
||||||
$bNeedToSettings = false;
|
$bNeedToSettings = false;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
namespace SnappyMail\GPG;
|
namespace SnappyMail\GPG;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
abstract class Base
|
abstract class Base
|
||||||
{
|
{
|
||||||
const
|
const
|
||||||
|
|
@ -64,7 +66,13 @@ abstract class Base
|
||||||
|
|
||||||
function __construct(string $homedir)
|
function __construct(string $homedir)
|
||||||
{
|
{
|
||||||
$this->options['homedir'] = \rtrim($homedir, '/\\');
|
$homedir = \rtrim($homedir, '/\\');
|
||||||
|
// BSD 4.4 max length
|
||||||
|
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
||||||
|
throw new \Exception('Socket name for S.gpg-agent.extra is too long');
|
||||||
|
}
|
||||||
|
$this->options['homedir'] = $homedir;
|
||||||
|
// \putenv("GNUPGHOME={$homedir}");
|
||||||
}
|
}
|
||||||
|
|
||||||
function __destruct()
|
function __destruct()
|
||||||
|
|
@ -169,22 +177,13 @@ abstract class Base
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports a public key
|
* Exports a public or private key
|
||||||
*/
|
*/
|
||||||
public function export(string $fingerprint) /*: string|false*/
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a private key
|
|
||||||
*/
|
|
||||||
public function exportPrivateKey(string $fingerprint) /*: string|false*/
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a key
|
* Imports a key
|
||||||
*/
|
*/
|
||||||
|
|
@ -209,12 +208,11 @@ abstract class Base
|
||||||
/**
|
/**
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
*/
|
*/
|
||||||
public function keyInfo(string $pattern, int $private = 0) : array
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the mode for error_reporting
|
* Sets the mode for error_reporting
|
||||||
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
||||||
|
|
@ -243,7 +241,7 @@ abstract class Base
|
||||||
/**
|
/**
|
||||||
* Signs a given file
|
* Signs a given file
|
||||||
*/
|
*/
|
||||||
public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/
|
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -284,10 +282,7 @@ abstract class Base
|
||||||
/**
|
/**
|
||||||
* Add a key for decryption
|
* Add a key for decryption
|
||||||
*/
|
*/
|
||||||
public function addDecryptKey(string $fingerprint,
|
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $passphrase
|
|
||||||
) : bool
|
|
||||||
{
|
{
|
||||||
$this->decryptKeys[$fingerprint] = $passphrase;
|
$this->decryptKeys[$fingerprint] = $passphrase;
|
||||||
// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase;
|
// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase;
|
||||||
|
|
@ -306,10 +301,7 @@ abstract class Base
|
||||||
/**
|
/**
|
||||||
* Add a key for signing
|
* Add a key for signing
|
||||||
*/
|
*/
|
||||||
public function addSignKey(string $fingerprint,
|
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $passphrase
|
|
||||||
) : bool
|
|
||||||
{
|
{
|
||||||
$this->signKeys[$fingerprint] = $passphrase;
|
$this->signKeys[$fingerprint] = $passphrase;
|
||||||
// $this->signKeys[\substr($fingerprint, -16)] = $passphrase;
|
// $this->signKeys[\substr($fingerprint, -16)] = $passphrase;
|
||||||
|
|
@ -356,10 +348,7 @@ abstract class Base
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPassphrase($keyId,
|
public function addPassphrase($keyId, SensitiveString $passphrase)
|
||||||
#[\SensitiveParameter]
|
|
||||||
$passphrase
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
$this->passphrases[$keyId] = $passphrase;
|
$this->passphrases[$keyId] = $passphrase;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
@ -367,10 +356,11 @@ abstract class Base
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the armored output
|
* Toggle the armored output
|
||||||
|
* When true the output is ASCII
|
||||||
*/
|
*/
|
||||||
public function setArmor(int $armor = 1) : bool
|
public function setArmor(bool $armor = true) : bool
|
||||||
{
|
{
|
||||||
$this->armor = !!$armor;
|
$this->armor = $armor;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
namespace SnappyMail\GPG;
|
namespace SnappyMail\GPG;
|
||||||
|
|
||||||
class PGP extends Base
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
|
class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
{
|
{
|
||||||
private
|
private
|
||||||
$_message,
|
$_message,
|
||||||
|
|
@ -95,7 +97,7 @@ class PGP extends Base
|
||||||
$fclose = $this->setOutput($output);
|
$fclose = $this->setOutput($output);
|
||||||
|
|
||||||
if ($this->decryptKeys) {
|
if ($this->decryptKeys) {
|
||||||
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->decryptKeys);
|
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->decryptKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->exec(['--decrypt','--skip-verify']);
|
$result = $this->exec(['--decrypt','--skip-verify']);
|
||||||
|
|
@ -137,6 +139,7 @@ class PGP extends Base
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
// \rewind($fp);
|
||||||
return $this->_decrypt($fp, $output);
|
return $this->_decrypt($fp, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,6 +218,7 @@ class PGP extends Base
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
\rewind($fp);
|
||||||
return $this->_encrypt($fp, $output);
|
return $this->_encrypt($fp, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,14 +238,14 @@ class PGP extends Base
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _exportKey($keyId, $private = false)
|
protected function _exportKey($keyId, bool $private = false)
|
||||||
{
|
{
|
||||||
$keys = $this->keyInfo($keyId, $private ? 1 : 0);
|
$keys = $this->keyInfo($keyId, $private);
|
||||||
if (!$keys) {
|
if (!$keys) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
if ($private && $this->passphrases) {
|
if ($private && $this->passphrases) {
|
||||||
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->passphrases);
|
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->passphrases));
|
||||||
}
|
}
|
||||||
$result = $this->exec([
|
$result = $this->exec([
|
||||||
$private ? '--export-secret-keys' : '--export',
|
$private ? '--export-secret-keys' : '--export',
|
||||||
|
|
@ -252,21 +256,19 @@ class PGP extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports a public key
|
* Exports a public or private key
|
||||||
*/
|
*/
|
||||||
public function export(string $fingerprint) /*: string|false*/
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
|
// \SnappyMail\Log::debug('GnuPG', "export({$fingerprint}, {$passphrase})");
|
||||||
|
if (null !== $passphrase) {
|
||||||
|
return $this
|
||||||
|
->addPassphrase($fingerprint, $passphrase)
|
||||||
|
->_exportKey($fingerprint, true);
|
||||||
|
}
|
||||||
return $this->_exportKey($fingerprint);
|
return $this->_exportKey($fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a private key
|
|
||||||
*/
|
|
||||||
public function exportPrivateKey(string $fingerprint) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->_exportKey($fingerprint, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the errortext, if a function fails
|
* Returns the errortext, if a function fails
|
||||||
*/
|
*/
|
||||||
|
|
@ -296,8 +298,13 @@ class PGP extends Base
|
||||||
* Also saves revocation certificate in {homedir}/openpgp-revocs.d/
|
* Also saves revocation certificate in {homedir}/openpgp-revocs.d/
|
||||||
* https://www.gnupg.org/documentation/manuals/gnupg/OpenPGP-Key-Management.html
|
* https://www.gnupg.org/documentation/manuals/gnupg/OpenPGP-Key-Management.html
|
||||||
*/
|
*/
|
||||||
public function generateKey(PGPKeySettings $settings) /*: string|false*/
|
public function generateKey(string $uid, SensitiveString $passphrase) /*: string|false*/
|
||||||
{
|
{
|
||||||
|
$settings = new PGPKeySettings;
|
||||||
|
$settings->name = $uid;
|
||||||
|
$settings->email = $uid;
|
||||||
|
$settings->passphrase = $passphrase;
|
||||||
|
|
||||||
$arguments = [
|
$arguments = [
|
||||||
'--batch',
|
'--batch',
|
||||||
'--yes',
|
'--yes',
|
||||||
|
|
@ -362,7 +369,7 @@ class PGP extends Base
|
||||||
$arguments = ['--import'];
|
$arguments = ['--import'];
|
||||||
|
|
||||||
if ($this->passphrases) {
|
if ($this->passphrases) {
|
||||||
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->passphrases);
|
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->passphrases));
|
||||||
} else {
|
} else {
|
||||||
$arguments[] = '--batch';
|
$arguments[] = '--batch';
|
||||||
}
|
}
|
||||||
|
|
@ -421,13 +428,13 @@ class PGP extends Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteKey(string $keyId, bool $private)
|
public function deleteKey(string $keyId, bool $private) : bool
|
||||||
{
|
{
|
||||||
$key = $this->keyInfo($keyId, $private ? 1 : 0);
|
$key = $this->keyInfo($keyId, $private);
|
||||||
if (!$key) {
|
if (!$key) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
// if (!$private && $this->keyInfo($keyId, 1)) {
|
// if (!$private && $this->keyInfo($keyId, true)) {
|
||||||
// throw new \Exception('Delete private key first: ' . $keyId);
|
// throw new \Exception('Delete private key first: ' . $keyId);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
@ -448,7 +455,7 @@ class PGP extends Base
|
||||||
/**
|
/**
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
*/
|
*/
|
||||||
public function keyInfo(string $pattern, int $private = 0) : array
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
{
|
{
|
||||||
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
||||||
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
||||||
|
|
@ -572,6 +579,30 @@ class PGP extends Base
|
||||||
return $keys;
|
return $keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
|
*/
|
||||||
|
public function allKeysInfo(string $pattern) : array
|
||||||
|
{
|
||||||
|
$keys = [
|
||||||
|
'public' => [],
|
||||||
|
'private' => []
|
||||||
|
];
|
||||||
|
// Public
|
||||||
|
foreach (($this->keyinfo($pattern) ?: []) as $key) {
|
||||||
|
$key['can_verify'] = $key['can_sign'];
|
||||||
|
unset($key['can_sign']);
|
||||||
|
$keys['public'][] = $key;
|
||||||
|
}
|
||||||
|
// Private, read https://github.com/php-gnupg/php-gnupg/issues/5
|
||||||
|
foreach (($this->keyinfo($pattern, 1) ?: []) as $key) {
|
||||||
|
$key['can_decrypt'] = $key['can_encrypt'];
|
||||||
|
unset($key['can_encrypt']);
|
||||||
|
$keys['private'][] = $key;
|
||||||
|
}
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the mode for error_reporting
|
* Sets the mode for error_reporting
|
||||||
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
||||||
|
|
@ -629,7 +660,7 @@ class PGP extends Base
|
||||||
foreach ($this->signKeys as $fingerprint => $pass) {
|
foreach ($this->signKeys as $fingerprint => $pass) {
|
||||||
$arguments[] = '--local-user ' . \escapeshellarg($fingerprint);
|
$arguments[] = '--local-user ' . \escapeshellarg($fingerprint);
|
||||||
}
|
}
|
||||||
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->signKeys);
|
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->signKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->exec($arguments);
|
$result = $this->exec($arguments);
|
||||||
|
|
@ -666,15 +697,16 @@ class PGP extends Base
|
||||||
/**
|
/**
|
||||||
* Signs a given file
|
* Signs a given file
|
||||||
*/
|
*/
|
||||||
public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/
|
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
\rewind($fp);
|
||||||
return $this->_sign($fp, $output);
|
return $this->_sign($fp, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _verify($input, string $signature)
|
protected function _verify($input, string $signature) /*: array|false*/
|
||||||
{
|
{
|
||||||
$arguments = ['--verify'];
|
$arguments = ['--verify'];
|
||||||
if ($signature) {
|
if ($signature) {
|
||||||
|
|
@ -768,6 +800,7 @@ class PGP extends Base
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
// \rewind($fp);
|
||||||
return $this->_verify($fp, $signature);
|
return $this->_verify($fp, $signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace SnappyMail\GPG;
|
namespace SnappyMail\GPG;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
class SMIME extends Base
|
class SMIME extends Base
|
||||||
{
|
{
|
||||||
private
|
private
|
||||||
|
|
@ -235,9 +237,9 @@ class SMIME extends Base
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _exportKey($keyId, $private = false)
|
protected function _exportKey($keyId, bool $private = false)
|
||||||
{
|
{
|
||||||
$keys = $this->keyInfo($keyId, $private ? 1 : 0);
|
$keys = $this->keyInfo($keyId, $private);
|
||||||
if (!$keys) {
|
if (!$keys) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
|
|
@ -253,21 +255,18 @@ class SMIME extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports a public key
|
* Exports a public or private key
|
||||||
*/
|
*/
|
||||||
public function export(string $fingerprint) /*: string|false*/
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
|
if ($passphrase) {
|
||||||
|
return $this
|
||||||
|
->addPassphrase($fingerprint, $passphrase)
|
||||||
|
->_exportKey($fingerprint, true);
|
||||||
|
}
|
||||||
return $this->_exportKey($fingerprint);
|
return $this->_exportKey($fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a private key
|
|
||||||
*/
|
|
||||||
public function exportPrivateKey(string $fingerprint) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->_exportKey($fingerprint, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function _importKey($input) /*: array|false*/
|
protected function _importKey($input) /*: array|false*/
|
||||||
{
|
{
|
||||||
$arguments = ['--import'];
|
$arguments = ['--import'];
|
||||||
|
|
@ -334,7 +333,7 @@ class SMIME extends Base
|
||||||
|
|
||||||
public function deleteKey(string $keyId, bool $private)
|
public function deleteKey(string $keyId, bool $private)
|
||||||
{
|
{
|
||||||
$key = $this->keyInfo($keyId, $private ? 1 : 0);
|
$key = $this->keyInfo($keyId, $private);
|
||||||
if (!$key) {
|
if (!$key) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +358,7 @@ class SMIME extends Base
|
||||||
/**
|
/**
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
*/
|
*/
|
||||||
public function keyInfo(string $pattern, int $private = 0) : array
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
{
|
{
|
||||||
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
||||||
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
||||||
|
|
@ -540,7 +539,7 @@ class SMIME extends Base
|
||||||
/**
|
/**
|
||||||
* Signs a given file
|
* Signs a given file
|
||||||
*/
|
*/
|
||||||
public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/
|
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
|
|
@ -548,7 +547,7 @@ class SMIME extends Base
|
||||||
return $this->_sign($fp, $output);
|
return $this->_sign($fp, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _verify($input, string $signature)
|
protected function _verify($input, string $signature) /*: array|false*/
|
||||||
{
|
{
|
||||||
$arguments = ['--verify'];
|
$arguments = ['--verify'];
|
||||||
if ($signature) {
|
if ($signature) {
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@ abstract class Request
|
||||||
const
|
const
|
||||||
/**
|
/**
|
||||||
* Authentication
|
* Authentication
|
||||||
|
* These are bitwise options
|
||||||
*/
|
*/
|
||||||
AUTH_BASIC = 1,
|
AUTH_BASIC = 1,
|
||||||
AUTH_DIGEST = 2,
|
AUTH_DIGEST = 2,
|
||||||
AUTH_BEARER = 3;
|
AUTH_BEARER = 4;
|
||||||
|
|
||||||
public
|
public
|
||||||
$timeout = 5, // timeout in seconds.
|
$timeout = 5, // timeout in seconds.
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ class Response
|
||||||
if (\function_exists('gzinflate') && isset($this->headers['content-encoding'])
|
if (\function_exists('gzinflate') && isset($this->headers['content-encoding'])
|
||||||
&& (false !== \stripos($this->headers['content-encoding'], 'gzip'))) {
|
&& (false !== \stripos($this->headers['content-encoding'], 'gzip'))) {
|
||||||
$this->body = \gzinflate(\substr($body, 10, -4));
|
$this->body = \gzinflate(\substr($body, 10, -4));
|
||||||
|
if (false === $this->body) {
|
||||||
|
$err = \error_get_last() ?: ['message' => 'gzinflate failed'];
|
||||||
|
throw new \RuntimeException("{$err['message']} for {$request_uri}");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->body = $body;
|
$this->body = $body;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,423 +8,26 @@ defined('GNUPG_SIG_MODE_CLEAR') || define('GNUPG_SIG_MODE_CLEAR', 2);
|
||||||
|
|
||||||
use SnappyMail\GPG\PGP as GPG;
|
use SnappyMail\GPG\PGP as GPG;
|
||||||
|
|
||||||
class GnuPG
|
abstract class GnuPG
|
||||||
{
|
{
|
||||||
private
|
|
||||||
$homedir,
|
|
||||||
// Instance of gnupg pecl extension https://www.php.net/gnupg
|
|
||||||
$GnuPG,
|
|
||||||
// Instance of \SnappyMail\GPG\PGP
|
|
||||||
$GPG;
|
|
||||||
|
|
||||||
function __construct(string $homedir)
|
|
||||||
{
|
|
||||||
$homedir = \rtrim($homedir, '/\\');
|
|
||||||
// BSD 4.4 max length
|
|
||||||
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
|
||||||
throw new \Exception('socket name for S.gpg-agent.extra is too long');
|
|
||||||
}
|
|
||||||
$this->homedir = $homedir;
|
|
||||||
// \putenv("GNUPGHOME={$homedir}");
|
|
||||||
|
|
||||||
if (\class_exists('gnupg') && \version_compare(\phpversion('gnupg'), '1.5', '>=')) {
|
|
||||||
$this->GnuPG = new \gnupg([
|
|
||||||
// It is the file name of the executable program implementing this protocol which is usually path of the gpg executable.
|
|
||||||
// 'file_name' => '/usr/bin/gpg',
|
|
||||||
// It is the directory name of the configuration directory. It also overrides GNUPGHOME environment variable that is used for the same purpose.
|
|
||||||
'home_dir' => $homedir
|
|
||||||
]);
|
|
||||||
// Output is ASCII
|
|
||||||
$this->GnuPG->setarmor(1);
|
|
||||||
} else {
|
|
||||||
$this->getGPG();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
$conf = "{$homedir}/gpg-agent.conf";
|
|
||||||
if (!\file_exists($conf)) {
|
|
||||||
\file_put_contents($conf, 'default-cache-ttl 1');
|
|
||||||
}
|
|
||||||
$conf = "{$homedir}/gpg.conf";
|
|
||||||
if (!\file_exists($conf)) {
|
|
||||||
\file_put_contents($conf, "batch\nno-comments");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isSupported() : bool
|
public static function isSupported() : bool
|
||||||
{
|
{
|
||||||
return \class_exists('gnupg') || GPG::isSupported();
|
return GPG::isSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static $instance;
|
private static $instance;
|
||||||
public static function getInstance(string $homedir) : ?self
|
public static function getInstance(string $homedir) : ?PGPInterface
|
||||||
{
|
{
|
||||||
if (!static::$instance) {
|
if (!static::$instance) {
|
||||||
static::$instance = new self($homedir);
|
if (GPG::isSupported()) {
|
||||||
|
static::$instance = new GPG($homedir);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else if (PECL::isSupported()) {
|
||||||
|
static::$instance = new PECL($homedir);
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
return static::$instance;
|
return static::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handler()
|
|
||||||
{
|
|
||||||
return $this->GnuPG ?: $this->GPG;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGPG(bool $throw = true) : ?GPG
|
|
||||||
{
|
|
||||||
if (!$this->GPG) {
|
|
||||||
if (GPG::isSupported()) {
|
|
||||||
$this->GPG = new GPG($this->homedir);
|
|
||||||
} else if ($throw) {
|
|
||||||
throw new \Exception('GnuPG not supported');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->GPG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a key for decryption
|
|
||||||
*/
|
|
||||||
public function addDecryptKey(string $fingerprint,
|
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $passphrase
|
|
||||||
) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->adddecryptkey($fingerprint, $passphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a key for encryption
|
|
||||||
*/
|
|
||||||
public function addEncryptKey(string $fingerprint) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->addencryptkey($fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a key for signing
|
|
||||||
*/
|
|
||||||
public function addSignKey(string $fingerprint,
|
|
||||||
#[\SensitiveParameter]
|
|
||||||
?string $passphrase
|
|
||||||
) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->addsignkey($fingerprint, $passphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all keys which were set for decryption before
|
|
||||||
*/
|
|
||||||
public function clearDecryptKeys() : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->cleardecryptkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all keys which were set for encryption before
|
|
||||||
*/
|
|
||||||
public function clearEncryptKeys() : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->clearencryptkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all keys which were set for signing before
|
|
||||||
*/
|
|
||||||
public function clearSignKeys() : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->clearsignkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a given text
|
|
||||||
*/
|
|
||||||
public function decrypt(string $text) /*: string|false */
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->decrypt($text)
|
|
||||||
: $this->GPG->decrypt($text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a given file
|
|
||||||
*/
|
|
||||||
public function decryptFile(string $filename) /*: string|false */
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->decrypt(\file_get_contents($filename))
|
|
||||||
: $this->GPG->decryptFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a given resource
|
|
||||||
*/
|
|
||||||
public function decryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false */
|
|
||||||
{
|
|
||||||
if (!$fp || !\is_resource($fp)) {
|
|
||||||
throw new \Exception('Invalid stream resource');
|
|
||||||
}
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->decrypt(\stream_get_contents($fp))
|
|
||||||
: $this->GPG->decryptStream($fp, $output);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts and verifies a given text
|
|
||||||
*/
|
|
||||||
public function decryptVerify(string $text, string &$plaintext) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->decryptverify($text, $plaintext)
|
|
||||||
: $this->GPG->decryptverify($text, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts and verifies a given file
|
|
||||||
*/
|
|
||||||
public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->decryptverify(\file_get_contents($filename), $plaintext)
|
|
||||||
: $this->GPG->decryptverifyFile($filename, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteKey(string $keyId, bool $private) : bool
|
|
||||||
{
|
|
||||||
return $this->getGPG()->deleteKey($keyId, $private);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a given text
|
|
||||||
*/
|
|
||||||
public function encrypt(string $plaintext) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->encrypt($plaintext)
|
|
||||||
: $this->GPG->encrypt($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a given text
|
|
||||||
*/
|
|
||||||
public function encryptFile(string $filename) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->encrypt(\file_get_contents($filename))
|
|
||||||
: $this->GPG->encryptFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function encryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false*/
|
|
||||||
{
|
|
||||||
\rewind($fp);
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->encrypt(\stream_get_contents($fp))
|
|
||||||
: $this->GPG->encryptStream($fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a key
|
|
||||||
*/
|
|
||||||
public function export(string $fingerprint,
|
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $passphrase = ''
|
|
||||||
) /*: string|false*/
|
|
||||||
{
|
|
||||||
if ($passphrase) {
|
|
||||||
return $this->getGPG()
|
|
||||||
->addPassphrase($fingerprint, $passphrase)
|
|
||||||
->exportPrivateKey($fingerprint);
|
|
||||||
}
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->export($fingerprint)
|
|
||||||
: $this->GPG->export($fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the engine info
|
|
||||||
*/
|
|
||||||
public function getEngineInfo() : array
|
|
||||||
{
|
|
||||||
return $this->handler()->getengineinfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the errortext, if a function fails
|
|
||||||
*/
|
|
||||||
public function getError() /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->handler()->geterror();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the error info
|
|
||||||
*/
|
|
||||||
public function getErrorInfo() : array
|
|
||||||
{
|
|
||||||
return $this->handler()->geterrorinfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently active protocol for all operations
|
|
||||||
*/
|
|
||||||
public function getProtocol() : int
|
|
||||||
{
|
|
||||||
return $this->handler()->getprotocol();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a key
|
|
||||||
*/
|
|
||||||
public function generateKey(string $uid,
|
|
||||||
#[\SensitiveParameter]
|
|
||||||
string $passphrase
|
|
||||||
) /*: string|false*/
|
|
||||||
{
|
|
||||||
$GPG = $this->getGPG(false);
|
|
||||||
return $GPG ? $GPG->generateKey($uid, $passphrase) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports a key
|
|
||||||
*/
|
|
||||||
public function import(string $keydata) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->handler()->import($keydata);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports a key
|
|
||||||
*/
|
|
||||||
public function importFile(string $filename) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->import(\file_get_contents($filename))
|
|
||||||
: $this->GPG->importFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
|
||||||
*/
|
|
||||||
public function keyInfo(string $pattern) : array
|
|
||||||
{
|
|
||||||
$keys = [
|
|
||||||
'public' => [],
|
|
||||||
'private' => []
|
|
||||||
];
|
|
||||||
// Public
|
|
||||||
foreach (($this->handler()->keyinfo($pattern) ?: []) as $key) {
|
|
||||||
$key['can_verify'] = $key['can_sign'];
|
|
||||||
unset($key['can_sign']);
|
|
||||||
$keys['public'][] = $key;
|
|
||||||
}
|
|
||||||
// Private, read https://github.com/php-gnupg/php-gnupg/issues/5
|
|
||||||
foreach (($this->handler()->keyinfo($pattern, 1) ?: []) as $key) {
|
|
||||||
$key['can_decrypt'] = $key['can_encrypt'];
|
|
||||||
unset($key['can_encrypt']);
|
|
||||||
$keys['private'][] = $key;
|
|
||||||
}
|
|
||||||
return $keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle armored output
|
|
||||||
* When true the output is ASCII
|
|
||||||
*/
|
|
||||||
public function setArmor(bool $armor = true) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->setarmor($armor ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the mode for error_reporting
|
|
||||||
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
|
||||||
* By default GNUPG_ERROR_SILENT is used.
|
|
||||||
*/
|
|
||||||
public function setErrorMode(int $errormode) : void
|
|
||||||
{
|
|
||||||
$this->handler()->seterrormode($errormode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the mode for signing
|
|
||||||
* GNUPG_SIG_MODE_NORMAL, GNUPG_SIG_MODE_DETACH and GNUPG_SIG_MODE_CLEAR.
|
|
||||||
* By default GNUPG_SIG_MODE_CLEAR
|
|
||||||
*/
|
|
||||||
public function setSignMode(int $signmode) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->setsignmode($signmode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs a given text
|
|
||||||
*/
|
|
||||||
public function sign(string $plaintext) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->sign($plaintext)
|
|
||||||
: $this->GPG->sign($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs a given file
|
|
||||||
*/
|
|
||||||
public function signFile(string $filename) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->sign(\file_get_contents($filename))
|
|
||||||
: $this->GPG->signFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs a given file
|
|
||||||
*/
|
|
||||||
public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/
|
|
||||||
{
|
|
||||||
\rewind($fp);
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->sign(\stream_get_contents($fp))
|
|
||||||
: $this->GPG->signStream($fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies a signed text
|
|
||||||
*/
|
|
||||||
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/
|
|
||||||
{
|
|
||||||
$result = $this->GnuPG
|
|
||||||
? $this->GnuPG->verify($signed_text, $signature ?: false, $plaintext)
|
|
||||||
: $this->GPG->verify($signed_text, $signature, $plaintext);
|
|
||||||
if (!$result) {
|
|
||||||
if ($this->GnuPG) {
|
|
||||||
\SnappyMail\Log::notice('GnuPG', 'gnupg_verify() failed: ' . $this->GnuPG->geterror());
|
|
||||||
\SnappyMail\Log::info('GnuPG', \print_r($this->GnuPG->geterrorinfo(),1));
|
|
||||||
} else {
|
|
||||||
\SnappyMail\Log::notice('GPG', 'GPG->verify() failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies a signed file
|
|
||||||
*/
|
|
||||||
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext)
|
|
||||||
: $this->GPG->verifyFile($filename, $signature, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies a given resource
|
|
||||||
*/
|
|
||||||
public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: string|false */
|
|
||||||
{
|
|
||||||
if (!$fp || !\is_resource($fp)) {
|
|
||||||
throw new \Exception('Invalid stream resource');
|
|
||||||
}
|
|
||||||
return $this->getGPG()->verifyStream($fp, $signature, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
397
snappymail/v/0.0.0/app/libraries/snappymail/pgp/pecl.php
Normal file
397
snappymail/v/0.0.0/app/libraries/snappymail/pgp/pecl.php
Normal file
|
|
@ -0,0 +1,397 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SnappyMail\PGP;
|
||||||
|
|
||||||
|
defined('GNUPG_SIG_MODE_NORMAL') || define('GNUPG_SIG_MODE_NORMAL', 0);
|
||||||
|
defined('GNUPG_SIG_MODE_DETACH') || define('GNUPG_SIG_MODE_DETACH', 1);
|
||||||
|
defined('GNUPG_SIG_MODE_CLEAR') || define('GNUPG_SIG_MODE_CLEAR', 2);
|
||||||
|
|
||||||
|
use SnappyMail\GPG\PGP as GPG;
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
|
class PECL implements \SnappyMail\PGP\PGPInterface
|
||||||
|
{
|
||||||
|
private
|
||||||
|
$homedir,
|
||||||
|
// Instance of gnupg pecl extension https://www.php.net/gnupg
|
||||||
|
$GnuPG,
|
||||||
|
// Instance of \SnappyMail\GPG\PGP
|
||||||
|
$GPG;
|
||||||
|
|
||||||
|
function __construct(string $homedir)
|
||||||
|
{
|
||||||
|
$homedir = \rtrim($homedir, '/\\');
|
||||||
|
// BSD 4.4 max length
|
||||||
|
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
||||||
|
throw new \Exception('Socket name for S.gpg-agent.extra is too long');
|
||||||
|
}
|
||||||
|
$this->homedir = $homedir;
|
||||||
|
// \putenv("GNUPGHOME={$homedir}");
|
||||||
|
|
||||||
|
$this->GnuPG = new \gnupg([
|
||||||
|
// It is the file name of the executable program implementing this protocol which is usually path of the gpg executable.
|
||||||
|
// 'file_name' => '/usr/bin/gpg',
|
||||||
|
// It is the directory name of the configuration directory. It also overrides GNUPGHOME environment variable that is used for the same purpose.
|
||||||
|
'home_dir' => $homedir
|
||||||
|
]);
|
||||||
|
// Output is ASCII
|
||||||
|
$this->GnuPG->setarmor(1);
|
||||||
|
/*
|
||||||
|
$conf = "{$homedir}/gpg-agent.conf";
|
||||||
|
if (!\file_exists($conf)) {
|
||||||
|
\file_put_contents($conf, 'default-cache-ttl 1');
|
||||||
|
}
|
||||||
|
$conf = "{$homedir}/gpg.conf";
|
||||||
|
if (!\file_exists($conf)) {
|
||||||
|
\file_put_contents($conf, "batch\nno-comments");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function __destruct()
|
||||||
|
{
|
||||||
|
$this->GnuPG->cleardecryptkeys();
|
||||||
|
$this->GnuPG->clearsignkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSupported() : bool
|
||||||
|
{
|
||||||
|
return \class_exists('gnupg') && \version_compare(\phpversion('gnupg'), '1.5', '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gnupgError()
|
||||||
|
{
|
||||||
|
$error = $this->GnuPG->geterrorinfo();
|
||||||
|
if ($error) {
|
||||||
|
throw new \Exception("{$error['gpgme_source']} {$error['generic_message']}. {$error['gpgme_message']}", $error['gpgme_code']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGPG(bool $throw = true) : ?GPG
|
||||||
|
{
|
||||||
|
if (!$this->GPG) {
|
||||||
|
if (GPG::isSupported()) {
|
||||||
|
$this->GPG = new GPG($this->homedir);
|
||||||
|
} else if ($throw) {
|
||||||
|
throw new \Exception('GPG not supported');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->GPG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for decryption
|
||||||
|
*/
|
||||||
|
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
|
{
|
||||||
|
// \SnappyMail\Log::debug('GnuPG', "addDecryptKey({$fingerprint}, {$passphrase})");
|
||||||
|
return $this->GnuPG->adddecryptkey($fingerprint, \strval($passphrase)) || $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for encryption
|
||||||
|
*/
|
||||||
|
public function addEncryptKey(string $fingerprint) : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->addencryptkey($fingerprint) || $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for signing
|
||||||
|
*/
|
||||||
|
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
|
{
|
||||||
|
// \SnappyMail\Log::debug('GnuPG', "addSignKey({$fingerprint}, {$passphrase})");
|
||||||
|
return $this->GnuPG->addsignkey($fingerprint, \strval($passphrase)) || $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all keys which were set for decryption before
|
||||||
|
*/
|
||||||
|
public function clearDecryptKeys() : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->cleardecryptkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all keys which were set for encryption before
|
||||||
|
*/
|
||||||
|
public function clearEncryptKeys() : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->clearencryptkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all keys which were set for signing before
|
||||||
|
*/
|
||||||
|
public function clearSignKeys() : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->clearsignkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts a given text
|
||||||
|
*/
|
||||||
|
public function decrypt(string $text) /*: string|false */
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decrypt($text);
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts a given file
|
||||||
|
*/
|
||||||
|
public function decryptFile(string $filename) /*: string|false */
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decrypt(\file_get_contents($filename));
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts a given resource
|
||||||
|
*/
|
||||||
|
public function decryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false */
|
||||||
|
{
|
||||||
|
if (!$fp || !\is_resource($fp)) {
|
||||||
|
throw new \Exception('Invalid stream resource');
|
||||||
|
}
|
||||||
|
// \rewind($fp);
|
||||||
|
$result = $this->GnuPG->decrypt(\stream_get_contents($fp));
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts and verifies a given text
|
||||||
|
*/
|
||||||
|
public function decryptVerify(string $text, string &$plaintext) /*: array|false*/
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decryptverify($text, $plaintext);
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts and verifies a given file
|
||||||
|
*/
|
||||||
|
public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decryptverify(\file_get_contents($filename), $plaintext);
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteKey(string $keyId, bool $private) : bool
|
||||||
|
{
|
||||||
|
return $this->getGPG()->deleteKey($keyId, $private);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a given text
|
||||||
|
*/
|
||||||
|
public function encrypt(string $plaintext) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->encrypt($plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a given text
|
||||||
|
*/
|
||||||
|
public function encryptFile(string $filename) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->encrypt(\file_get_contents($filename)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false*/
|
||||||
|
{
|
||||||
|
\rewind($fp);
|
||||||
|
return $this->GnuPG->encrypt(\stream_get_contents($fp)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports a key
|
||||||
|
*/
|
||||||
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
|
{
|
||||||
|
if (null !== $passphrase) {
|
||||||
|
return $this->getGPG()->export($fingerprint, $passphrase);
|
||||||
|
}
|
||||||
|
return $this->GnuPG->export($fingerprint) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the engine info
|
||||||
|
*/
|
||||||
|
public function getEngineInfo() : array
|
||||||
|
{
|
||||||
|
return $this->GnuPG->getengineinfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the errortext, if a function fails
|
||||||
|
*/
|
||||||
|
public function getError() /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->geterror();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error info
|
||||||
|
*/
|
||||||
|
public function getErrorInfo() : array
|
||||||
|
{
|
||||||
|
return $this->GnuPG->geterrorinfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently active protocol for all operations
|
||||||
|
*/
|
||||||
|
public function getProtocol() : int
|
||||||
|
{
|
||||||
|
return $this->GnuPG->getprotocol();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a key
|
||||||
|
*/
|
||||||
|
public function generateKey(string $uid, SensitiveString $passphrase) /*: string|false*/
|
||||||
|
{
|
||||||
|
$GPG = $this->getGPG(false);
|
||||||
|
return $GPG ? $GPG->generateKey($uid, $passphrase) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a key
|
||||||
|
*/
|
||||||
|
public function import(string $keydata) /*: array|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->import($keydata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a key
|
||||||
|
*/
|
||||||
|
public function importFile(string $filename) /*: array|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->import(\file_get_contents($filename)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
|
{
|
||||||
|
return $this->GnuPG->keyinfo($pattern, $private ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
|
*/
|
||||||
|
public function allKeysInfo(string $pattern) : array
|
||||||
|
{
|
||||||
|
$keys = [
|
||||||
|
'public' => [],
|
||||||
|
'private' => []
|
||||||
|
];
|
||||||
|
// Public
|
||||||
|
foreach (($this->GnuPG->keyinfo($pattern) ?: []) as $key) {
|
||||||
|
$key['can_verify'] = $key['can_sign'];
|
||||||
|
unset($key['can_sign']);
|
||||||
|
$keys['public'][] = $key;
|
||||||
|
}
|
||||||
|
// Private, read https://github.com/php-gnupg/php-gnupg/issues/5
|
||||||
|
foreach (($this->GnuPG->keyinfo($pattern, 1) ?: []) as $key) {
|
||||||
|
$key['can_decrypt'] = $key['can_encrypt'];
|
||||||
|
unset($key['can_encrypt']);
|
||||||
|
$keys['private'][] = $key;
|
||||||
|
}
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle armored output
|
||||||
|
* When true the output is ASCII
|
||||||
|
*/
|
||||||
|
public function setArmor(bool $armor = true) : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->setarmor($armor ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mode for error_reporting
|
||||||
|
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
||||||
|
* By default GNUPG_ERROR_SILENT is used.
|
||||||
|
*/
|
||||||
|
public function setErrorMode(int $errormode) : void
|
||||||
|
{
|
||||||
|
$this->GnuPG->seterrormode($errormode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mode for signing
|
||||||
|
* GNUPG_SIG_MODE_NORMAL, GNUPG_SIG_MODE_DETACH and GNUPG_SIG_MODE_CLEAR.
|
||||||
|
* By default GNUPG_SIG_MODE_CLEAR
|
||||||
|
*/
|
||||||
|
public function setSignMode(int $signmode) : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->setsignmode($signmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a given text
|
||||||
|
*/
|
||||||
|
public function sign(string $plaintext) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->sign($plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a given file
|
||||||
|
*/
|
||||||
|
public function signFile(string $filename) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->sign(\file_get_contents($filename)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a given file
|
||||||
|
*/
|
||||||
|
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/
|
||||||
|
{
|
||||||
|
\rewind($fp);
|
||||||
|
return $this->GnuPG->sign(\stream_get_contents($fp)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a signed text
|
||||||
|
*/
|
||||||
|
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->verify($signed_text, $signature ?: false, $plaintext) ?: $this->gnupgError();
|
||||||
|
if (!$result) {
|
||||||
|
\SnappyMail\Log::notice('GnuPG', 'gnupg_verify() failed: ' . $this->GnuPG->geterror());
|
||||||
|
\SnappyMail\Log::info('GnuPG', \print_r($this->GnuPG->geterrorinfo(),1));
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a signed file
|
||||||
|
*/
|
||||||
|
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a given resource
|
||||||
|
*/
|
||||||
|
public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: array|false */
|
||||||
|
{
|
||||||
|
if (!$fp || !\is_resource($fp)) {
|
||||||
|
throw new \Exception('Invalid stream resource');
|
||||||
|
}
|
||||||
|
// \rewind($fp);
|
||||||
|
return $this->GnuPG->verify(\stream_get_contents($fp), $signature, $plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SnappyMail\PGP;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
|
interface PGPInterface
|
||||||
|
{
|
||||||
|
public static function isSupported() : bool;
|
||||||
|
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool;
|
||||||
|
public function addEncryptKey(string $fingerprint) : bool;
|
||||||
|
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool;
|
||||||
|
public function clearDecryptKeys() : bool;
|
||||||
|
public function clearEncryptKeys() : bool;
|
||||||
|
public function clearSignKeys() : bool;
|
||||||
|
public function decrypt(string $text) /*: string|false */;
|
||||||
|
public function decryptFile(string $filename) /*: string|false */;
|
||||||
|
public function decryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false */;
|
||||||
|
public function decryptVerify(string $text, string &$plaintext) /*: array|false*/;
|
||||||
|
public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/;
|
||||||
|
public function deleteKey(string $keyId, bool $private) : bool;
|
||||||
|
public function encrypt(string $plaintext) /*: string|false*/;
|
||||||
|
public function encryptFile(string $filename) /*: string|false*/;
|
||||||
|
public function encryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false*/;
|
||||||
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/;
|
||||||
|
public function getEngineInfo() : array;
|
||||||
|
public function getError() /*: string|false*/;
|
||||||
|
public function getErrorInfo() : array;
|
||||||
|
public function getProtocol() : int;
|
||||||
|
public function generateKey(string $uid, SensitiveString $passphrase) /*: string|false*/;
|
||||||
|
public function import(string $keydata) /*: array|false*/;
|
||||||
|
public function importFile(string $filename) /*: array|false*/;
|
||||||
|
public function allKeysInfo(string $pattern) : array;
|
||||||
|
public function setArmor(bool $armor = true) : bool;
|
||||||
|
public function setErrorMode(int $errormode) : void;
|
||||||
|
public function setSignMode(int $signmode) : bool;
|
||||||
|
public function sign(string $plaintext) /*: string|false*/;
|
||||||
|
public function signFile(string $filename) /*: string|false*/;
|
||||||
|
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/;
|
||||||
|
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/;
|
||||||
|
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/;
|
||||||
|
public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: array|false */;
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace SnappyMail;
|
namespace SnappyMail;
|
||||||
|
|
||||||
class SensitiveString /* extends SensitiveParameterValue | SensitiveParameter */ implements \Stringable
|
class SensitiveString /* extends SensitiveParameterValue | SensitiveParameter */ implements \Stringable, \JsonSerializable
|
||||||
{
|
{
|
||||||
private string $value, $nonce;
|
private string $value, $nonce;
|
||||||
private static ?string $key = null;
|
private static ?string $key = null;
|
||||||
|
|
@ -28,6 +28,7 @@ class SensitiveString /* extends SensitiveParameterValue | SensitiveParameter */
|
||||||
string $value
|
string $value
|
||||||
) : void
|
) : void
|
||||||
{
|
{
|
||||||
|
\strlen($value) && \RainLoop\Api::Actions()->logMask($value);
|
||||||
if (\is_callable('sodium_crypto_secretbox')) {
|
if (\is_callable('sodium_crypto_secretbox')) {
|
||||||
$this->nonce = \random_bytes(\SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
$this->nonce = \random_bytes(\SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||||
if (!static::$key) {
|
if (!static::$key) {
|
||||||
|
|
@ -60,6 +61,12 @@ class SensitiveString /* extends SensitiveParameterValue | SensitiveParameter */
|
||||||
return $this->getValue();
|
return $this->getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
throw new \Exception("JSON serialization of 'SnappyMail\\SensitiveString' is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
public function __debugInfo(): array
|
public function __debugInfo(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ class Certificate
|
||||||
return \openssl_get_cipher_methods($aliases);
|
return \openssl_get_cipher_methods($aliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createSelfSigned(string $passphrase = '') : array
|
public function createSelfSigned(\SnappyMail\SensitiveString $passphrase, ?string $privateKey = null) : array
|
||||||
{
|
{
|
||||||
$options = array(
|
$options = array(
|
||||||
'config' => __DIR__ . '/openssl.cnf',
|
'config' => __DIR__ . '/openssl.cnf',
|
||||||
|
|
@ -134,7 +134,13 @@ class Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
$pkey = null; // openssl_pkey_new($options);
|
$pkey = null; // openssl_pkey_new($options);
|
||||||
$csr = \openssl_csr_new($dn, $pkey, $options);
|
if ($privateKey) {
|
||||||
|
$pkey = \openssl_pkey_get_private($privateKey, $passphrase);
|
||||||
|
if (!$pkey) {
|
||||||
|
throw new \RuntimeException('OpenSSL pkey: ' . \openssl_error_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$csr = \openssl_csr_new($dn, $pkey, $options);
|
||||||
if ($csr) {
|
if ($csr) {
|
||||||
$this->x509 = \openssl_csr_sign(
|
$this->x509 = \openssl_csr_sign(
|
||||||
$csr,
|
$csr,
|
||||||
|
|
@ -145,13 +151,11 @@ class Certificate
|
||||||
);
|
);
|
||||||
if ($this->x509/* && $this->canSign() && $this->canEncrypt()*/) {
|
if ($this->x509/* && $this->canSign() && $this->canEncrypt()*/) {
|
||||||
$this->pkey = $pkey;
|
$this->pkey = $pkey;
|
||||||
$privatekey = '';
|
$privateKey || \openssl_pkey_export($pkey, $privateKey, $passphrase);
|
||||||
$certificate = '';
|
$certificate = '';
|
||||||
$csrStr = '';
|
|
||||||
\openssl_pkey_export($pkey, $privatekey, $passphrase);
|
|
||||||
\openssl_x509_export($this->x509, $certificate);
|
\openssl_x509_export($this->x509, $certificate);
|
||||||
return array(
|
return array(
|
||||||
'pkey' => $privatekey,
|
'pkey' => $privateKey,
|
||||||
'x509' => $certificate,
|
'x509' => $certificate,
|
||||||
// 'pkcs12' => $this->asPKCS12($pkey, $passphrase/*, array $args = array()*/)
|
// 'pkcs12' => $this->asPKCS12($pkey, $passphrase/*, array $args = array()*/)
|
||||||
// 'canSign' => $this->canSign(),
|
// 'canSign' => $this->canSign(),
|
||||||
|
|
@ -167,7 +171,11 @@ class Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns binary data
|
// returns binary data
|
||||||
public function asPKCS12(string $pass = '', array $args = array()) : string
|
public function asPKCS12(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $pass = '',
|
||||||
|
array $args = array()
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
$out = '';
|
$out = '';
|
||||||
\openssl_pkcs12_export($this->x509, $out, $this->pkey, $pass, $args);
|
\openssl_pkcs12_export($this->x509, $out, $this->pkey, $pass, $args);
|
||||||
|
|
|
||||||
|
|
@ -34,15 +34,18 @@ class OpenSSL
|
||||||
{
|
{
|
||||||
$data = \openssl_x509_parse(\openssl_x509_read($certificate));
|
$data = \openssl_x509_parse(\openssl_x509_read($certificate));
|
||||||
if (!$data) {
|
if (!$data) {
|
||||||
\error_log("OpenSSL parse: " . \openssl_error_string());
|
\SnappyMail\Log::error('OpenSSL', "parse: " . \openssl_error_string());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$key = \str_replace(':', '', $data['extensions']['subjectKeyIdentifier'] ?? $data['hash']);
|
$key = \str_replace(':', '', $data['extensions']['subjectKeyIdentifier'] ?? $data['hash']);
|
||||||
$filename = "{$this->homedir}/{$key}.crt";
|
$filename = "{$this->homedir}/{$key}.crt";
|
||||||
if (!\file_exists($filename)) {
|
if (\file_exists($filename)) {
|
||||||
|
\SnappyMail\Log::debug('OpenSSL', "certificate {$key} already imported");
|
||||||
|
} else {
|
||||||
\file_put_contents("{$this->homedir}/{$key}.crt", $certificate);
|
\file_put_contents("{$this->homedir}/{$key}.crt", $certificate);
|
||||||
// \unlink("{$this->homedir}/certificates.json");
|
// \unlink("{$this->homedir}/certificates.json");
|
||||||
$this->certificates(true);
|
$this->certificates(true);
|
||||||
|
\SnappyMail\Log::debug('OpenSSL', "certificate {$key} imported");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +53,7 @@ class OpenSSL
|
||||||
public function certificates(bool $force = false) : array
|
public function certificates(bool $force = false) : array
|
||||||
{
|
{
|
||||||
$cacheFile = "{$this->homedir}/certificates.json";
|
$cacheFile = "{$this->homedir}/certificates.json";
|
||||||
$result = \file_exists($cacheFile)
|
$result = (!$force && \file_exists($cacheFile))
|
||||||
? \json_decode(\file_get_contents($cacheFile), true)
|
? \json_decode(\file_get_contents($cacheFile), true)
|
||||||
: null;
|
: null;
|
||||||
if (!\is_array($result)) {
|
if (!\is_array($result)) {
|
||||||
|
|
@ -98,7 +101,7 @@ class OpenSSL
|
||||||
}
|
}
|
||||||
$result[] = $short;
|
$result[] = $short;
|
||||||
} else {
|
} else {
|
||||||
\error_log("OpenSSL parse({$file}): " . \openssl_error_string());
|
\SnappyMail\Log::error('OpenSSL', "parse({$file}): " . \openssl_error_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
\file_put_contents($cacheFile, \json_encode($result));
|
\file_put_contents($cacheFile, \json_encode($result));
|
||||||
|
|
@ -129,8 +132,7 @@ class OpenSSL
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPrivateKey(/*OpenSSLAsymmetricKey|string*/$privateKey,
|
public function setPrivateKey(/*OpenSSLAsymmetricKey|string*/$privateKey,
|
||||||
#[\SensitiveParameter]
|
?\SnappyMail\SensitiveString $passphrase = null
|
||||||
?string $passphrase = null
|
|
||||||
) : void
|
) : void
|
||||||
{
|
{
|
||||||
$this->privateKey = \openssl_pkey_get_private($privateKey, $passphrase);
|
$this->privateKey = \openssl_pkey_get_private($privateKey, $passphrase);
|
||||||
|
|
@ -142,6 +144,19 @@ class OpenSSL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function asn1parse(/*string|Temporary* / $input) : ?string
|
||||||
|
{
|
||||||
|
if (\is_string($input)) {
|
||||||
|
$tmp = new Temporary('smimein-');
|
||||||
|
if (!$tmp->putContents($input)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$input = $tmp;
|
||||||
|
}
|
||||||
|
`openssl asn1parse -in $input->filename()`
|
||||||
|
}
|
||||||
|
*/
|
||||||
public function decrypt(/*string|Temporary*/ $input) : ?string
|
public function decrypt(/*string|Temporary*/ $input) : ?string
|
||||||
{
|
{
|
||||||
if (\is_string($input)) {
|
if (\is_string($input)) {
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "توقيع جيد من %USER%",
|
"GOOD_SIGNATURE": "توقيع جيد من %USER%",
|
||||||
"ERROR": "%TYPE% error: %ERROR%",
|
"ERROR": "%TYPE% error: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "OpenPGP استيراد مفتاح",
|
"POPUP_IMPORT_TITLE": "OpenPGP استيراد مفتاح",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME signed message",
|
"SIGNED_MESSAGE": "S\/MIME signed message",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Валиден подпис за %USER%",
|
"GOOD_SIGNATURE": "Валиден подпис за %USER%",
|
||||||
"ERROR": "%TYPE% грешка: %ERROR%",
|
"ERROR": "%TYPE% грешка: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Внасяне на OpenPGP ключ",
|
"POPUP_IMPORT_TITLE": "Внасяне на OpenPGP ключ",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Подписано с S\/MIME",
|
"SIGNED_MESSAGE": "Подписано с S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Шифровано с S\/MIME",
|
"ENCRYPTED_MESSAGE": "Шифровано с S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Ověřený podpis od %USER%",
|
"GOOD_SIGNATURE": "Ověřený podpis od %USER%",
|
||||||
"ERROR": "%TYPE% chyba: %ERROR%",
|
"ERROR": "%TYPE% chyba: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Import OpenPGP klíče",
|
"POPUP_IMPORT_TITLE": "Import OpenPGP klíče",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Zpráva podepsaná S\/MIME",
|
"SIGNED_MESSAGE": "Zpráva podepsaná S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Zpráva šifrovaná S\/MIME",
|
"ENCRYPTED_MESSAGE": "Zpráva šifrovaná S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Accepteret signatur for %USER\"",
|
"GOOD_SIGNATURE": "Accepteret signatur for %USER\"",
|
||||||
"ERROR": "%TYPE% fejl: %ERROR%",
|
"ERROR": "%TYPE% fejl: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importer OpenPGP nøgle",
|
"POPUP_IMPORT_TITLE": "Importer OpenPGP nøgle",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME signeret meddelelse",
|
"SIGNED_MESSAGE": "S\/MIME signeret meddelelse",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME krypteret meddelelse",
|
"ENCRYPTED_MESSAGE": "S\/MIME krypteret meddelelse",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Gültige Unterschrift von %USER%",
|
"GOOD_SIGNATURE": "Gültige Unterschrift von %USER%",
|
||||||
"ERROR": "%TYPE%-Fehler: %ERROR%",
|
"ERROR": "%TYPE%-Fehler: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "OpenPGP-Schlüssel importieren",
|
"POPUP_IMPORT_TITLE": "OpenPGP-Schlüssel importieren",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME-signierte Nachricht",
|
"SIGNED_MESSAGE": "S\/MIME-signierte Nachricht",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME-verschlüsselte Nachricht",
|
"ENCRYPTED_MESSAGE": "S\/MIME-verschlüsselte Nachricht",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Good signature from %USER%",
|
"GOOD_SIGNATURE": "Good signature from %USER%",
|
||||||
"ERROR": "%TYPE% error: %ERROR%",
|
"ERROR": "%TYPE% error: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Εισαγωγή κλειδιού OpenPGP",
|
"POPUP_IMPORT_TITLE": "Εισαγωγή κλειδιού OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Μήνυμα υπογεγραμμένο με S\/MIME",
|
"SIGNED_MESSAGE": "Μήνυμα υπογεγραμμένο με S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Μήνυμα κωδικοποιημένο με S\/MIME",
|
"ENCRYPTED_MESSAGE": "Μήνυμα κωδικοποιημένο με S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Good signature from %USER%",
|
"GOOD_SIGNATURE": "Good signature from %USER%",
|
||||||
"ERROR": "%TYPE% error: %ERROR%",
|
"ERROR": "%TYPE% error: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME signed message",
|
"SIGNED_MESSAGE": "S\/MIME signed message",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Good signature from %USER%",
|
"GOOD_SIGNATURE": "Good signature from %USER%",
|
||||||
"ERROR": "%TYPE% error: %ERROR%",
|
"ERROR": "%TYPE% error: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S/MIME Certificates",
|
"CERTIFICATES": "S/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S/MIME signed message",
|
"SIGNED_MESSAGE": "S/MIME signed message",
|
||||||
"ENCRYPTED_MESSAGE": "S/MIME encrypted message",
|
"ENCRYPTED_MESSAGE": "S/MIME encrypted message",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Buena firma de %USER%",
|
"GOOD_SIGNATURE": "Buena firma de %USER%",
|
||||||
"ERROR": "Error %TYPE%: %ERROR%",
|
"ERROR": "Error %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importar clave OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importar clave OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Mensaje firmado mediante S\/MIME",
|
"SIGNED_MESSAGE": "Mensaje firmado mediante S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Mensaje cifrado mediante S\/MIME",
|
"ENCRYPTED_MESSAGE": "Mensaje cifrado mediante S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Korralik signatuur kasutajalt %USER%",
|
"GOOD_SIGNATURE": "Korralik signatuur kasutajalt %USER%",
|
||||||
"ERROR": "%TYPE% viga: %ERROR%",
|
"ERROR": "%TYPE% viga: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Impordi OpenPGP võti",
|
"POPUP_IMPORT_TITLE": "Impordi OpenPGP võti",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME abil signeeritud kiri",
|
"SIGNED_MESSAGE": "S\/MIME abil signeeritud kiri",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME abil krüpteeritud kiri",
|
"ENCRYPTED_MESSAGE": "S\/MIME abil krüpteeritud kiri",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "%USER%-en sinadura egokia",
|
"GOOD_SIGNATURE": "%USER%-en sinadura egokia",
|
||||||
"ERROR": "%TYPE% errorea: %ERROR%",
|
"ERROR": "%TYPE% errorea: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Inportatu OpenPGP gakoa",
|
"POPUP_IMPORT_TITLE": "Inportatu OpenPGP gakoa",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME sinatutako mezua",
|
"SIGNED_MESSAGE": "S\/MIME sinatutako mezua",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME zifratutako mezua",
|
"ENCRYPTED_MESSAGE": "S\/MIME zifratutako mezua",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "امضای صحیح از %USER%",
|
"GOOD_SIGNATURE": "امضای صحیح از %USER%",
|
||||||
"ERROR": "خطای %TYPE%: %ERROR%",
|
"ERROR": "خطای %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "واردکردن کلید OpenPGP",
|
"POPUP_IMPORT_TITLE": "واردکردن کلید OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "پیام توسط S\/MIME امضاء شد",
|
"SIGNED_MESSAGE": "پیام توسط S\/MIME امضاء شد",
|
||||||
"ENCRYPTED_MESSAGE": "پیام توسط S\/MIME رمزنگاری شد",
|
"ENCRYPTED_MESSAGE": "پیام توسط S\/MIME رمزنگاری شد",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Oikea allekirjoitus %USER% lle",
|
"GOOD_SIGNATURE": "Oikea allekirjoitus %USER% lle",
|
||||||
"ERROR": "%TYPE% virhe: %ERROR%",
|
"ERROR": "%TYPE% virhe: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Tuo OpenPGP avain",
|
"POPUP_IMPORT_TITLE": "Tuo OpenPGP avain",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME allekirjoitettu viesti",
|
"SIGNED_MESSAGE": "S\/MIME allekirjoitettu viesti",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME salattu visti",
|
"ENCRYPTED_MESSAGE": "S\/MIME salattu visti",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Signature valide pour %USER%",
|
"GOOD_SIGNATURE": "Signature valide pour %USER%",
|
||||||
"ERROR": "Erreur %TYPE%: %ERROR%",
|
"ERROR": "Erreur %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importer la clef OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importer la clef OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Message signé par S\/MIME",
|
"SIGNED_MESSAGE": "Message signé par S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Message chiffré par S\/MIME",
|
"ENCRYPTED_MESSAGE": "Message chiffré par S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Jó aláírás tőle: %USER%",
|
"GOOD_SIGNATURE": "Jó aláírás tőle: %USER%",
|
||||||
"ERROR": "%TYPE% hiba: %ERROR%",
|
"ERROR": "%TYPE% hiba: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "OpenPGP kulcs importálás",
|
"POPUP_IMPORT_TITLE": "OpenPGP kulcs importálás",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME aláírt üzenet",
|
"SIGNED_MESSAGE": "S\/MIME aláírt üzenet",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME kódolt üzenet",
|
"ENCRYPTED_MESSAGE": "S\/MIME kódolt üzenet",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Tandatangan sah dari user %USER%",
|
"GOOD_SIGNATURE": "Tandatangan sah dari user %USER%",
|
||||||
"ERROR": "Kesalahan %TYPE%: %ERROR%",
|
"ERROR": "Kesalahan %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Impor kunci OpenPGP",
|
"POPUP_IMPORT_TITLE": "Impor kunci OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Pesan bertanda-tangan S\/MIME",
|
"SIGNED_MESSAGE": "Pesan bertanda-tangan S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Pesan terenkripsi S\/MIME",
|
"ENCRYPTED_MESSAGE": "Pesan terenkripsi S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Gild undirritun frá %USER%",
|
"GOOD_SIGNATURE": "Gild undirritun frá %USER%",
|
||||||
"ERROR": "%TYPE% villa: %ERROR%",
|
"ERROR": "%TYPE% villa: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Flytja inn OpenPGP-lykil",
|
"POPUP_IMPORT_TITLE": "Flytja inn OpenPGP-lykil",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Skeyti undirritað með S\/MIME",
|
"SIGNED_MESSAGE": "Skeyti undirritað með S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Skeyti dulritað með S\/MIME",
|
"ENCRYPTED_MESSAGE": "Skeyti dulritað með S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Firma valida da %USER%",
|
"GOOD_SIGNATURE": "Firma valida da %USER%",
|
||||||
"ERROR": "Errore di %TYPE%: %ERROR%",
|
"ERROR": "Errore di %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importa chiave OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importa chiave OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Messaggio firmato con S\/MIME",
|
"SIGNED_MESSAGE": "Messaggio firmato con S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Messaggio cifrato con S\/MIME",
|
"ENCRYPTED_MESSAGE": "Messaggio cifrato con S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "%USER%からの正しい署名",
|
"GOOD_SIGNATURE": "%USER%からの正しい署名",
|
||||||
"ERROR": "%TYPE% エラー: %ERROR%",
|
"ERROR": "%TYPE% エラー: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "OpenPGPキーをインポート",
|
"POPUP_IMPORT_TITLE": "OpenPGPキーをインポート",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME署名済みメッセージ",
|
"SIGNED_MESSAGE": "S\/MIME署名済みメッセージ",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME暗号化メッセージ",
|
"ENCRYPTED_MESSAGE": "S\/MIME暗号化メッセージ",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "%USER%(으)로부터 수신한 양호한 서명",
|
"GOOD_SIGNATURE": "%USER%(으)로부터 수신한 양호한 서명",
|
||||||
"ERROR": "%TYPE% 오류: %ERROR%",
|
"ERROR": "%TYPE% 오류: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "OpenPGP 키 가져오기",
|
"POPUP_IMPORT_TITLE": "OpenPGP 키 가져오기",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME로 서명된 메세지입니다.",
|
"SIGNED_MESSAGE": "S\/MIME로 서명된 메세지입니다.",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME로 암호화된 메세지입니다.",
|
"ENCRYPTED_MESSAGE": "S\/MIME로 암호화된 메세지입니다.",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Tinkamas parašas nuo %USER%",
|
"GOOD_SIGNATURE": "Tinkamas parašas nuo %USER%",
|
||||||
"ERROR": "%TYPE% klaida: %ERROR%",
|
"ERROR": "%TYPE% klaida: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importuoti OpenPGP raktą",
|
"POPUP_IMPORT_TITLE": "Importuoti OpenPGP raktą",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME pasirašytas pranešimas",
|
"SIGNED_MESSAGE": "S\/MIME pasirašytas pranešimas",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME šifruotas pranešimas",
|
"ENCRYPTED_MESSAGE": "S\/MIME šifruotas pranešimas",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Good signature from %USER%",
|
"GOOD_SIGNATURE": "Good signature from %USER%",
|
||||||
"ERROR": "%TYPE% error: %ERROR%",
|
"ERROR": "%TYPE% error: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME signed message",
|
"SIGNED_MESSAGE": "S\/MIME signed message",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Gyldig signatur fra %USER%",
|
"GOOD_SIGNATURE": "Gyldig signatur fra %USER%",
|
||||||
"ERROR": "%TYPE%-feil: %ERROR%",
|
"ERROR": "%TYPE%-feil: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importer OpenPGP-nøkkel",
|
"POPUP_IMPORT_TITLE": "Importer OpenPGP-nøkkel",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME-signert melding",
|
"SIGNED_MESSAGE": "S\/MIME-signert melding",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME-kryptert melding",
|
"ENCRYPTED_MESSAGE": "S\/MIME-kryptert melding",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Juiste handtekening van %USER%",
|
"GOOD_SIGNATURE": "Juiste handtekening van %USER%",
|
||||||
"ERROR": "%TYPE% foutmelding: %ERROR%",
|
"ERROR": "%TYPE% foutmelding: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Zelfondertekend certificaat aanmaken",
|
"CREATE_SELF_SIGNED": "Zelfondertekend certificaat aanmaken",
|
||||||
"VALID_UNTIL": "Geldig tot"
|
"VALID_UNTIL": "Geldig tot",
|
||||||
|
"PRIVATE_KEY": "Privésleutel"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importeer OpenPGP sleutel",
|
"POPUP_IMPORT_TITLE": "Importeer OpenPGP sleutel",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Importeer S\/MIME certificaat",
|
"POPUP_IMPORT_TITLE": "Importeer S\/MIME certificaat",
|
||||||
|
"CERTIFICATE": "Certificaat",
|
||||||
"CERTIFICATES": "S\/MIME Certificaten",
|
"CERTIFICATES": "S\/MIME Certificaten",
|
||||||
"SIGNED_MESSAGE": "S\/MIME ondertekend bericht",
|
"SIGNED_MESSAGE": "S\/MIME ondertekend bericht",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME versleuteld bericht",
|
"ENCRYPTED_MESSAGE": "S\/MIME versleuteld bericht",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Poprawnie podpisana przez %USER%",
|
"GOOD_SIGNATURE": "Poprawnie podpisana przez %USER%",
|
||||||
"ERROR": "Błąd %TYPE%: %ERROR%",
|
"ERROR": "Błąd %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Utwórz samodzielnie podpisany certyfikat",
|
"CREATE_SELF_SIGNED": "Utwórz samodzielnie podpisany certyfikat",
|
||||||
"VALID_UNTIL": "Ważny do"
|
"VALID_UNTIL": "Ważny do",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importowanie klucza OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importowanie klucza OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Importuj certyfikat S\/MIME",
|
"POPUP_IMPORT_TITLE": "Importuj certyfikat S\/MIME",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "Certyfikaty S\/MIME",
|
"CERTIFICATES": "Certyfikaty S\/MIME",
|
||||||
"SIGNED_MESSAGE": "Wiadomość podpisana S\/MIME",
|
"SIGNED_MESSAGE": "Wiadomość podpisana S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Wiadomość zaszyfrowana S\/MIME",
|
"ENCRYPTED_MESSAGE": "Wiadomość zaszyfrowana S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Boa assinatura de %USER%",
|
"GOOD_SIGNATURE": "Boa assinatura de %USER%",
|
||||||
"ERROR": "Erro %TYPE%: %ERROR%",
|
"ERROR": "Erro %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importar chave OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importar chave OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Mensagem assinada com S\/MIME",
|
"SIGNED_MESSAGE": "Mensagem assinada com S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Mensagem criptografada com S\/MIME",
|
"ENCRYPTED_MESSAGE": "Mensagem criptografada com S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -154,8 +154,8 @@
|
||||||
"HAS_VIRUS_WARNING": "AVISO: vírus detetado",
|
"HAS_VIRUS_WARNING": "AVISO: vírus detetado",
|
||||||
"TAGS": "Etiquetas",
|
"TAGS": "Etiquetas",
|
||||||
"NEW_TAG": "Nova etiqueta",
|
"NEW_TAG": "Nova etiqueta",
|
||||||
"ENABLE_TRACKING_LINKS": "Enable tracking links",
|
"ENABLE_TRACKING_LINKS": "Ativar ligações de rastreamento",
|
||||||
"TRACKING_ENABLED": "Links are now tracked when clicked!"
|
"TRACKING_ENABLED": "As ligações irão ser rastreadas quando clicadas!"
|
||||||
},
|
},
|
||||||
"MESSAGE_TAGS": {
|
"MESSAGE_TAGS": {
|
||||||
"$important": "Importante",
|
"$important": "Importante",
|
||||||
|
|
@ -255,7 +255,7 @@
|
||||||
"POPUPS_IDENTITY": {
|
"POPUPS_IDENTITY": {
|
||||||
"TITLE_ADD_IDENTITY": "Adicionar identidade?",
|
"TITLE_ADD_IDENTITY": "Adicionar identidade?",
|
||||||
"TITLE_UPDATE_IDENTITY": "Atualizar identidade?",
|
"TITLE_UPDATE_IDENTITY": "Atualizar identidade?",
|
||||||
"LABEL": "Label",
|
"LABEL": "Rótulo",
|
||||||
"BUTTON_ADD_IDENTITY": "Adicionar",
|
"BUTTON_ADD_IDENTITY": "Adicionar",
|
||||||
"BUTTON_UPDATE_IDENTITY": "Atualizar",
|
"BUTTON_UPDATE_IDENTITY": "Atualizar",
|
||||||
"SIGNATURE": "Assinatura",
|
"SIGNATURE": "Assinatura",
|
||||||
|
|
@ -281,8 +281,9 @@
|
||||||
"VERIFY": "Validar",
|
"VERIFY": "Validar",
|
||||||
"GOOD_SIGNATURE": "Boa assinatura de %USER%",
|
"GOOD_SIGNATURE": "Boa assinatura de %USER%",
|
||||||
"ERROR": "Erro %TYPE%: %ERROR%",
|
"ERROR": "Erro %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Criar auto-assinado",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Válido até",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importar chave OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importar chave OpenPGP",
|
||||||
|
|
@ -295,18 +296,19 @@
|
||||||
"SIGNED_MESSAGE": "Mensagem assinada com OpenPGP",
|
"SIGNED_MESSAGE": "Mensagem assinada com OpenPGP",
|
||||||
"ENCRYPTED_MESSAGE": "Mensagem encriptada com OpenPGP",
|
"ENCRYPTED_MESSAGE": "Mensagem encriptada com OpenPGP",
|
||||||
"STORE_IN_GNUPG": "Guardar no servidor em GnuPG",
|
"STORE_IN_GNUPG": "Guardar no servidor em GnuPG",
|
||||||
"STORE_PUBLIC_KEY_IN_GNUPG": "Store public key on server in GnuPG",
|
"STORE_PUBLIC_KEY_IN_GNUPG": "Guardar a chave pública no servidor em GnuPG",
|
||||||
"STORE_PRIVATE_KEY_IN_GNUPG": "Store private key on server in GnuPG",
|
"STORE_PRIVATE_KEY_IN_GNUPG": "Guardar a chave privada no servidor em GnuPG",
|
||||||
"BACKUP_ON_SERVER": "Guardar (encriptado) no servidor",
|
"BACKUP_ON_SERVER": "Guardar (encriptado) no servidor",
|
||||||
"BACKUP_PUBLIC_KEY_ON_SERVER": "Backup public key on server",
|
"BACKUP_PUBLIC_KEY_ON_SERVER": "Fazer cópia de segurança da chave pública no servidor",
|
||||||
"BACKUP_PRIVATE_KEY_ON_SERVER": "Backup private key on server"
|
"BACKUP_PRIVATE_KEY_ON_SERVER": "Fazer cópia de segurança da chave privada no servidor"
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Importar certificado S\/MIME",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATE": "Certificate",
|
||||||
|
"CERTIFICATES": "Certificados S\/MIME",
|
||||||
"SIGNED_MESSAGE": "Mensagem assinada com S\/MIME",
|
"SIGNED_MESSAGE": "Mensagem assinada com S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Mensagem encriptada com S\/MIME",
|
"ENCRYPTED_MESSAGE": "Mensagem encriptada com S\/MIME",
|
||||||
"PRIVATE_KEY_OF": "S\/MIME private key of %EMAIL%"
|
"PRIVATE_KEY_OF": "Chave privada S\/MIME de %EMAIL%"
|
||||||
},
|
},
|
||||||
"POPUPS_FILTER": {
|
"POPUPS_FILTER": {
|
||||||
"TITLE_CREATE_FILTER": "Criar um filtro?",
|
"TITLE_CREATE_FILTER": "Criar um filtro?",
|
||||||
|
|
@ -405,7 +407,7 @@
|
||||||
"IMAGEUPLOAD": "Escolher imagem",
|
"IMAGEUPLOAD": "Escolher imagem",
|
||||||
"UNDO": "Desfazer",
|
"UNDO": "Desfazer",
|
||||||
"REDO": "Refazer",
|
"REDO": "Refazer",
|
||||||
"REMOVESTYLE": "Remove style"
|
"REMOVESTYLE": "Remover estilo"
|
||||||
},
|
},
|
||||||
"SETTINGS_LABELS": {
|
"SETTINGS_LABELS": {
|
||||||
"GENERAL": "Geral",
|
"GENERAL": "Geral",
|
||||||
|
|
@ -530,7 +532,7 @@
|
||||||
"SETTINGS_OPENPGP": {
|
"SETTINGS_OPENPGP": {
|
||||||
"BUTTON_IMPORT_KEY": "Importar chave",
|
"BUTTON_IMPORT_KEY": "Importar chave",
|
||||||
"BUTTON_GENERATE_KEY_PAIR": "Gerar chaves",
|
"BUTTON_GENERATE_KEY_PAIR": "Gerar chaves",
|
||||||
"IMPORT_FROM_SERVER": "Import from server",
|
"IMPORT_FROM_SERVER": "Importar de servidor",
|
||||||
"TITLE_PRIVATE": "Privada",
|
"TITLE_PRIVATE": "Privada",
|
||||||
"TITLE_PUBLIC": "Pública",
|
"TITLE_PUBLIC": "Pública",
|
||||||
"GET_MAILVELOPE": "Obter extensão Mailvelope para o navegador."
|
"GET_MAILVELOPE": "Obter extensão Mailvelope para o navegador."
|
||||||
|
|
|
||||||
|
|
@ -154,8 +154,8 @@
|
||||||
"HAS_VIRUS_WARNING": "AVISO: vírus detetado",
|
"HAS_VIRUS_WARNING": "AVISO: vírus detetado",
|
||||||
"TAGS": "Etiquetas",
|
"TAGS": "Etiquetas",
|
||||||
"NEW_TAG": "Nova etiqueta",
|
"NEW_TAG": "Nova etiqueta",
|
||||||
"ENABLE_TRACKING_LINKS": "Enable tracking links",
|
"ENABLE_TRACKING_LINKS": "Ativar ligações de rastreamento",
|
||||||
"TRACKING_ENABLED": "Links are now tracked when clicked!"
|
"TRACKING_ENABLED": "As ligações irão ser rastreadas quando clicadas!"
|
||||||
},
|
},
|
||||||
"MESSAGE_TAGS": {
|
"MESSAGE_TAGS": {
|
||||||
"$important": "Importante",
|
"$important": "Importante",
|
||||||
|
|
@ -255,7 +255,7 @@
|
||||||
"POPUPS_IDENTITY": {
|
"POPUPS_IDENTITY": {
|
||||||
"TITLE_ADD_IDENTITY": "Adicionar identidade?",
|
"TITLE_ADD_IDENTITY": "Adicionar identidade?",
|
||||||
"TITLE_UPDATE_IDENTITY": "Atualizar identidade?",
|
"TITLE_UPDATE_IDENTITY": "Atualizar identidade?",
|
||||||
"LABEL": "Label",
|
"LABEL": "Rótulo",
|
||||||
"BUTTON_ADD_IDENTITY": "Adicionar",
|
"BUTTON_ADD_IDENTITY": "Adicionar",
|
||||||
"BUTTON_UPDATE_IDENTITY": "Atualizar",
|
"BUTTON_UPDATE_IDENTITY": "Atualizar",
|
||||||
"SIGNATURE": "Assinatura",
|
"SIGNATURE": "Assinatura",
|
||||||
|
|
@ -281,8 +281,9 @@
|
||||||
"VERIFY": "Validar",
|
"VERIFY": "Validar",
|
||||||
"GOOD_SIGNATURE": "Boa assinatura de %USER%",
|
"GOOD_SIGNATURE": "Boa assinatura de %USER%",
|
||||||
"ERROR": "Erro %TYPE%: %ERROR%",
|
"ERROR": "Erro %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Criar auto-assinado",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Válido até",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importar chave OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importar chave OpenPGP",
|
||||||
|
|
@ -295,18 +296,19 @@
|
||||||
"SIGNED_MESSAGE": "Mensagem assinada com OpenPGP",
|
"SIGNED_MESSAGE": "Mensagem assinada com OpenPGP",
|
||||||
"ENCRYPTED_MESSAGE": "Mensagem encriptada com OpenPGP",
|
"ENCRYPTED_MESSAGE": "Mensagem encriptada com OpenPGP",
|
||||||
"STORE_IN_GNUPG": "Guardar no servidor em GnuPG",
|
"STORE_IN_GNUPG": "Guardar no servidor em GnuPG",
|
||||||
"STORE_PUBLIC_KEY_IN_GNUPG": "Store public key on server in GnuPG",
|
"STORE_PUBLIC_KEY_IN_GNUPG": "Guardar a chave pública no servidor em GnuPG",
|
||||||
"STORE_PRIVATE_KEY_IN_GNUPG": "Store private key on server in GnuPG",
|
"STORE_PRIVATE_KEY_IN_GNUPG": "Guardar a chave privada no servidor em GnuPG",
|
||||||
"BACKUP_ON_SERVER": "Guardar (encriptado) no servidor",
|
"BACKUP_ON_SERVER": "Guardar (encriptado) no servidor",
|
||||||
"BACKUP_PUBLIC_KEY_ON_SERVER": "Backup public key on server",
|
"BACKUP_PUBLIC_KEY_ON_SERVER": "Fazer cópia de segurança da chave pública no servidor",
|
||||||
"BACKUP_PRIVATE_KEY_ON_SERVER": "Backup private key on server"
|
"BACKUP_PRIVATE_KEY_ON_SERVER": "Fazer cópia de segurança da chave privada no servidor"
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Importar certificado S\/MIME",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATE": "Certificate",
|
||||||
|
"CERTIFICATES": "Certificados S\/MIME",
|
||||||
"SIGNED_MESSAGE": "Mensagem assinada com S\/MIME",
|
"SIGNED_MESSAGE": "Mensagem assinada com S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Mensagem encriptada com S\/MIME",
|
"ENCRYPTED_MESSAGE": "Mensagem encriptada com S\/MIME",
|
||||||
"PRIVATE_KEY_OF": "S\/MIME private key of %EMAIL%"
|
"PRIVATE_KEY_OF": "Chave privada S\/MIME de %EMAIL%"
|
||||||
},
|
},
|
||||||
"POPUPS_FILTER": {
|
"POPUPS_FILTER": {
|
||||||
"TITLE_CREATE_FILTER": "Criar um filtro?",
|
"TITLE_CREATE_FILTER": "Criar um filtro?",
|
||||||
|
|
@ -405,7 +407,7 @@
|
||||||
"IMAGEUPLOAD": "Escolher imagem",
|
"IMAGEUPLOAD": "Escolher imagem",
|
||||||
"UNDO": "Desfazer",
|
"UNDO": "Desfazer",
|
||||||
"REDO": "Refazer",
|
"REDO": "Refazer",
|
||||||
"REMOVESTYLE": "Remove style"
|
"REMOVESTYLE": "Remover estilo"
|
||||||
},
|
},
|
||||||
"SETTINGS_LABELS": {
|
"SETTINGS_LABELS": {
|
||||||
"GENERAL": "Geral",
|
"GENERAL": "Geral",
|
||||||
|
|
@ -530,7 +532,7 @@
|
||||||
"SETTINGS_OPENPGP": {
|
"SETTINGS_OPENPGP": {
|
||||||
"BUTTON_IMPORT_KEY": "Importar chave",
|
"BUTTON_IMPORT_KEY": "Importar chave",
|
||||||
"BUTTON_GENERATE_KEY_PAIR": "Gerar chaves",
|
"BUTTON_GENERATE_KEY_PAIR": "Gerar chaves",
|
||||||
"IMPORT_FROM_SERVER": "Import from server",
|
"IMPORT_FROM_SERVER": "Importar de servidor",
|
||||||
"TITLE_PRIVATE": "Privada",
|
"TITLE_PRIVATE": "Privada",
|
||||||
"TITLE_PUBLIC": "Pública",
|
"TITLE_PUBLIC": "Pública",
|
||||||
"GET_MAILVELOPE": "Obter extensão Mailvelope para o navegador."
|
"GET_MAILVELOPE": "Obter extensão Mailvelope para o navegador."
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Good signature from %USER%",
|
"GOOD_SIGNATURE": "Good signature from %USER%",
|
||||||
"ERROR": "%TYPE% error: %ERROR%",
|
"ERROR": "%TYPE% error: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
"POPUP_IMPORT_TITLE": "Import OpenPGP key",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME signed message",
|
"SIGNED_MESSAGE": "S\/MIME signed message",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Подтвержденная сигнатура для %USER%",
|
"GOOD_SIGNATURE": "Подтвержденная сигнатура для %USER%",
|
||||||
"ERROR": "%TYPE% ошибка: %ERROR%",
|
"ERROR": "%TYPE% ошибка: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Импорт OpenPGP ключа",
|
"POPUP_IMPORT_TITLE": "Импорт OpenPGP ключа",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME подписанное сообщение",
|
"SIGNED_MESSAGE": "S\/MIME подписанное сообщение",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME шифрованное сообщение",
|
"ENCRYPTED_MESSAGE": "S\/MIME шифрованное сообщение",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Good signature from %USER%",
|
"GOOD_SIGNATURE": "Good signature from %USER%",
|
||||||
"ERROR": "%TYPE% error: %ERROR%",
|
"ERROR": "%TYPE% error: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Importovať kľúč OpenPGP",
|
"POPUP_IMPORT_TITLE": "Importovať kľúč OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Správa podpísaná s S\/MIME",
|
"SIGNED_MESSAGE": "Správa podpísaná s S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Správa šifrovaná s S\/MIME",
|
"ENCRYPTED_MESSAGE": "Správa šifrovaná s S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Dober podpis od %USER%",
|
"GOOD_SIGNATURE": "Dober podpis od %USER%",
|
||||||
"ERROR": "%TYPE% napaka: %ERROR%",
|
"ERROR": "%TYPE% napaka: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Uvoz OpenPGP ključa",
|
"POPUP_IMPORT_TITLE": "Uvoz OpenPGP ključa",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Sporočilo, podpisano z S\/MIME",
|
"SIGNED_MESSAGE": "Sporočilo, podpisano z S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Sporočilo, šifrirano z S\/MIME",
|
"ENCRYPTED_MESSAGE": "Sporočilo, šifrirano z S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Godkänd signatur för %USER%",
|
"GOOD_SIGNATURE": "Godkänd signatur för %USER%",
|
||||||
"ERROR": "%TYPE%-fel: %ERROR%",
|
"ERROR": "%TYPE%-fel: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Import av OpenPGP-nyckel",
|
"POPUP_IMPORT_TITLE": "Import av OpenPGP-nyckel",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME-signerat meddelande",
|
"SIGNED_MESSAGE": "S\/MIME-signerat meddelande",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME-krypterat meddelande",
|
"ENCRYPTED_MESSAGE": "S\/MIME-krypterat meddelande",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Geçerli imza %USER%",
|
"GOOD_SIGNATURE": "Geçerli imza %USER%",
|
||||||
"ERROR": "%TYPE% hatası: %ERROR%",
|
"ERROR": "%TYPE% hatası: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "OpenPGP key'i içe aktar",
|
"POPUP_IMPORT_TITLE": "OpenPGP key'i içe aktar",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME imzalı mesaj",
|
"SIGNED_MESSAGE": "S\/MIME imzalı mesaj",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME şifreli mesaj",
|
"ENCRYPTED_MESSAGE": "S\/MIME şifreli mesaj",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Підтверджена сигнатура для %USER%",
|
"GOOD_SIGNATURE": "Підтверджена сигнатура для %USER%",
|
||||||
"ERROR": "%TYPE% помилка: %ERROR%",
|
"ERROR": "%TYPE% помилка: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Імпорт OpenPGP ключа",
|
"POPUP_IMPORT_TITLE": "Імпорт OpenPGP ключа",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME підписане повідомлення",
|
"SIGNED_MESSAGE": "S\/MIME підписане повідомлення",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME шифроване повідомлення",
|
"ENCRYPTED_MESSAGE": "S\/MIME шифроване повідомлення",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "Chữ ký ổn từ %USER%",
|
"GOOD_SIGNATURE": "Chữ ký ổn từ %USER%",
|
||||||
"ERROR": "Lỗi %TYPE%: %ERROR%",
|
"ERROR": "Lỗi %TYPE%: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "Nhập mật mã OpenPGP",
|
"POPUP_IMPORT_TITLE": "Nhập mật mã OpenPGP",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "Thư đã được ký xác nhận bằng mã S\/MIME",
|
"SIGNED_MESSAGE": "Thư đã được ký xác nhận bằng mã S\/MIME",
|
||||||
"ENCRYPTED_MESSAGE": "Thư đã được mã hóa bởi S\/MIME",
|
"ENCRYPTED_MESSAGE": "Thư đã được mã hóa bởi S\/MIME",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "来自 %USER% 的可用签名",
|
"GOOD_SIGNATURE": "来自 %USER% 的可用签名",
|
||||||
"ERROR": "%TYPE% 错误: %ERROR%",
|
"ERROR": "%TYPE% 错误: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "导入 OpenPGP 密钥",
|
"POPUP_IMPORT_TITLE": "导入 OpenPGP 密钥",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "由 S\/MIME 签名",
|
"SIGNED_MESSAGE": "由 S\/MIME 签名",
|
||||||
"ENCRYPTED_MESSAGE": "由 S\/MIME 加密",
|
"ENCRYPTED_MESSAGE": "由 S\/MIME 加密",
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@
|
||||||
"GOOD_SIGNATURE": "來自「%USER%」的良好簽署",
|
"GOOD_SIGNATURE": "來自「%USER%」的良好簽署",
|
||||||
"ERROR": "%TYPE% 錯誤: %ERROR%",
|
"ERROR": "%TYPE% 錯誤: %ERROR%",
|
||||||
"CREATE_SELF_SIGNED": "Create self-signed",
|
"CREATE_SELF_SIGNED": "Create self-signed",
|
||||||
"VALID_UNTIL": "Valid until"
|
"VALID_UNTIL": "Valid until",
|
||||||
|
"PRIVATE_KEY": "Private key"
|
||||||
},
|
},
|
||||||
"OPENPGP": {
|
"OPENPGP": {
|
||||||
"POPUP_IMPORT_TITLE": "匯入 OpenPGP 金鑰",
|
"POPUP_IMPORT_TITLE": "匯入 OpenPGP 金鑰",
|
||||||
|
|
@ -303,6 +304,7 @@
|
||||||
},
|
},
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
|
"CERTIFICATE": "Certificate",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "已透過 S\/MIME 簽署郵件",
|
"SIGNED_MESSAGE": "已透過 S\/MIME 簽署郵件",
|
||||||
"ENCRYPTED_MESSAGE": "已透過 S\/MIME 加密郵件",
|
"ENCRYPTED_MESSAGE": "已透過 S\/MIME 加密郵件",
|
||||||
|
|
|
||||||
|
|
@ -89,14 +89,14 @@
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>S/MIME</strong></summary>
|
<summary><strong>S/MIME</strong></summary>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>Private key</label>
|
<label data-i18n="CRYPTO/PRIVATE_KEY"></label>
|
||||||
<textarea name="smimeKey" class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: smimeKey"></textarea>
|
<textarea name="smimeKey" class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: smimeKey"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>Certificate</label>
|
<label data-i18n="SMIME/CERTIFICATE"></label>
|
||||||
<textarea name="smimeCertificate" class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: smimeCertificate"></textarea>
|
<textarea name="smimeCertificate" class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: smimeCertificate"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group" data-bind="hidden:smimeKey">
|
<div class="control-group" data-bind="hidden:smimeCertificate">
|
||||||
<label></label>
|
<label></label>
|
||||||
<button type="button" data-bind="click: $root.createSelfSigned" data-i18n="CRYPTO/CREATE_SELF_SIGNED"></button>
|
<button type="button" data-bind="click: $root.createSelfSigned" data-i18n="CRYPTO/CREATE_SELF_SIGNED"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue