Crypt/GPG/ProcessHandler.php
public function handleStatus($line)
    {
        $tokens = explode(' ', $line);
        switch ($tokens[0]) {
        case 'NODATA':
            $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
            break;

        case 'DECRYPTION_OKAY':
            // If the message is encrypted, this is the all-clear signal.
            $this->data['DecryptionOkay'] = true;
            $this->errorCode = Crypt_GPG::ERROR_NONE;
            break;

        case 'DELETE_PROBLEM':
            if ($tokens[1] == '1') {
                $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
                break;
            } elseif ($tokens[1] == '2') {
                $this->errorCode = Crypt_GPG::ERROR_DELETE_PRIVATE_KEY;
                break;
            }
            break;

        case 'IMPORT_OK':
            $this->data['Import']['fingerprint'] = $tokens[2];

            if (empty($this->data['Import']['fingerprints'])) {
                $this->data['Import']['fingerprints'] = array($tokens[2]);
            } else if (!in_array($tokens[2], $this->data['Import']['fingerprints'])) {
                $this->data['Import']['fingerprints'][] = $tokens[2];
            }

            break;

        case 'IMPORT_RES':
            $this->data['Import']['public_imported']   = intval($tokens[3]);
            $this->data['Import']['public_unchanged']  = intval($tokens[5]);
            $this->data['Import']['private_imported']  = intval($tokens[11]);
            $this->data['Import']['private_unchanged'] = intval($tokens[12]);
            break;

        case 'NO_PUBKEY':
        case 'NO_SECKEY':
            $this->data['ErrorKeyId'] = $tokens[1];

            if ($this->errorCode != Crypt_GPG::ERROR_MISSING_PASSPHRASE
                && $this->errorCode != Crypt_GPG::ERROR_BAD_PASSPHRASE
            ) {
                $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
            }

            // note: this message is also received if there are multiple
            // recipients and a previous key had a correct passphrase.
            $this->data['MissingKeys'][$tokens[1]] = $tokens[1];

            // @FIXME: remove missing passphrase registered in ENC_TO handler
            //         This is for GnuPG 2.1
            unset($this->data['MissingPassphrases'][$tokens[1]]);
            break;

        case 'KEY_CONSIDERED':
            // In GnuPG 2.1.x exporting/importing a secret key requires passphrase
            // However, no NEED_PASSPRASE is returned, https://bugs.gnupg.org/gnupg/issue2667
            // So, handling KEY_CONSIDERED and GET_HIDDEN is needed.
            if (!array_key_exists('KeyConsidered', $this->data)) {
                $this->data['KeyConsidered'] = $tokens[1];
            }
            break;

        case 'USERID_HINT':
            // remember the user id for pretty exception messages
            // GnuPG 2.1.15 gives me: "USERID_HINT 0000000000000000 [?]"
            $keyId = $tokens[1];
            if (strcspn($keyId, '0')) {
                $username = implode(' ', array_splice($tokens, 2));
                $this->data['BadPassphrases'][$keyId] = $username;
            }
            break;

        case 'ENC_TO':
            // Now we know the message is encrypted. Set flag to check if
            // decryption succeeded.
            $this->data['DecryptionOkay'] = false;

            // this is the new key message
            $this->data['CurrentSubKeyId'] = $keyId = $tokens[1];

            // For some reason in GnuPG 2.1.11 I get only ENC_TO and no
            // NEED_PASSPHRASE/MISSING_PASSPHRASE/USERID_HINT
            // This is not needed for GnuPG 2.1.15
            if (!empty($_ENV['PINENTRY_USER_DATA'])) {
                $passphrases = json_decode($_ENV['PINENTRY_USER_DATA'], true);
            } else {
                $passphrases = array();
            }

            // @TODO: Get user name/email
            $this->data['BadPassphrases'][$keyId] = $keyId;
            if (empty($passphrases) || empty($passphrases[$keyId])) {
                $this->data['MissingPassphrases'][$keyId] = $keyId;
            }
            break;

        case 'GOOD_PASSPHRASE':
            // if we got a good passphrase, remove the key from the list of
            // bad passphrases.
            if (isset($this->data['CurrentSubKeyId'])) {
                unset($this->data['BadPassphrases'][$this->data['CurrentSubKeyId']]);
                unset($this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]);
            }

            $this->needPassphrase--;
            break;

        case 'BAD_PASSPHRASE':
            $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
            break;

        case 'MISSING_PASSPHRASE':
            if (isset($this->data['CurrentSubKeyId'])) {
                $this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]
                    = $this->data['CurrentSubKeyId'];
            }

            $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
            break;

        case 'GET_HIDDEN':
            if ($tokens[1] == 'passphrase.enter' && isset($this->data['KeyConsidered'])) {
                $tokens[1] = $this->data['KeyConsidered'];
            } else {
                break;
            }
            // no break

        case 'NEED_PASSPHRASE':
            $passphrase = $this->getPin($tokens[1]);

            $this->engine->sendCommand($passphrase);

            if ($passphrase === '') {
                $this->needPassphrase++;
            }
            break;

        case 'SIG_CREATED':
            $this->data['SigCreated'] = $line;
            break;

        case 'SIG_ID':
            // note: signature id comes before new signature line and may not
            // exist for some signature types
            $this->data['SignatureId'] = $tokens[1];
            break;

        case 'EXPSIG':
        case 'EXPKEYSIG':
        case 'REVKEYSIG':
        case 'BADSIG':
        case 'ERRSIG':
            $this->errorCode = Crypt_GPG::ERROR_BAD_SIGNATURE;
            // no break
        case 'GOODSIG':
            $signature = new Crypt_GPG_Signature();

            // if there was a signature id, set it on the new signature
            if (!empty($this->data['SignatureId'])) {
                $signature->setId($this->data['SignatureId']);
                $this->data['SignatureId'] = '';
            }

            // Detect whether fingerprint or key id was returned and set
            // signature values appropriately. Key ids are strings of either
            // 16 or 8 hexadecimal characters. Fingerprints are strings of 40
            // hexadecimal characters. The key id is the last 16 characters of
            // the key fingerprint.
            if (mb_strlen($tokens[1], '8bit') > 16) {
                $signature->setKeyFingerprint($tokens[1]);
                $signature->setKeyId(mb_substr($tokens[1], -16, null, '8bit'));
            } else {
                $signature->setKeyId($tokens[1]);
            }

            // get user id string
            if ($tokens[0] != 'ERRSIG') {
                $string = implode(' ', array_splice($tokens, 2));
                $string = rawurldecode($string);

                $signature->setUserId(Crypt_GPG_UserId::parse($string));
            }

            $this->data['Signatures'][] = $signature;
            break;

        case 'VALIDSIG':
            if (empty($this->data['Signatures'])) {
                break;
            }

            $signature = end($this->data['Signatures']);

            $signature->setValid(true);
            $signature->setKeyFingerprint($tokens[1]);

            if (strpos($tokens[3], 'T') === false) {
                $signature->setCreationDate($tokens[3]);
            } else {
                $signature->setCreationDate(strtotime($tokens[3]));
            }

            if (array_key_exists(4, $tokens)) {
                if (strpos($tokens[4], 'T') === false) {
                    $signature->setExpirationDate($tokens[4]);
                } else {
                    $signature->setExpirationDate(strtotime($tokens[4]));
                }
            }

            break;

        case 'KEY_CREATED':
            if (isset($this->data['Handle']) && $tokens[3] == $this->data['Handle']) {
                $this->data['KeyCreated'] = $tokens[2];
            }
            break;

        case 'KEY_NOT_CREATED':
            if (isset($this->data['Handle']) && $tokens[1] == $this->data['Handle']) {
                $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_CREATED;
            }
            break;

        case 'PROGRESS':
            // todo: at some point, support reporting status async
            break;

        // GnuPG 2.1 uses FAILURE and ERROR responses
        case 'FAILURE':
        case 'ERROR':
            $errnum  = (int) $tokens[2];
            $source  = $errnum >> 24;
            $errcode = $errnum & 0xFFFFFF;

            switch ($errcode) {
            case 11: // bad passphrase
            case 87: // bad PIN
                $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
                break;

            case 177: // no passphrase
            case 178: // no PIN
                $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
                break;

            case 58:
                $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
                break;
            }

            break;
        }
    }
public function handleStatus($line)
    {
        $tokens = explode(' ', $line);
        switch ($tokens[0]) {
        case 'NODATA':
            $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
            break;

        case 'DECRYPTION_OKAY':
            // If the message is encrypted, this is the all-clear signal.
            $this->data['DecryptionOkay'] = true;
            $this->errorCode = Crypt_GPG::ERROR_NONE;
            break;

        case 'DELETE_PROBLEM':
            if ($tokens[1] == '1') {
                $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
                break;
            } elseif ($tokens[1] == '2') {
                $this->errorCode = Crypt_GPG::ERROR_DELETE_PRIVATE_KEY;
                break;
            }
            break;

        case 'IMPORT_OK':
            $this->data['Import']['fingerprint'] = $tokens[2];

            if (empty($this->data['Import']['fingerprints'])) {
                $this->data['Import']['fingerprints'] = array($tokens[2]);
            } else if (!in_array($tokens[2], $this->data['Import']['fingerprints'])) {
                $this->data['Import']['fingerprints'][] = $tokens[2];
            }

            break;

        case 'IMPORT_RES':
            $this->data['Import']['public_imported']   = intval($tokens[3]);
            $this->data['Import']['public_unchanged']  = intval($tokens[5]);
            $this->data['Import']['private_imported']  = intval($tokens[11]);
            $this->data['Import']['private_unchanged'] = intval($tokens[12]);
            break;

        case 'NO_PUBKEY':
        case 'NO_SECKEY':
            $this->data['ErrorKeyId'] = $tokens[1];

            if ($this->errorCode != Crypt_GPG::ERROR_MISSING_PASSPHRASE
                && $this->errorCode != Crypt_GPG::ERROR_BAD_PASSPHRASE
            ) {
                $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
            }

            // note: this message is also received if there are multiple
            // recipients and a previous key had a correct passphrase.
            $this->data['MissingKeys'][$tokens[1]] = $tokens[1];

            // @FIXME: remove missing passphrase registered in ENC_TO handler
            //         This is for GnuPG 2.1
            unset($this->data['MissingPassphrases'][$tokens[1]]);
            break;

        case 'KEY_CONSIDERED':
            // In GnuPG 2.1.x exporting/importing a secret key requires passphrase
            // However, no NEED_PASSPRASE is returned, https://bugs.gnupg.org/gnupg/issue2667
            // So, handling KEY_CONSIDERED and GET_HIDDEN is needed.
            if (!array_key_exists('KeyConsidered', $this->data)) {
                $this->data['KeyConsidered'] = $tokens[1];
            }
            break;

        case 'USERID_HINT':
            // remember the user id for pretty exception messages
            // GnuPG 2.1.15 gives me: "USERID_HINT 0000000000000000 [?]"
            $keyId = $tokens[1];
            if (strcspn($keyId, '0')) {
                $username = implode(' ', array_splice($tokens, 2));
                $this->data['BadPassphrases'][$keyId] = $username;
            }
            break;

        case 'ENC_TO':
            // Now we know the message is encrypted. Set flag to check if
            // decryption succeeded.
            $this->data['DecryptionOkay'] = false;

            // this is the new key message
            $this->data['CurrentSubKeyId'] = $keyId = $tokens[1];

            // For some reason in GnuPG 2.1.11 I get only ENC_TO and no
            // NEED_PASSPHRASE/MISSING_PASSPHRASE/USERID_HINT
            // This is not needed for GnuPG 2.1.15
            if (!empty($_ENV['PINENTRY_USER_DATA'])) {
                $passphrases = json_decode($_ENV['PINENTRY_USER_DATA'], true);
            } else {
                $passphrases = array();
            }

            // @TODO: Get user name/email
            $this->data['BadPassphrases'][$keyId] = $keyId;
            if (empty($passphrases) || empty($passphrases[$keyId])) {
                $this->data['MissingPassphrases'][$keyId] = $keyId;
            }
            break;

        case 'GOOD_PASSPHRASE':
            // if we got a good passphrase, remove the key from the list of
            // bad passphrases.
            if (isset($this->data['CurrentSubKeyId'])) {
                unset($this->data['BadPassphrases'][$this->data['CurrentSubKeyId']]);
                unset($this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]);
            }

            $this->needPassphrase--;
            break;

        case 'BAD_PASSPHRASE':
            $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
            break;

        case 'MISSING_PASSPHRASE':
            if (isset($this->data['CurrentSubKeyId'])) {
                $this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]
                    = $this->data['CurrentSubKeyId'];
            }

            $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
            break;

        case 'GET_HIDDEN':
            if ($tokens[1] == 'passphrase.enter' && isset($this->data['KeyConsidered'])) {
                $tokens[1] = $this->data['KeyConsidered'];
            } else {
                break;
            }
            // no break

        case 'NEED_PASSPHRASE':
            $passphrase = $this->getPin($tokens[1]);

            $this->engine->sendCommand($passphrase);

            if ($passphrase === '') {
                $this->needPassphrase++;
            }
            break;

        case 'SIG_CREATED':
            $this->data['SigCreated'] = $line;
            break;

        case 'SIG_ID':
            // note: signature id comes before new signature line and may not
            // exist for some signature types
            $this->data['SignatureId'] = $tokens[1];
            break;

        case 'EXPSIG':
        case 'EXPKEYSIG':
        case 'REVKEYSIG':
        case 'BADSIG':
        case 'ERRSIG':
            $this->errorCode = Crypt_GPG::ERROR_BAD_SIGNATURE;
            // no break
        case 'GOODSIG':
            $signature = new Crypt_GPG_Signature();

            // if there was a signature id, set it on the new signature
            if (!empty($this->data['SignatureId'])) {
                $signature->setId($this->data['SignatureId']);
                $this->data['SignatureId'] = '';
            }

            // Detect whether fingerprint or key id was returned and set
            // signature values appropriately. Key ids are strings of either
            // 16 or 8 hexadecimal characters. Fingerprints are strings of 40
            // hexadecimal characters. The key id is the last 16 characters of
            // the key fingerprint.
            if (mb_strlen($tokens[1], '8bit') > 16) {
                $signature->setKeyFingerprint($tokens[1]);
                $signature->setKeyId(mb_substr($tokens[1], -16, null, '8bit'));
            } else {
                $signature->setKeyId($tokens[1]);
            }

            // get user id string
            if ($tokens[0] != 'ERRSIG') {
                $string = implode(' ', array_splice($tokens, 2));
                $string = rawurldecode($string);

                $signature->setUserId(Crypt_GPG_UserId::parse($string));
            }

            $this->data['Signatures'][] = $signature;
            break;

        case 'VALIDSIG':
            if (empty($this->data['Signatures'])) {
                break;
            }

            $signature = end($this->data['Signatures']);

            $signature->setValid(true);
            $signature->setKeyFingerprint($tokens[1]);

            if (strpos($tokens[3], 'T') === false) {
                $signature->setCreationDate($tokens[3]);
            } else {
                $signature->setCreationDate(strtotime($tokens[3]));
            }

            if (array_key_exists(4, $tokens)) {
                if (strpos($tokens[4], 'T') === false) {
                    $signature->setExpirationDate($tokens[4]);
                } else {
                    $signature->setExpirationDate(strtotime($tokens[4]));
                }
            }

            break;

        case 'KEY_CREATED':
            if (isset($this->data['Handle']) && $tokens[3] == $this->data['Handle']) {
                $this->data['KeyCreated'] = $tokens[2];
            }
            break;

        case 'KEY_NOT_CREATED':
            if (isset($this->data['Handle']) && $tokens[1] == $this->data['Handle']) {
                $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_CREATED;
            }
            break;

        case 'PROGRESS':
            // todo: at some point, support reporting status async
            break;

        // GnuPG 2.1 uses FAILURE and ERROR responses
        case 'FAILURE':
        case 'ERROR':
            $errnum  = (int) $tokens[2];
            $source  = $errnum >> 24;
            $errcode = $errnum & 0xFFFFFF;

            switch ($errcode) {
            case 11: // bad passphrase
            case 87: // bad PIN
                $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
                break;

            case 177: // no passphrase
            case 178: // no PIN
                $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
                break;

            case 58:
                $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
                break;
            }

            break;
        }
    }
Handles error values in the status output from GPG

This method is responsible for setting the
{@link self::$errorCode}. See <b>doc/DETAILS</b> in the
{@link http://www.gnupg.org/download/ GPG distribution} for detailed
information on GPG's status output.

@param string $line the status line to handle.

@return void