mirror of
https://github.com/the-djmaze/snappymail.git
synced 2025-09-10 23:24:15 +08:00
Improved and bugfix handling of GnuPG for #89
This commit is contained in:
parent
51fedab4fc
commit
1d3673f117
3 changed files with 461 additions and 306 deletions
|
@ -25,10 +25,10 @@ trait Pgp
|
|||
*/
|
||||
if (80 < \strlen($homedir)) {
|
||||
// First try a symbolic link
|
||||
$link = \sys_get_temp_dir() . '/snappymail';
|
||||
if (\is_dir($link) || \mkdir($link, 0700, true)) {
|
||||
$tmpdir = \sys_get_temp_dir() . '/snappymail';
|
||||
if (\is_dir($tmpdir) || \mkdir($tmpdir, 0700, true)) {
|
||||
$link = $tmpdir . '/' . \md5($homedir);
|
||||
if (\is_link($homedir) || \symlink($homedir, $link)) {
|
||||
if (\is_link($link) || \symlink($homedir, $link)) {
|
||||
$homedir = $link;
|
||||
}
|
||||
}
|
||||
|
@ -49,50 +49,9 @@ trait Pgp
|
|||
public function DoGnupgGetKeys() : array
|
||||
{
|
||||
$GPG = $this->GnuPG();
|
||||
if ($GPG) {
|
||||
$keys = [];
|
||||
/**
|
||||
* PECL GnuPG can't list private
|
||||
*
|
||||
* gpg --list-secret-keys
|
||||
* gpg --list-public-keys
|
||||
*/
|
||||
foreach ($GPG->keyInfo('') as $info) {
|
||||
if (!$info['disabled'] && !$info['expired'] && !$info['revoked']) {
|
||||
$info['can_verify'] = $info['can_sign'];
|
||||
$info['can_sign'] = $info['can_decrypt'] = false;
|
||||
foreach ($info['subkeys'] as $key) {
|
||||
$hasKey = $GPG->hasPrivateKey($key['keygrip']);
|
||||
$info['can_sign'] = $info['can_sign'] || ($info['can_verify'] && $hasKey);
|
||||
$info['can_decrypt'] = $info['can_decrypt'] || ($info['can_encrypt'] && $hasKey);
|
||||
}
|
||||
foreach ($info['uids'] as $uid) {
|
||||
$id = $uid['email'];
|
||||
if (isset($keys[$id])) {
|
||||
// Public Key tasks
|
||||
$keys[$id]['can_verify'] = $keys[$id]['can_verify'] || $info['can_verify'];
|
||||
$keys[$id]['can_encrypt'] = $keys[$id]['can_encrypt'] || $info['can_encrypt'];
|
||||
// Private Key tasks
|
||||
$keys[$id]['can_sign'] = $keys[$id]['can_sign'] || $info['can_sign'];
|
||||
$keys[$id]['can_decrypt'] = $keys[$id]['can_decrypt'] || $info['can_decrypt'];
|
||||
} else {
|
||||
$keys[$id] = [
|
||||
'name' => $uid['name'],
|
||||
'email' => $uid['email'],
|
||||
// Public Key tasks
|
||||
'can_verify' => $info['can_sign'],
|
||||
'can_encrypt' => $info['can_encrypt'],
|
||||
// Private Key tasks
|
||||
'can_sign' => $info['can_sign'],
|
||||
'can_decrypt' => $info['can_decrypt']
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->DefaultResponse(__FUNCTION__, $keys);
|
||||
}
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
return $GPG
|
||||
? $this->DefaultResponse(__FUNCTION__, $GPG->keyInfo(''))
|
||||
: $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
public function DoGnupgImportKey() : array
|
||||
|
|
|
@ -8,6 +8,8 @@ class GnuPG
|
|||
$homedir,
|
||||
// Instance of gnupg pecl extension
|
||||
$GnuPG,
|
||||
// Instance of \SnappyMail\PGP\GPG
|
||||
$GPG,
|
||||
// Instance of PEAR Crypt_GPG
|
||||
$Crypt_GPG;
|
||||
|
||||
|
@ -38,6 +40,9 @@ class GnuPG
|
|||
]);
|
||||
// Output is ASCII
|
||||
$self->GnuPG->setarmor(1);
|
||||
} else if (\SnappyMail\PGP\GPG::isSupported()) {
|
||||
$self = new self;
|
||||
$self->GPG = new \SnappyMail\PGP\GPG($homedir);
|
||||
} else {
|
||||
/**
|
||||
* $binary = trim(`which gpg`) ?: trim(`which gpg2`);
|
||||
|
@ -68,6 +73,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->adddecryptkey($fingerprint, $passphrase);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->adddecryptkey($fingerprint, $passphrase);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
$this->Crypt_GPG->addDecryptKey($fingerprint, $passphrase);
|
||||
return true;
|
||||
|
@ -83,6 +91,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->addencryptkey($fingerprint);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->addencryptkey($fingerprint);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
$this->Crypt_GPG->addEncryptKey($fingerprint);
|
||||
return true;
|
||||
|
@ -98,6 +109,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->addsignkey($fingerprint, $passphrase);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->addsignkey($fingerprint, $passphrase);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
$this->Crypt_GPG->addSignKey($fingerprint, $passphrase);
|
||||
return true;
|
||||
|
@ -113,6 +127,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->cleardecryptkeys();
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->cleardecryptkeys();
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
$this->Crypt_GPG->clearDecryptKeys();
|
||||
return true;
|
||||
|
@ -128,6 +145,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->clearencryptkeys();
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->clearencryptkeys();
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
$this->Crypt_GPG->clearEncryptKeys();
|
||||
return true;
|
||||
|
@ -143,6 +163,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->clearsignkeys();
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->clearsignkeys();
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
$this->Crypt_GPG->clearSignKeys();
|
||||
return true;
|
||||
|
@ -158,6 +181,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->decrypt($text);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->decrypt($text);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->decrypt($encryptedData);
|
||||
}
|
||||
|
@ -172,6 +198,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->decrypt(\file_get_contents($filename));
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->decryptFile($filename);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->decryptFile($filename, $decryptedFile = null);
|
||||
}
|
||||
|
@ -186,6 +215,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->decryptverify($text, $plaintext);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->decryptverify($text, $plaintext);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->decryptAndVerify($text, $ignoreVerifyErrors = false);
|
||||
}
|
||||
|
@ -200,6 +232,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->decryptverify(\file_get_contents($filename), $plaintext);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->decryptverifyFile($filename, $plaintext);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->decryptAndVerifyFile($filename, $decryptedFile = null, $ignoreVerifyErrors = false);
|
||||
}
|
||||
|
@ -214,6 +249,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->encrypt($plaintext);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->encrypt($plaintext);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->encrypt($plaintext);
|
||||
}
|
||||
|
@ -228,6 +266,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->encrypt(\file_get_contents($filename));
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->encryptFile($filename);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->encryptFile($filename, $encryptedFile = null);
|
||||
}
|
||||
|
@ -242,6 +283,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->encryptsign($plaintext);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->encryptsign($plaintext);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->encryptAndSign($plaintext);
|
||||
}
|
||||
|
@ -256,6 +300,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->encryptsign(\file_get_contents($filename));
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->encryptsignFile($filename);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->encryptAndSignFile($filename, $signedFile = null);
|
||||
}
|
||||
|
@ -270,6 +317,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->export($fingerprint);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->export($fingerprint);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
$this->Crypt_GPG->exportPrivateKey($fingerprint, $armor = true);
|
||||
$this->Crypt_GPG->exportPublicKey($fingerprint, $armor = true);
|
||||
|
@ -286,6 +336,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->getengineinfo();
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->getengineinfo();
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return [
|
||||
'protocol' => null,
|
||||
|
@ -305,6 +358,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->geterror();
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->geterror();
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return true;
|
||||
}
|
||||
|
@ -319,6 +375,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->geterrorinfo();
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->geterrorinfo();
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return true;
|
||||
}
|
||||
|
@ -333,6 +392,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->getprotocol();
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->getprotocol();
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return true;
|
||||
}
|
||||
|
@ -347,6 +409,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->import($keydata);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->import($keydata);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->importKey($keydata);
|
||||
}
|
||||
|
@ -361,6 +426,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->import(\file_get_contents($filename));
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->importFile($filename);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->importKeyFile($filename);
|
||||
}
|
||||
|
@ -372,32 +440,65 @@ class GnuPG
|
|||
*/
|
||||
public function keyInfo(string $pattern) : array
|
||||
{
|
||||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->keyinfo($pattern);
|
||||
/* // v1.5 Slow and fails
|
||||
return \array_merge(
|
||||
$keys = [];
|
||||
$GPG = $this->GnuPG ?: $this->GPG;
|
||||
if ($GPG) {
|
||||
// Public
|
||||
$this->GnuPG->keyinfo($pattern),
|
||||
foreach ($GPG->keyinfo($pattern) as $info) {
|
||||
if (!$info['disabled'] && !$info['expired'] && !$info['revoked']) {
|
||||
/*
|
||||
$hasPrivateKey = false;
|
||||
foreach ($info['subkeys'] as $key) {
|
||||
$hasPrivateKey |= \is_file("{$this->homedir}/private-keys-v1.d/{$key['keygrip']}.key");
|
||||
}
|
||||
*/
|
||||
foreach ($info['uids'] as $uid) {
|
||||
$id = $uid['email'];
|
||||
if (isset($keys[$id])) {
|
||||
$keys[$id]['can_sign'] = $keys[$id]['can_sign'] || $info['can_sign'];
|
||||
$keys[$id]['can_encrypt'] = $keys[$id]['can_encrypt'] || $info['can_encrypt'];
|
||||
} else {
|
||||
$keys[$id] = [
|
||||
'name' => $uid['name'],
|
||||
'email' => $uid['email'],
|
||||
// Public Key tasks
|
||||
'can_verify' => $info['can_sign'],
|
||||
'can_encrypt' => $info['can_encrypt'],
|
||||
// Private Key tasks
|
||||
'can_sign' => false,
|
||||
'can_decrypt' => false
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Private, read https://github.com/php-gnupg/php-gnupg/issues/5
|
||||
$this->GnuPG->keyinfo($pattern, 1)
|
||||
);
|
||||
*/
|
||||
foreach ($GPG->keyinfo($pattern, 1) as $info) {
|
||||
if (!$info['disabled'] && !$info['expired'] && !$info['revoked']) {
|
||||
foreach ($info['uids'] as $uid) {
|
||||
$id = $uid['email'];
|
||||
if (isset($keys[$id])) {
|
||||
$keys[$id]['can_sign'] = $keys[$id]['can_sign'] || $info['can_sign'];
|
||||
$keys[$id]['can_decrypt'] = $keys[$id]['can_decrypt'] || $info['can_encrypt'];
|
||||
} else {
|
||||
$keys[$id] = [
|
||||
'name' => $uid['name'],
|
||||
'email' => $uid['email'],
|
||||
// Public Key tasks
|
||||
'can_verify' => false,
|
||||
'can_encrypt' => false,
|
||||
// Private Key tasks
|
||||
'can_sign' => $info['can_sign'],
|
||||
'can_decrypt' => $info['can_encrypt']
|
||||
];
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with information about all keys that matches the given pattern
|
||||
*/
|
||||
public function hasPrivateKey(string $keygrip) : bool
|
||||
{
|
||||
if ($this->GnuPG || $this->Crypt_GPG) {
|
||||
return \is_file("{$this->homedir}/private-keys-v1.d/{$keygrip}.key");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if ($this->Crypt_GPG) {
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -409,6 +510,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->setarmor($armor ? 1 : 0);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->setarmor($armor ? 1 : 0);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
//$armor ? \Crypt_GPG::ARMOR_ASCII : \Crypt_GPG::ARMOR_
|
||||
return true;
|
||||
|
@ -426,6 +530,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
$this->GnuPG->seterrormode($errormode);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
$this->GPG->seterrormode($errormode);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
}
|
||||
}
|
||||
|
@ -440,6 +547,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->setsignmode($signmode);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->setsignmode($signmode);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return true;
|
||||
}
|
||||
|
@ -454,6 +564,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->sign($plaintext);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->sign($plaintext);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->sign($data, $mode = self::SIGN_MODE_NORMAL);
|
||||
}
|
||||
|
@ -468,6 +581,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->sign(\file_get_contents($filename));
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->signFile($filename);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->signFile($filename, $signedFile = null, $mode = self::SIGN_MODE_NORMAL);
|
||||
}
|
||||
|
@ -482,6 +598,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->verify($signed_text, $signature, $plaintext);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->verify($signed_text, $signature, $plaintext);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->verify($signed_text, $signature = '');
|
||||
}
|
||||
|
@ -496,6 +615,9 @@ class GnuPG
|
|||
if ($this->GnuPG) {
|
||||
return $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext);
|
||||
}
|
||||
if ($this->GPG) {
|
||||
return $this->GPG->verifyFile($filename, $signature, $plaintext);
|
||||
}
|
||||
if ($this->Crypt_GPG) {
|
||||
return $this->Crypt_GPG->verifyFile($filename, $signature = '');
|
||||
}
|
||||
|
|
|
@ -1,19 +1,8 @@
|
|||
<?php
|
||||
/**
|
||||
# gpg1 --version
|
||||
gpg (GnuPG) 1.4.23
|
||||
Pubkey: RSA, RSA-E, RSA-S, ELG-E, DSA
|
||||
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256
|
||||
Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
|
||||
Compression: Uncompressed, ZIP, ZLIB, BZIP2
|
||||
|
||||
# gpg2 --version
|
||||
gpg (GnuPG) 2.2.20
|
||||
libgcrypt 1.8.5
|
||||
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
|
||||
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256
|
||||
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
|
||||
Compression: Uncompressed, ZIP, ZLIB, BZIP2
|
||||
* This class is inspired by PEAR Crypt_GPG and PECL gnupg
|
||||
* It does not support gpg v1 because that is missing ECDH, ECDSA, EDDSA
|
||||
* It does not support gpg < v2.2.5 as they are from before 2018
|
||||
*/
|
||||
|
||||
namespace SnappyMail\PGP;
|
||||
|
@ -53,13 +42,14 @@ class GPG
|
|||
$debug = false;
|
||||
|
||||
private
|
||||
$_processHandler = null,
|
||||
$_errorHandlers = array(),
|
||||
$_statusHandlers = array(),
|
||||
$_commandBuffer,
|
||||
$_message,
|
||||
$_input,
|
||||
$_output;
|
||||
$_output,
|
||||
|
||||
$signKeys = array(),
|
||||
$encryptKeys = array(),
|
||||
$decryptKeys = array();
|
||||
|
||||
private
|
||||
$binary,
|
||||
|
@ -149,7 +139,8 @@ class GPG
|
|||
*/
|
||||
public function addDecryptKey(string $fingerprint, string $passphrase) : bool
|
||||
{
|
||||
return false;
|
||||
$this->signKeys[$fingerprint] = $passphrase;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,7 +148,8 @@ class GPG
|
|||
*/
|
||||
public function addEncryptKey(string $fingerprint) : bool
|
||||
{
|
||||
return false;
|
||||
$this->encryptKeys[$fingerprint] = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,6 +157,7 @@ class GPG
|
|||
*/
|
||||
public function addSignKey(string $fingerprint, ?string $passphrase) : bool
|
||||
{
|
||||
$this->decryptKeys[$fingerprint] = $passphrase;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -173,7 +166,8 @@ class GPG
|
|||
*/
|
||||
public function clearDecryptKeys() : bool
|
||||
{
|
||||
return false;
|
||||
$this->decryptKeys = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,7 +175,8 @@ class GPG
|
|||
*/
|
||||
public function clearEncryptKeys() : bool
|
||||
{
|
||||
return false;
|
||||
$this->encryptKeys = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,7 +184,8 @@ class GPG
|
|||
*/
|
||||
public function clearSignKeys() : bool
|
||||
{
|
||||
return false;
|
||||
$this->signKeys = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -301,12 +297,59 @@ class GPG
|
|||
return false;
|
||||
}
|
||||
|
||||
public function addPassphrase($key, $passphrase)
|
||||
{
|
||||
$this->passphrases[$key] = $passphrase;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function _importKey($input) /*: array|false*/
|
||||
{
|
||||
$arguments = array('--import');
|
||||
|
||||
$envKeys = array();
|
||||
if (empty($this->passphrases)) {
|
||||
$arguments[] = '--batch';
|
||||
} else {
|
||||
foreach ($this->passphrases as $keyId => $key) {
|
||||
$envKeys[$keyId] = \is_array($key) ? $key['passphrase'] : $key;
|
||||
}
|
||||
}
|
||||
$_ENV['PINENTRY_USER_DATA'] = \json_encode($envKeys);
|
||||
|
||||
$this->reset();
|
||||
$this->setInput($input);
|
||||
$result = $this->exec($arguments);
|
||||
|
||||
foreach ($result['status'] as $line) {
|
||||
if (\strpos($line, 'IMPORT_RES')) {
|
||||
$line = \explode(' ', \explode('IMPORT_RES ', $line)[1]);
|
||||
return [
|
||||
'imported' => (int) $line[2],
|
||||
'unchanged' => (int) $line[4],
|
||||
'newuserids' => (int) $line[5],
|
||||
'newsubkeys' => (int) $line[6],
|
||||
'secretimported' => (int) $line[10],
|
||||
'secretunchanged' => (int) $line[11],
|
||||
'newsignatures' => (int) $line[7],
|
||||
'skippedkeys' => (int) $line[12],
|
||||
'fingerprint' => ''
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a key
|
||||
*/
|
||||
public function import(string $keydata) /*: array|false*/
|
||||
{
|
||||
return false;
|
||||
if (!$keydata) {
|
||||
throw new \Exception('No valid input data found.');
|
||||
}
|
||||
return $this->_importKey($keydata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -314,104 +357,48 @@ class GPG
|
|||
*/
|
||||
public function importFile(string $filename) /*: array|false*/
|
||||
{
|
||||
$fp = \fopen($filename, 'rb');
|
||||
try {
|
||||
if (!$fp) {
|
||||
throw new \Exception("Could not open file '{$filename}'");
|
||||
}
|
||||
return $this->_importKey($fp);
|
||||
} finally {
|
||||
$fp && \fclose($fp);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteKey(string $keyId, bool $private)
|
||||
{
|
||||
$key = $this->keyInfo($keyId, $private ? 1 : 0);
|
||||
if (!$key) {
|
||||
return false;
|
||||
// throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||
}
|
||||
if (!$private && $this->keyInfo($keyId, 1)) {
|
||||
return false;
|
||||
throw new \Exception('Delete private key first: ' . $keyId);
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
$result = $this->exec(array(
|
||||
'--batch',
|
||||
'--yes',
|
||||
$private ? '--delete-secret-key' : '--delete-key',
|
||||
\escapeshellarg($key[0]['subkeys'][0]['fingerprint'])
|
||||
));
|
||||
|
||||
// $result['status'][0] = '[GNUPG:] ERROR keylist.getkey 17'
|
||||
// $result['errors'][0] = 'gpg: error reading key: No public key'
|
||||
|
||||
// print_r($result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with information about all keys that matches the given pattern
|
||||
*/
|
||||
public function keyInfo(string $pattern) : array
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with information about all keys that matches the given pattern
|
||||
*/
|
||||
public function hasPrivateKey(string $keygrip) : bool
|
||||
{
|
||||
return \is_file("{$this->options['homedir']}/private-keys-v1.d/{$keygrip}.key");
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle armored output
|
||||
* When true the output is ASCII
|
||||
*/
|
||||
public function setArmor(bool $armor = true) : bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a given text
|
||||
*/
|
||||
public function sign(string $plaintext) /*: string|false*/
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a given file
|
||||
*/
|
||||
public function signFile(string $filename) /*: string|false*/
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a signed text
|
||||
*/
|
||||
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a signed file
|
||||
*/
|
||||
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC 4880
|
||||
* https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.5
|
||||
*/
|
||||
public function signatureIssuer(string $signature) /*: array|false*/
|
||||
{
|
||||
if (preg_match('/-----BEGIN PGP SIGNATURE-----(.+)-----END PGP SIGNATURE-----/', $signature, $match)) {
|
||||
// TODO: use https://github.com/singpolyma/openpgp-php ?
|
||||
$binary = \base64_decode(\trim($match[1]));
|
||||
return \strtoupper(\bin2hex(\substr($binary, 24, 8)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This hangs with:
|
||||
* gpg: can't connect to the agent: IPC connect call failed
|
||||
*/
|
||||
public function getPrivateKeys($keyId = '') : array
|
||||
public function keyInfo(string $pattern, int $private = 0) : array
|
||||
{
|
||||
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
||||
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
||||
|
@ -420,61 +407,21 @@ class GPG
|
|||
'--with-fingerprint',
|
||||
'--with-fingerprint',
|
||||
'--fixed-list-mode',
|
||||
'--list-secret-keys'
|
||||
$private ? '--list-secret-keys' : '--list-public-keys'
|
||||
);
|
||||
if ($keyId) {
|
||||
if ($pattern) {
|
||||
$arguments[] = '--utf8-strings';
|
||||
$arguments[] = \escapeshellarg($keyId);
|
||||
$arguments[] = \escapeshellarg($pattern);
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
$this->reset();
|
||||
$this->setOutput($output);
|
||||
$this->exec($arguments);
|
||||
|
||||
echo $output;
|
||||
return [];
|
||||
|
||||
$fingerprints = array();
|
||||
foreach (\explode(PHP_EOL, $output) as $line) {
|
||||
$lineExp = \explode(':', $line);
|
||||
if ('fpr' === $lineExp[0]) {
|
||||
$fingerprints[] = $lineExp[9];
|
||||
}
|
||||
}
|
||||
return $fingerprints;
|
||||
}
|
||||
|
||||
public function getPublicKeys($keyId = '') : array
|
||||
{
|
||||
// $privateKeyFingerprints = $this->getPrivateKeys($keyId);
|
||||
|
||||
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
||||
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
||||
$arguments = array(
|
||||
'--with-colons',
|
||||
'--with-fingerprint',
|
||||
'--with-fingerprint',
|
||||
'--fixed-list-mode',
|
||||
'--list-public-keys' // --list-keys ?
|
||||
);
|
||||
if ($keyId) {
|
||||
$arguments[] = '--utf8-strings';
|
||||
$arguments[] = \escapeshellarg($keyId);
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
$this->reset();
|
||||
$this->setOutput($output);
|
||||
$this->exec($arguments);
|
||||
$result = $this->exec($arguments);
|
||||
|
||||
$keys = array();
|
||||
$key = null; // current key
|
||||
$subKey = null; // current sub-key
|
||||
|
||||
foreach (\explode(PHP_EOL, $output) as $line) {
|
||||
foreach (\explode(PHP_EOL, $result['output']) as $line) {
|
||||
$tokens = \explode(':', $line);
|
||||
|
||||
switch ($tokens[0])
|
||||
|
@ -482,6 +429,7 @@ return [];
|
|||
case 'tru':
|
||||
break;
|
||||
|
||||
case 'sec':
|
||||
case 'pub':
|
||||
// new primary key means last key should be added to the array
|
||||
if ($key !== null) {
|
||||
|
@ -491,20 +439,21 @@ return [];
|
|||
'disabled' => false,
|
||||
'expired' => false,
|
||||
'revoked' => false,
|
||||
'is_secret' => false,
|
||||
'is_secret' => 'ssb' === $tokens[0],
|
||||
'can_sign' => \str_contains($tokens[11], 's'),
|
||||
'can_encrypt' => \str_contains($tokens[11], 'e'),
|
||||
'uids' => [],
|
||||
'subkeys' => []
|
||||
];
|
||||
// Fall through to add (sub)key
|
||||
case 'sub':
|
||||
// Fall through to add subkey
|
||||
case 'ssb': // secure subkey
|
||||
case 'sub': // public subkey
|
||||
$key['subkeys'][] = [
|
||||
'fingerprint' => '', // fpr:::::::::....:
|
||||
'keyid' => $tokens[4],
|
||||
'timestamp' => $tokens[5],
|
||||
'expires' => $tokens[6],
|
||||
'is_secret' => false,
|
||||
'is_secret' => 'ssb' === $tokens[0],
|
||||
'invalid' => false,
|
||||
// escaESCA
|
||||
'can_encrypt' => \str_contains($tokens[11], 'e'),
|
||||
|
@ -523,6 +472,10 @@ return [];
|
|||
$key['subkeys'][\array_key_last($key['subkeys'])]['fingerprint'] = $tokens[9];
|
||||
break;
|
||||
|
||||
case 'grp':
|
||||
$key['subkeys'][\array_key_last($key['subkeys'])]['keygrip'] = $tokens[9];
|
||||
break;
|
||||
|
||||
case 'uid':
|
||||
$string = \stripcslashes($tokens[9]); // as per documentation
|
||||
$name = '';
|
||||
|
@ -570,45 +523,186 @@ return [];
|
|||
return $keys;
|
||||
}
|
||||
|
||||
private function _debug(string $msg)
|
||||
/**
|
||||
* Toggle armored output
|
||||
* When true the output is ASCII
|
||||
*/
|
||||
public function setArmor(bool $armor = true) : bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a given text
|
||||
*/
|
||||
public function sign(string $plaintext) /*: string|false*/
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a given file
|
||||
*/
|
||||
public function signFile(string $filename) /*: string|false*/
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function _verify($input, string $signature)
|
||||
{
|
||||
$this->reset();
|
||||
|
||||
$arguments = array('--verify');
|
||||
if ('' === $signature) {
|
||||
// signed or clearsigned data
|
||||
$this->setInput($input);
|
||||
} else {
|
||||
// detached signature
|
||||
$this->setInput($signature);
|
||||
$this->_message =& $input;
|
||||
// Signed data goes in FD_MESSAGE, detached signature data goes in FD_INPUT.
|
||||
$arguments[] = '--enable-special-filenames';
|
||||
$arguments[] = '- "-&' . self::FD_MESSAGE . '"';
|
||||
}
|
||||
|
||||
$result = $this->exec($arguments);
|
||||
|
||||
$signatures = [];
|
||||
foreach ($result['status'] as $line) {
|
||||
$tokens = \explode(' ', $line);
|
||||
switch ($tokens[0])
|
||||
{
|
||||
case 'VERIFICATION_COMPLIANCE_MODE':
|
||||
case 'TRUST_FULLY':
|
||||
break;
|
||||
|
||||
case 'EXPSIG':
|
||||
case 'EXPKEYSIG':
|
||||
case 'REVKEYSIG':
|
||||
case 'BADSIG':
|
||||
case 'ERRSIG':
|
||||
case 'GOODSIG':
|
||||
$signatures[] = [
|
||||
'fingerprint' => '',
|
||||
'validity' => 0,
|
||||
'timestamp' => 0,
|
||||
'status' => 'GOODSIG' === $tokens[0] ? 0 : 1,
|
||||
'summary' => 'GOODSIG' === $tokens[0] ? 0 : 4,
|
||||
'keyid' => $tokens[1],
|
||||
'uid' => \rawurldecode(\implode(' ', \array_splice($tokens, 2))),
|
||||
'valid' => false
|
||||
];
|
||||
break;
|
||||
|
||||
case 'VALIDSIG':
|
||||
$last = \array_key_last($signatures);
|
||||
$signatures[$last]['fingerprint'] = $tokens[1];
|
||||
$signatures[$last]['timestamp'] = (int) $tokens[3];
|
||||
$signatures[$last]['expires'] = (int) $tokens[4];
|
||||
$signatures[$last]['version'] = (int) $tokens[5];
|
||||
// $signatures[$last]['reserved'] = (int) $tokens[6];
|
||||
// $signatures[$last]['pubkey-algo'] = (int) $tokens[7];
|
||||
// $signatures[$last]['hash-algo'] = (int) $tokens[8];
|
||||
// $signatures[$last]['sig-class'] = $tokens[9];
|
||||
// $signatures[$last]['primary-fingerprint'] = $tokens[10];
|
||||
$signatures[$last]['valid'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $signatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a signed text
|
||||
*/
|
||||
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/
|
||||
{
|
||||
return $this->_verify($signed_text, $signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a signed file
|
||||
*/
|
||||
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/
|
||||
{
|
||||
$fp = \fopen($filename, 'rb');
|
||||
try {
|
||||
if (!$fp) {
|
||||
throw new \Exception("Could not open file '{$filename}'");
|
||||
}
|
||||
return $this->_verify($fp, $signature);
|
||||
} finally {
|
||||
$fp && \fclose($fp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a signed file
|
||||
*/
|
||||
public function verifyStream($fp, string $signature, string &$plaintext = null) /*: array|false*/
|
||||
{
|
||||
if (!$fp || !\is_resource($fp)) {
|
||||
throw new \Exception('Invalid stream resource');
|
||||
}
|
||||
return $this->_verify($fp, $signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC 4880
|
||||
* https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.5
|
||||
*/
|
||||
public function signatureIssuer(string $signature) /*: array|false*/
|
||||
{
|
||||
if (preg_match('/-----BEGIN PGP SIGNATURE-----(.+)-----END PGP SIGNATURE-----/', $signature, $match)) {
|
||||
// TODO: use https://github.com/singpolyma/openpgp-php ?
|
||||
$binary = \base64_decode(\trim($match[1]));
|
||||
return \strtoupper(\bin2hex(\substr($binary, 24, 8)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function _debug(string $msg) : void
|
||||
{
|
||||
if ($this->debug) {
|
||||
echo $msg . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function setOutput(&$output)
|
||||
private function setInput(&$input) : void
|
||||
{
|
||||
$this->_output =& $output;
|
||||
$this->_input =& $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the GPG engine, preparing it for a new operation
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @see Crypt_GPG_Engine::run()
|
||||
* @see Crypt_GPG_Engine::setOperation()
|
||||
*/
|
||||
public function reset()
|
||||
private function setOutput($output) : void
|
||||
{
|
||||
$this->_output = \is_resource($output) ? $output : null;
|
||||
}
|
||||
|
||||
private function reset() : void
|
||||
{
|
||||
$this->_message = null;
|
||||
$this->_input = null;
|
||||
$this->_output = '';
|
||||
$this->_output = null;
|
||||
$this->_commandBuffer = '';
|
||||
|
||||
$this->_errorHandlers = array();
|
||||
$this->_statusHandlers = array();
|
||||
/*
|
||||
if ($this->debug) {
|
||||
$this->addStatusHandler(array($this, '_handleDebugStatus'));
|
||||
$this->addErrorHandler(array($this, '_handleDebugError'));
|
||||
}
|
||||
|
||||
$this->_processHandler = new Crypt_GPG_ProcessHandler($this);
|
||||
$this->addStatusHandler(array($this->_processHandler, 'handleStatus'));
|
||||
$this->addErrorHandler(array($this->_processHandler, 'handleError'));
|
||||
*/
|
||||
}
|
||||
|
||||
public function agent()
|
||||
|
@ -617,7 +711,7 @@ return [];
|
|||
// echo `gpg-agent --daemon --homedir $home 2>&1`;
|
||||
}
|
||||
|
||||
private function exec(array $arguments)
|
||||
private function exec(array $arguments) /*: array|false*/
|
||||
{
|
||||
if (\version_compare($this->version, '2.2.5', '<')) {
|
||||
// Too old (<2018)
|
||||
|
@ -717,9 +811,8 @@ return [];
|
|||
$messageComplete = true;
|
||||
}
|
||||
|
||||
if (\is_string($this->_output)) {
|
||||
$outputBuffer =& $this->_output;
|
||||
}
|
||||
$status = [];
|
||||
$errors = [];
|
||||
|
||||
// convenience variables
|
||||
$fdInput = $proc_pipes[self::FD_INPUT];
|
||||
|
@ -727,7 +820,7 @@ return [];
|
|||
$fdError = $proc_pipes[self::FD_ERROR];
|
||||
$fdStatus = $proc_pipes[self::FD_STATUS];
|
||||
$fdCommand = $proc_pipes[self::FD_COMMAND];
|
||||
$fdMessage = $this->_openPipes->get(self::FD_MESSAGE);
|
||||
$fdMessage = $proc_pipes[self::FD_MESSAGE];
|
||||
|
||||
// select loop delay in milliseconds
|
||||
$delay = 0;
|
||||
|
@ -774,7 +867,7 @@ return [];
|
|||
}
|
||||
|
||||
// set up output streams
|
||||
if ($outputBuffer != '' && \is_resource($this->_output)) {
|
||||
if ('' != $outputBuffer && $this->_output) {
|
||||
$outputStreams[] = $this->_output;
|
||||
}
|
||||
|
||||
|
@ -920,17 +1013,8 @@ return [];
|
|||
$this->_debug('GPG error stream ready for reading');
|
||||
$this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from GPG error');
|
||||
foreach ($this->_openPipes->readPipeLines(self::FD_ERROR) as $line) {
|
||||
if (!$this->_errorHandlers) {
|
||||
$this->debug ? $this->_debug('ERROR ' . $line) : \trigger_error($line, E_USER_WARNING);
|
||||
}
|
||||
foreach ($this->_errorHandlers as $handler) {
|
||||
\array_unshift($handler['args'], $line);
|
||||
\call_user_func_array(
|
||||
$handler['callback'],
|
||||
$handler['args']
|
||||
);
|
||||
\array_shift($handler['args']);
|
||||
}
|
||||
$errors[] = $line;
|
||||
$this->debug && $this->_debug("\t{$line}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -940,20 +1024,11 @@ return [];
|
|||
$this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from GPG status');
|
||||
// pass lines to status handlers
|
||||
foreach ($this->_openPipes->readPipeLines(self::FD_STATUS) as $line) {
|
||||
if (!$this->_statusHandlers) {
|
||||
$this->debug ? $this->_debug($line) : \trigger_error($line);
|
||||
}
|
||||
// only pass lines beginning with magic prefix
|
||||
if ('[GNUPG:] ' == \substr($line, 0, 9)) {
|
||||
$line = \substr($line, 9);
|
||||
foreach ($this->_statusHandlers as $handler) {
|
||||
\array_unshift($handler['args'], $line);
|
||||
\call_user_func_array(
|
||||
$handler['callback'],
|
||||
$handler['args']
|
||||
);
|
||||
\array_shift($handler['args']);
|
||||
}
|
||||
$status[] = $line;
|
||||
$this->debug && $this->_debug("\t{$line}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -992,10 +1067,18 @@ return [];
|
|||
$this->_debug('END PROCESSING');
|
||||
|
||||
$this->proc_close();
|
||||
|
||||
return [
|
||||
'output' => $outputBuffer,
|
||||
'status' => $status,
|
||||
'errors' => $errors
|
||||
];
|
||||
}
|
||||
|
||||
private function proc_close()
|
||||
private function proc_close() : int
|
||||
{
|
||||
$exitCode = 0;
|
||||
|
||||
// clear PINs from environment if they were set
|
||||
$_ENV['PINENTRY_USER_DATA'] = null;
|
||||
|
||||
|
@ -1018,18 +1101,9 @@ return [];
|
|||
if ($exitCode > 0) {
|
||||
$this->_debug('=> subprocess returned an unexpected exit code: ' . $exitCode);
|
||||
}
|
||||
|
||||
// close file handles before throwing an exception
|
||||
if (\is_resource($this->_input)) {
|
||||
\fclose($this->_input);
|
||||
}
|
||||
|
||||
if (\is_resource($this->_output)) {
|
||||
\fclose($this->_output);
|
||||
}
|
||||
|
||||
$this->_processHandler && $this->_processHandler->throwException($exitCode);
|
||||
}
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
private static function findBinary($name) : ?string
|
||||
|
@ -1131,7 +1205,7 @@ class GpgProcPipes
|
|||
// the pipe was seleted for writing, we assume it was EPIPE.
|
||||
// There's no way to get the actual error code in PHP. See
|
||||
// PHP Bug #39598. https://bugs.php.net/bug.php?id=39598
|
||||
$this->pipes->close($number);
|
||||
$this->close($number);
|
||||
}
|
||||
return $length ?: 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue