/*
 * Copyright (C) 2003-2005  Justin Karneges <justin@affinix.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 */

#ifndef GPGOP_H
#define GPGOP_H

#include "qpipe.h"
#include <QtCrypto>

namespace gpgQCAPlugin {

class GpgOp : public QObject
{
    Q_OBJECT
public:
    enum Type
    {
        Check,             // --version
        SecretKeyringFile, // --list-secret-keys
        PublicKeyringFile, // --list-public-keys
        SecretKeys,        // --fixed-list-mode --with-colons --list-secret-keys
        PublicKeys,        // --fixed-list-mode --with-colons --list-public-keys
        Encrypt,           // --encrypt
        Decrypt,           // --decrypt
        Sign,              // --sign
        SignAndEncrypt,    // --sign --encrypt
        SignClearsign,     // --clearsign
        SignDetached,      // --detach-sign
        Verify,            // --verify
        VerifyDetached,    // --verify
        Import,            // --import
        Export,            // --export
        DeleteKey          // --delete-key
    };

    enum VerifyResult
    {
        VerifyGood, // good sig
        VerifyBad,  // bad sig
        VerifyNoKey // we don't have signer's public key
    };

    enum Error
    {
        ErrorProcess,          // startup, process, or ipc error
        ErrorPassphrase,       // passphrase was either wrong or not provided
        ErrorFormat,           // input format was bad
        ErrorSignerExpired,    // signing key is expired
        ErrorEncryptExpired,   // encrypting key is expired
        ErrorEncryptUntrusted, // encrypting key is untrusted
        ErrorEncryptInvalid,   // encrypting key is invalid in some way
        ErrorDecryptNoKey,     // missing decrypt key
        ErrorUnknown,          // other error
        ErrorSignerRevoked,    // signing key is revoked
        ErrorSignatureExpired, // signature is expired
        ErrorEncryptRevoked    // encrypting key is revoked
    };

    class Event
    {
    public:
        enum Type
        {
            None,
            ReadyRead,
            BytesWritten,
            Finished,
            NeedPassphrase,
            NeedCard,
            ReadyReadDiagnosticText
        };

        Type    type;
        int     written; // BytesWritten
        QString keyId;   // NeedPassphrase

        Event()
            : type(None)
            , written(0)
        {
        }
    };

    class KeyItem
    {
    public:
        enum Type
        {
            RSA,
            DSA,
            ElGamal,
            Unknown
        };

        enum Caps
        {
            Encrypt = 0x01,
            Sign    = 0x02,
            Certify = 0x04,
            Auth    = 0x08
        };

        QString   id;
        Type      type;
        int       bits;
        QDateTime creationDate;
        QDateTime expirationDate;
        int       caps; // flags OR'd together
        QString   fingerprint;

        KeyItem()
            : type(Unknown)
            , bits(0)
            , caps(0)
        {
        }
    };

    class Key
    {
    public:
        QList<KeyItem> keyItems; // first item is primary
        QStringList    userIds;
        bool           isTrusted;

        Key()
            : isTrusted(false)
        {
        }
    };
    typedef QList<Key> KeyList;

    explicit GpgOp(const QString &bin, QObject *parent = nullptr);
    ~GpgOp() override;

    void reset();

    bool isActive() const;
    Type op() const;

    void setAsciiFormat(bool b);
    void setDisableAgent(bool b);
    void setAlwaysTrust(bool b);
    void setKeyrings(const QString &pubfile, const QString &secfile); // for keylists and import

    void doCheck();
    void doSecretKeyringFile();
    void doPublicKeyringFile();
    void doSecretKeys();
    void doPublicKeys();
    void doEncrypt(const QStringList &recip_ids);
    void doDecrypt();
    void doSign(const QString &signer_id);
    void doSignAndEncrypt(const QString &signer_id, const QStringList &recip_ids);
    void doSignClearsign(const QString &signer_id);
    void doSignDetached(const QString &signer_id);
    void doVerify();
    void doVerifyDetached(const QByteArray &sig);
    void doImport(const QByteArray &in);
    void doExport(const QString &key_id);
    void doDeleteKey(const QString &key_fingerprint);

#ifdef QPIPE_SECURE
    void submitPassphrase(const QCA::SecureArray &a);
#else
    void submitPassphrase(const QByteArray &a);
#endif
    void cardOkay();

    // for encrypt, decrypt, sign, verify, export
    QByteArray read();
    void       write(const QByteArray &in);
    void       endWrite();

    QString readDiagnosticText();

    // for synchronous operation
    Event waitForEvent(int msecs = -1);

    // results
    bool         success() const;
    Error        errorCode() const;
    KeyList      keys() const;          // Keys
    QString      keyringFile() const;   // KeyringFile
    QString      homeDir() const;       // GnuPG home directory
    QString      encryptedToId() const; // Decrypt (for ErrorDecryptNoKey)
    bool         wasSigned() const;     // Decrypt
    QString      signerId() const;      // Verify
    QDateTime    timestamp() const;     // Verify
    VerifyResult verifyResult() const;  // Verify

Q_SIGNALS:
    void readyRead();
    void bytesWritten(int bytes);
    void finished();
    void needPassphrase(const QString &keyId);
    void needCard();
    void readyReadDiagnosticText();

private:
    class Private;
    friend class Private;
    Private *d;
};

}

#endif
