mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Improved error handling on PGP and S/MIME decrypt
This commit is contained in:
parent
0e202a6640
commit
41969bf2c1
10 changed files with 68 additions and 77 deletions
3
dev/External/ko.js
vendored
3
dev/External/ko.js
vendored
|
|
@ -66,8 +66,7 @@ Object.assign(ko.bindingHandlers, {
|
||||||
},
|
},
|
||||||
update: (element, fValueAccessor) => {
|
update: (element, fValueAccessor) => {
|
||||||
let value = ko.unwrap(fValueAccessor());
|
let value = ko.unwrap(fValueAccessor());
|
||||||
value = isFunction(value) ? value() : value;
|
errorTip(element, isFunction(value) ? value() : value);
|
||||||
errorTip(element, value);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ export const GnuPGUserStore = new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
async verify(message) {
|
async verify(message) {
|
||||||
let data = message.pgpSigned(); // { bodyPartId: "1", sigPartId: "2", micAlg: "pgp-sha256" }
|
let data = message.pgpSigned(); // { partId: "1", sigPartId: "2", micAlg: "pgp-sha256" }
|
||||||
if (data) {
|
if (data) {
|
||||||
data = { ...data }; // clone
|
data = { ...data }; // clone
|
||||||
// const sender = message.from[0].email;
|
// const sender = message.from[0].email;
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ export const OpenPGPUserStore = new class {
|
||||||
* https://docs.openpgpjs.org/#sign-and-verify-cleartext-messages
|
* https://docs.openpgpjs.org/#sign-and-verify-cleartext-messages
|
||||||
*/
|
*/
|
||||||
async verify(message) {
|
async verify(message) {
|
||||||
const data = message.pgpSigned(), // { bodyPartId: "1", sigPartId: "2", micAlg: "pgp-sha256" }
|
const data = message.pgpSigned(), // { partId: "1", sigPartId: "2", micAlg: "pgp-sha256" }
|
||||||
publicKey = this.publicKeys().find(key => key.emails.includes(message.from[0].email));
|
publicKey = this.publicKeys().find(key => key.emails.includes(message.from[0].email));
|
||||||
if (data && publicKey) {
|
if (data && publicKey) {
|
||||||
data.folder = message.folder;
|
data.folder = message.folder;
|
||||||
|
|
|
||||||
|
|
@ -108,19 +108,14 @@ export const
|
||||||
}
|
}
|
||||||
|
|
||||||
async verify(message) {
|
async verify(message) {
|
||||||
const signed = message.pgpSigned();
|
const signed = message.pgpSigned(),
|
||||||
|
sender = message.from[0].email;
|
||||||
if (signed) {
|
if (signed) {
|
||||||
const sender = message.from[0].email,
|
// OpenPGP only when inline, else we must download the whole message
|
||||||
gnupg = GnuPGUserStore.hasPublicKeyForEmails([sender]),
|
if (!signed.sigPartId && OpenPGPUserStore.hasPublicKeyForEmails([sender])) {
|
||||||
openpgp = OpenPGPUserStore.hasPublicKeyForEmails([sender]);
|
|
||||||
// Detached signature use GnuPG first, else we must download whole message
|
|
||||||
if (gnupg && signed.sigPartId) {
|
|
||||||
return GnuPGUserStore.verify(message);
|
|
||||||
}
|
|
||||||
if (openpgp) {
|
|
||||||
return OpenPGPUserStore.verify(message);
|
return OpenPGPUserStore.verify(message);
|
||||||
}
|
}
|
||||||
if (gnupg) {
|
if (GnuPGUserStore.hasPublicKeyForEmails([sender])) {
|
||||||
return GnuPGUserStore.verify(message);
|
return GnuPGUserStore.verify(message);
|
||||||
}
|
}
|
||||||
// Mailvelope can't
|
// Mailvelope can't
|
||||||
|
|
|
||||||
|
|
@ -570,9 +570,14 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
}
|
}
|
||||||
|
|
||||||
pgpDecrypt() {
|
pgpDecrypt() {
|
||||||
const oMessage = currentMessage();
|
const oMessage = currentMessage(),
|
||||||
|
data = oMessage.pgpEncrypted();
|
||||||
|
delete data.error;
|
||||||
PgpUserStore.decrypt(oMessage).then(result => {
|
PgpUserStore.decrypt(oMessage).then(result => {
|
||||||
if (result) {
|
if (!result) {
|
||||||
|
// TODO: translate
|
||||||
|
throw Error('Decryption failed, canceled or not possible');
|
||||||
|
}
|
||||||
oMessage.pgpDecrypted(true);
|
oMessage.pgpDecrypted(true);
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
MimeToMessage(result.data, oMessage);
|
MimeToMessage(result.data, oMessage);
|
||||||
|
|
@ -585,14 +590,12 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// TODO: translate
|
|
||||||
throw Error('Decryption failed, canceled or not possible');
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error(e)
|
data.error = e.message;
|
||||||
alert(e.message);
|
})
|
||||||
|
.finally(() => {
|
||||||
|
oMessage.pgpEncrypted(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -627,16 +630,17 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
|
|
||||||
async smimeDecrypt() {
|
async smimeDecrypt() {
|
||||||
const message = currentMessage();
|
const message = currentMessage();
|
||||||
let pass, data = message.smimeEncrypted(); // { partId: "1" }
|
|
||||||
const addresses = message.from.concat(message.to, message.cc, message.bcc).map(item => item.email),
|
const addresses = message.from.concat(message.to, message.cc, message.bcc).map(item => item.email),
|
||||||
identity = IdentityUserStore.find(item => addresses.includes(item.email()));
|
identity = IdentityUserStore.find(item => addresses.includes(item.email())),
|
||||||
|
data = message.smimeEncrypted(); // { partId: "1" }
|
||||||
if (data && identity) {
|
if (data && identity) {
|
||||||
data = { ...data }; // clone
|
delete data.error;
|
||||||
data.folder = message.folder;
|
let pass, params = { ...data }; // clone
|
||||||
data.uid = message.uid;
|
params.folder = message.folder;
|
||||||
// data.bodyPart = data.bodyPart?.raw;
|
params.uid = message.uid;
|
||||||
data.certificate = identity.smimeCertificate();
|
// params.bodyPart = params.bodyPart?.raw;
|
||||||
data.privateKey = identity.smimeKey();
|
params.certificate = identity.smimeCertificate();
|
||||||
|
params.privateKey = identity.smimeKey();
|
||||||
if (identity.smimeKeyEncrypted()) {
|
if (identity.smimeKeyEncrypted()) {
|
||||||
pass = await Passphrases.ask(identity,
|
pass = await Passphrases.ask(identity,
|
||||||
i18n('SMIME/PRIVATE_KEY_OF', {EMAIL: identity.email()}),
|
i18n('SMIME/PRIVATE_KEY_OF', {EMAIL: identity.email()}),
|
||||||
|
|
@ -645,15 +649,20 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
if (!pass) {
|
if (!pass) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.passphrase = pass?.password;
|
params.passphrase = pass?.password;
|
||||||
}
|
}
|
||||||
Remote.post('SMimeDecryptMessage', null, data).then(response => {
|
Remote.post('SMimeDecryptMessage', null, params).then(response => {
|
||||||
if (response?.Result) {
|
if (response?.Result?.data) {
|
||||||
message.smimeDecrypted(true);
|
message.smimeDecrypted(true);
|
||||||
MimeToMessage(response.Result, message);
|
MimeToMessage(response.Result.data, message);
|
||||||
message.html() ? message.viewHtml() : message.viewPlain();
|
message.html() ? message.viewHtml() : message.viewPlain();
|
||||||
pass && pass.remember && Passphrases.set(identity, pass.password);
|
pass && pass.remember && Passphrases.set(identity, pass.password);
|
||||||
}
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
data.error = e.message
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
message.smimeEncrypted(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ class Message implements \JsonSerializable
|
||||||
$oMessage->pgpSigned = [
|
$oMessage->pgpSigned = [
|
||||||
// /?/Raw/&q[]=/0/Download/&q[]=/...
|
// /?/Raw/&q[]=/0/Download/&q[]=/...
|
||||||
// /?/Raw/&q[]=/0/View/&q[]=/...
|
// /?/Raw/&q[]=/0/View/&q[]=/...
|
||||||
'bodyPartId' => $oPart->SubParts()[0]->PartID(),
|
'partId' => $oPart->SubParts()[0]->PartID(),
|
||||||
'sigPartId' => $oPart->SubParts()[1]->PartID(),
|
'sigPartId' => $oPart->SubParts()[1]->PartID(),
|
||||||
'micAlg' => $oHeaders ? (string) $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, 'micalg') : ''
|
'micAlg' => $oHeaders ? (string) $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, 'micalg') : ''
|
||||||
];
|
];
|
||||||
|
|
@ -357,9 +357,9 @@ class Message implements \JsonSerializable
|
||||||
// An empty section specification refers to the entire message, including the header.
|
// An empty section specification refers to the entire message, including the header.
|
||||||
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
||||||
$sPgpText = \trim(
|
$sPgpText = \trim(
|
||||||
\trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['bodyPartId'].'.MIME]'))
|
\trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['partId'].'.MIME]'))
|
||||||
. "\r\n\r\n"
|
. "\r\n\r\n"
|
||||||
. \trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['bodyPartId'].']'))
|
. \trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['partId'].']'))
|
||||||
);
|
);
|
||||||
if ($sPgpText) {
|
if ($sPgpText) {
|
||||||
$oMessage->pgpSigned['body'] = $sPgpText;
|
$oMessage->pgpSigned['body'] = $sPgpText;
|
||||||
|
|
@ -398,7 +398,7 @@ class Message implements \JsonSerializable
|
||||||
// Cleartext Signature
|
// Cleartext Signature
|
||||||
if (!$oMessage->pgpSigned && \str_contains($sText, '-----BEGIN PGP SIGNED MESSAGE-----')) {
|
if (!$oMessage->pgpSigned && \str_contains($sText, '-----BEGIN PGP SIGNED MESSAGE-----')) {
|
||||||
$oMessage->pgpSigned = [
|
$oMessage->pgpSigned = [
|
||||||
'bodyPartId' => $oPart->PartID()
|
'partId' => $oPart->PartID()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ trait Pgp
|
||||||
} else {
|
} else {
|
||||||
$sFolderName = $this->GetActionParam('folder', '');
|
$sFolderName = $this->GetActionParam('folder', '');
|
||||||
$iUid = (int) $this->GetActionParam('uid', 0);
|
$iUid = (int) $this->GetActionParam('uid', 0);
|
||||||
$sBodyPartId = $this->GetActionParam('bodyPartId', '');
|
$sPartId = $this->GetActionParam('partId', '');
|
||||||
$sSigPartId = $this->GetActionParam('sigPartId', '');
|
$sSigPartId = $this->GetActionParam('sigPartId', '');
|
||||||
// $sMicAlg = $this->GetActionParam('micAlg', '');
|
// $sMicAlg = $this->GetActionParam('micAlg', '');
|
||||||
|
|
||||||
|
|
@ -312,10 +312,10 @@ trait Pgp
|
||||||
$oImapClient->FolderExamine($sFolderName);
|
$oImapClient->FolderExamine($sFolderName);
|
||||||
|
|
||||||
$aParts = [
|
$aParts = [
|
||||||
FetchType::BODY_PEEK.'['.$sBodyPartId.']',
|
FetchType::BODY_PEEK.'['.$sPartId.']',
|
||||||
// An empty section specification refers to the entire message, including the header.
|
// An empty section specification refers to the entire message, including the header.
|
||||||
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
||||||
FetchType::BODY_PEEK.'['.$sBodyPartId.'.MIME]'
|
FetchType::BODY_PEEK.'['.$sPartId.'.MIME]'
|
||||||
];
|
];
|
||||||
if ($sSigPartId) {
|
if ($sSigPartId) {
|
||||||
$aParts[] = FetchType::BODY_PEEK.'['.$sSigPartId.']';
|
$aParts[] = FetchType::BODY_PEEK.'['.$sSigPartId.']';
|
||||||
|
|
@ -323,11 +323,11 @@ trait Pgp
|
||||||
|
|
||||||
$oFetchResponse = $oImapClient->Fetch($aParts, $iUid, true)[0];
|
$oFetchResponse = $oImapClient->Fetch($aParts, $iUid, true)[0];
|
||||||
|
|
||||||
$sBodyMime = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.'.MIME]');
|
$sBodyMime = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.'.MIME]');
|
||||||
if ($sSigPartId) {
|
if ($sSigPartId) {
|
||||||
$result = [
|
$result = [
|
||||||
'text' => \preg_replace('/\\r?\\n/su', "\r\n",
|
'text' => \preg_replace('/\\r?\\n/su', "\r\n",
|
||||||
$sBodyMime . $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']')
|
$sBodyMime . $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.']')
|
||||||
),
|
),
|
||||||
'signature' => preg_replace('/[^\x00-\x7F]/', '',
|
'signature' => preg_replace('/[^\x00-\x7F]/', '',
|
||||||
$oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sSigPartId.']')
|
$oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sSigPartId.']')
|
||||||
|
|
@ -336,7 +336,7 @@ trait Pgp
|
||||||
} else {
|
} else {
|
||||||
// clearsigned text
|
// clearsigned text
|
||||||
$result = [
|
$result = [
|
||||||
'text' => $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']'),
|
'text' => $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.']'),
|
||||||
'signature' => ''
|
'signature' => ''
|
||||||
];
|
];
|
||||||
$decode = (new \MailSo\Mime\HeaderCollection($sBodyMime))->ValueByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING);
|
$decode = (new \MailSo\Mime\HeaderCollection($sBodyMime))->ValueByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING);
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ trait SMime
|
||||||
$SMIME->setPrivateKey($sPrivateKey, $oPassphrase);
|
$SMIME->setPrivateKey($sPrivateKey, $oPassphrase);
|
||||||
$result = $SMIME->decrypt($sBody);
|
$result = $SMIME->decrypt($sBody);
|
||||||
|
|
||||||
return $this->DefaultResponse($result ?: false);
|
return $this->DefaultResponse($result ? ['data' => $result] : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DoSMimeVerifyMessage() : array
|
public function DoSMimeVerifyMessage() : array
|
||||||
|
|
|
||||||
|
|
@ -333,9 +333,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$fingerprint) {
|
if (!$fingerprint) {
|
||||||
if (!empty($result['errors'])) {
|
|
||||||
\SnappyMail\Log::error('GPG', \implode("\n\t", $result['errors']));
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,10 +391,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($result['errors'][0])) {
|
|
||||||
\SnappyMail\Log::warning('GPG', $result['errors'][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -809,12 +802,11 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
$this->_debug('BEGIN DETECT MESSAGE KEY IDs');
|
$this->_debug('BEGIN DETECT MESSAGE KEY IDs');
|
||||||
$this->setInput($data);
|
$this->setInput($data);
|
||||||
// $_ENV['PINENTRY_USER_DATA'] = null;
|
// $_ENV['PINENTRY_USER_DATA'] = null;
|
||||||
$result = $this->exec(['--decrypt','--skip-verify']);
|
$result = $this->exec(['--decrypt','--skip-verify'], false);
|
||||||
$info = [
|
$info = [
|
||||||
'ENC_TO' => [],
|
'ENC_TO' => [],
|
||||||
// 'KEY_CONSIDERED' => [],
|
// 'KEY_CONSIDERED' => [],
|
||||||
// 'NO_SECKEY' => [],
|
// 'NO_SECKEY' => [],
|
||||||
// 'errors' => $result['errors']
|
|
||||||
];
|
];
|
||||||
foreach ($result['status'] as $line) {
|
foreach ($result['status'] as $line) {
|
||||||
$tokens = \explode(' ', $line);
|
$tokens = \explode(' ', $line);
|
||||||
|
|
@ -826,7 +818,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
return $info['ENC_TO'];
|
return $info['ENC_TO'];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function exec(array $arguments) /*: array|false*/
|
private function exec(array $arguments, bool $throw = true) /*: array|false*/
|
||||||
{
|
{
|
||||||
if (\version_compare($this->version, '2.2.5', '<')) {
|
if (\version_compare($this->version, '2.2.5', '<')) {
|
||||||
\SnappyMail\Log::error('GPG', "{$this->version} too old");
|
\SnappyMail\Log::error('GPG', "{$this->version} too old");
|
||||||
|
|
@ -948,12 +940,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
// Timeout after 5 seconds
|
// Timeout after 5 seconds
|
||||||
if (5 < \microtime(1) - $start) {
|
if (5 < \microtime(1) - $start) {
|
||||||
$errors[] = 'timeout';
|
$errors[] = 'timeout';
|
||||||
return [
|
throw new \RuntimeException(\implode("\n", $errors));
|
||||||
'output' => '',
|
|
||||||
'status' => $status,
|
|
||||||
'errors' => $errors
|
|
||||||
];
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$inputStreams = [];
|
$inputStreams = [];
|
||||||
|
|
@ -1076,7 +1063,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
// to use too much memory
|
// to use too much memory
|
||||||
if (\in_array($this->_input, $inputStreams, true) && \strlen($inputBuffer) < self::CHUNK_SIZE) {
|
if (\in_array($this->_input, $inputStreams, true) && \strlen($inputBuffer) < self::CHUNK_SIZE) {
|
||||||
$this->_debug('input stream is ready for reading');
|
$this->_debug('input stream is ready for reading');
|
||||||
$this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from input stream');
|
|
||||||
$chunk = \fread($this->_input, self::CHUNK_SIZE);
|
$chunk = \fread($this->_input, self::CHUNK_SIZE);
|
||||||
$length = \strlen($chunk);
|
$length = \strlen($chunk);
|
||||||
$inputBuffer .= $chunk;
|
$inputBuffer .= $chunk;
|
||||||
|
|
@ -1099,7 +1085,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
// read message (from PHP stream)
|
// read message (from PHP stream)
|
||||||
if (\in_array($this->_message, $inputStreams, true)) {
|
if (\in_array($this->_message, $inputStreams, true)) {
|
||||||
$this->_debug('message stream is ready for reading');
|
$this->_debug('message stream is ready for reading');
|
||||||
$this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from message stream');
|
|
||||||
$chunk = \fread($this->_message, self::CHUNK_SIZE);
|
$chunk = \fread($this->_message, self::CHUNK_SIZE);
|
||||||
$length = \strlen($chunk);
|
$length = \strlen($chunk);
|
||||||
$messageBuffer .= $chunk;
|
$messageBuffer .= $chunk;
|
||||||
|
|
@ -1109,7 +1094,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
// read output (from GPG)
|
// read output (from GPG)
|
||||||
if (\in_array($fdOutput, $inputStreams, true)) {
|
if (\in_array($fdOutput, $inputStreams, true)) {
|
||||||
$this->_debug('output stream ready for reading');
|
$this->_debug('output stream ready for reading');
|
||||||
$this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from output');
|
|
||||||
$chunk = \fread($fdOutput, self::CHUNK_SIZE);
|
$chunk = \fread($fdOutput, self::CHUNK_SIZE);
|
||||||
$length = \strlen($chunk);
|
$length = \strlen($chunk);
|
||||||
$outputBuffer .= $chunk;
|
$outputBuffer .= $chunk;
|
||||||
|
|
@ -1136,7 +1120,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
// read error (from GPG)
|
// read error (from GPG)
|
||||||
if (\in_array($fdError, $inputStreams, true)) {
|
if (\in_array($fdError, $inputStreams, true)) {
|
||||||
$this->_debug('error stream ready for reading');
|
$this->_debug('error stream ready for reading');
|
||||||
$this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from error');
|
|
||||||
foreach ($this->_openPipes->readPipeLines(self::FD_ERROR) as $line) {
|
foreach ($this->_openPipes->readPipeLines(self::FD_ERROR) as $line) {
|
||||||
$errors[] = $line;
|
$errors[] = $line;
|
||||||
$this->_debug("\t{$line}");
|
$this->_debug("\t{$line}");
|
||||||
|
|
@ -1146,7 +1129,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
// read status (from GPG)
|
// read status (from GPG)
|
||||||
if (\in_array($fdStatus, $inputStreams, true)) {
|
if (\in_array($fdStatus, $inputStreams, true)) {
|
||||||
$this->_debug('status stream ready for reading');
|
$this->_debug('status stream ready for reading');
|
||||||
$this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from status');
|
|
||||||
// pass lines to status handlers
|
// pass lines to status handlers
|
||||||
foreach ($this->_openPipes->readPipeLines(self::FD_STATUS) as $line) {
|
foreach ($this->_openPipes->readPipeLines(self::FD_STATUS) as $line) {
|
||||||
// only pass lines beginning with magic prefix
|
// only pass lines beginning with magic prefix
|
||||||
|
|
@ -1199,12 +1181,16 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
|
|
||||||
$this->_debug('END PROCESSING');
|
$this->_debug('END PROCESSING');
|
||||||
|
|
||||||
$this->proc_close();
|
$exitCode = $this->proc_close();
|
||||||
|
|
||||||
$this->_message = null;
|
$this->_message = null;
|
||||||
$this->_input = null;
|
$this->_input = null;
|
||||||
$this->_output = null;
|
$this->_output = null;
|
||||||
|
|
||||||
|
if ($throw && $exitCode && $errors) {
|
||||||
|
throw new \RuntimeException(\implode("\n", $errors), $exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'output' => $outputBuffer,
|
'output' => $outputBuffer,
|
||||||
'status' => $status,
|
'status' => $status,
|
||||||
|
|
|
||||||
|
|
@ -296,10 +296,11 @@
|
||||||
<div class="crypto-control encrypted" data-bind="css: {success: message().pgpDecrypted()}">
|
<div class="crypto-control encrypted" data-bind="css: {success: message().pgpDecrypted()}">
|
||||||
<span data-icon="🔒" data-i18n="OPENPGP/ENCRYPTED_MESSAGE"></span>
|
<span data-icon="🔒" data-i18n="OPENPGP/ENCRYPTED_MESSAGE"></span>
|
||||||
<button class="btn" data-bind="visible: pgpSupported, click: pgpDecrypt" data-i18n="CRYPTO/DECRYPT"></button>
|
<button class="btn" data-bind="visible: pgpSupported, click: pgpDecrypt" data-i18n="CRYPTO/DECRYPT"></button>
|
||||||
|
<div class="alert-error" data-icon="⚠" data-bind="visible: message().pgpEncrypted()?.error, text: message().pgpEncrypted()?.error"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div data-bind="visible: message().pgpSigned()">
|
<div data-bind="visible: message().pgpSigned()">
|
||||||
<div class="crypto-control signed" data-bind="css: {success: message().pgpVerified() && message().pgpVerified().success, error: message().pgpVerified() && !message().pgpVerified().success}">
|
<div class="crypto-control signed" data-bind="css: {success: message().pgpVerified()?.success, error: message().pgpVerified() && !message().pgpVerified().success}">
|
||||||
<span data-icon="✍" data-i18n="OPENPGP/SIGNED_MESSAGE"></span>
|
<span data-icon="✍" data-i18n="OPENPGP/SIGNED_MESSAGE"></span>
|
||||||
<button class="btn" data-bind="visible: pgpSupported, click: pgpVerify" data-i18n="CRYPTO/VERIFY"></button>
|
<button class="btn" data-bind="visible: pgpSupported, click: pgpVerify" data-i18n="CRYPTO/VERIFY"></button>
|
||||||
<div data-icon="⚠" data-bind="visible: message().pgpVerified()?.error, text: message().pgpVerified()?.error"></div>
|
<div data-icon="⚠" data-bind="visible: message().pgpVerified()?.error, text: message().pgpVerified()?.error"></div>
|
||||||
|
|
@ -310,6 +311,7 @@
|
||||||
<div class="crypto-control encrypted" data-bind="css: {success: message().smimeDecrypted()}">
|
<div class="crypto-control encrypted" data-bind="css: {success: message().smimeDecrypted()}">
|
||||||
<span data-icon="🔒" data-i18n="SMIME/ENCRYPTED_MESSAGE"></span>
|
<span data-icon="🔒" data-i18n="SMIME/ENCRYPTED_MESSAGE"></span>
|
||||||
<button class="btn" data-bind="click: smimeDecrypt" data-i18n="CRYPTO/DECRYPT"></button>
|
<button class="btn" data-bind="click: smimeDecrypt" data-i18n="CRYPTO/DECRYPT"></button>
|
||||||
|
<div class="alert-error" data-icon="⚠" data-bind="visible: message().smimeEncrypted()?.error, text: message().smimeEncrypted()?.error"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div data-bind="visible: message().smimeSigned()">
|
<div data-bind="visible: message().smimeSigned()">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue