File indexing completed on 2024-04-14 05:43:31

0001 /*
0002     SPDX-FileCopyrightText: 2008-2022 Rolf Eike Beer <kde@opensource.sf-tec.de>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #ifndef KGPGTRANSACTION_H
0007 #define KGPGTRANSACTION_H
0008 
0009 #include <QObject>
0010 #include <QString>
0011 #include <QStringList>
0012 
0013 class GPGProc;
0014 class KGpgSignTransactionHelper;
0015 class KGpgTransactionPrivate;
0016 class QByteArray;
0017 class QUrl;
0018 
0019 /**
0020  * @brief Process one GnuPG operation
0021  *
0022  * This class encapsulates one GnuPG operation. It will care for all
0023  * interaction with the gpg process. Everything you have to care about
0024  * is to set up the object properly, call start() and catch the done signal.
0025  *
0026  * This is an abstract base class for specific operations that implements
0027  * the basic I/O loop, the process setup and interaction and some convenience
0028  * members to set extra arguments for the process.
0029  *
0030  * If you want to add a new operation create a child class that implements
0031  * nextLine(). Usually you also need a constructor that takes some information
0032  * like the id of the key to modify.
0033  *
0034  * @author Rolf Eike Beer
0035  */
0036 class KGpgTransaction: public QObject {
0037     Q_OBJECT
0038 
0039     friend class KGpgTransactionPrivate;
0040     friend class KGpgSignTransactionHelper;
0041 
0042     Q_DISABLE_COPY(KGpgTransaction)
0043 
0044 public:
0045     /**
0046      * @brief return codes common to many transactions
0047      *
0048      * Every transaction may define additional return codes, which
0049      * should start at TS_COMMON_END + 1.
0050      */
0051     enum ts_transaction {
0052         TS_OK = 0,          ///< everything went fine
0053         TS_BAD_PASSPHRASE = 1,      ///< the passphrase was not correct
0054         TS_MSG_SEQUENCE = 2,        ///< unexpected sequence of GnuPG messages
0055         TS_USER_ABORTED = 3,        ///< the user aborted the transaction
0056         TS_INVALID_EMAIL = 4,       ///< the given email address is invalid
0057         TS_INPUT_PROCESS_ERROR = 5, ///< the connected input process returned an error
0058         TS_COMMON_END = 100     ///< placeholder for return values of derived classes
0059     };
0060     /**
0061      * @brief result codes for GnuPG boolean questions
0062      *
0063      * These are the possible answers to a boolean question of a GnuPG process.
0064      */
0065     enum ts_boolanswer {
0066         BA_UNKNOWN = 0,         ///< the question is not supported (this is an error)
0067         BA_YES = 1,         ///< answer "YES"
0068         BA_NO = 2           ///< answer "NO"
0069     };
0070     /**
0071      * @brief the known hints sent by GnuPG
0072      */
0073     enum ts_hintType {
0074         HT_KEYEXPIRED,      ///< key is expired
0075         HT_SIGEXPIRED,      ///< deprecated by GnuPG
0076         HT_NOSECKEY,        ///< secret key not available
0077         HT_ENCTO,       ///< message is encrypted for this key
0078         HT_PINENTRY_LAUNCHED    ///< pinentry was launched
0079     };
0080 
0081     /**
0082      * @brief KGpgTransaction constructor
0083      */
0084     explicit KGpgTransaction(QObject *parent = nullptr, const bool allowChaining = false);
0085     /**
0086      * @brief KGpgTransaction destructor
0087      */
0088     ~KGpgTransaction() override;
0089 
0090     /**
0091      * @brief Start the operation.
0092      */
0093     void start();
0094 
0095     /**
0096      * @brief sets the home directory of GnuPG called for this transaction
0097      */
0098     void setGnuPGHome(const QString &home);
0099 
0100     /**
0101      * @brief blocks until the transaction is complete
0102      * @return the result of the transaction like done() would
0103      * @retval TS_USER_ABORTED the timeout expired
0104      *
0105      * If this transaction has another transaction set as input then
0106      * it would wait for those transaction to finish first. The msecs
0107      * argument is used as limit for both transactions then so you
0108      * can end up waiting twice the given time (or longer if you have
0109      * more transactions chained).
0110      */
0111     int waitForFinished(const int msecs = -1);
0112 
0113     /**
0114      * @brief return description of this transaction
0115      * @return string used to describe what's going on
0116      *
0117      * This is especially useful when using this transaction from a KJob.
0118      */
0119     const QString &getDescription() const;
0120 
0121     /**
0122      * @brief connect the standard input of this transaction to another process
0123      *
0124      * Once the input process is connected this transaction will not emit
0125      * the done signal until the input process sends the done signal.
0126      *
0127      * The basic idea is that when an input transaction is set you only need
0128      * to care about this transaction. The other transaction is automatically
0129      * started when this one is started and is destroyed when this one is.
0130      */
0131     void setInputTransaction(KGpgTransaction *ta);
0132 
0133     /**
0134      * @brief tell the process the standard input is no longer connected
0135      *
0136      * If you had connected an input process you need to tell the transaction
0137      * once this input process is gone. Otherwise you will not get a done
0138      * signal from this transaction as it will wait for the finished signal
0139      * from the process that will never come.
0140      */
0141     void clearInputTransaction();
0142 
0143     /**
0144      * @brief check if another transaction will sent input to this
0145      */
0146     bool hasInputTransaction() const;
0147 
0148     /**
0149      * @brief abort this operation as soon as possible
0150      */
0151     void kill();
0152 
0153     /**
0154      * @brief add a command line argument to gpg process
0155      * @param arg new argument
0156      * @returns the position of the new argument
0157      *
0158      * This is a convenience function that allows adding one additional
0159      * argument to the command line of the process. This must be called
0160      * before start() is called. Usually you will call this from your
0161      * constructor.
0162      */
0163     int addArgument(const QString &arg);
0164     /**
0165      * @brief insert an argument at the given position
0166      * @param pos position to insert at
0167      * @param arg new argument
0168      */
0169     void insertArgument(const int pos, const QString &arg);
0170     /**
0171      * @brief insert arguments at the given position
0172      * @param pos position to insert at
0173      * @param args new arguments
0174      */
0175     void insertArguments(const int pos, const QStringList &args);
0176 
0177 Q_SIGNALS:
0178     /**
0179      * @brief Emitted when the operation was completed.
0180      * @param result return status of the transaction
0181      *
0182      * @see ts_transaction for the common status codes. Each transaction
0183      * may define additional status codes.
0184      */
0185     void done(int result);
0186 
0187     /**
0188      * @brief emits textual status information
0189      * @param msg the status message
0190      */
0191     void statusMessage(const QString &msg);
0192 
0193     /**
0194      * @brief emits procentual status information
0195      * @param processedAmount how much of the job is done
0196      * @param totalAmount how much needs to be done to complete this job
0197      */
0198     void infoProgress(qulonglong processedAmount, qulonglong totalAmount);
0199 
0200 protected:
0201     /**
0202      * @brief Called before the gpg process is started.
0203      * @return true if the process should be started
0204      *
0205      * You may reimplement this member if you need to do some special
0206      * operations before the process is started. The command line of the
0207      * process may be modified for the last time here.
0208      *
0209      * When you notice that some values passed are invalid or the
0210      * transaction does not need to be run for some other reason you should
0211      * call setSuccess() to set the return value and return false. In this
0212      * case the process is not started but the value is immediately
0213      * returned.
0214      */
0215     virtual bool preStart();
0216     /**
0217      * @brief Called when the gpg process is up and running.
0218      *
0219      * This functions is connected to the started() signal of the gpg process.
0220      */
0221     virtual void postStart();
0222     /**
0223      * @brief Called for every line the gpg process writes.
0224      * @param line the input from the process
0225      * @return true if "quit" should be sent to process
0226      *
0227      * You need to implement this member to get a usable subclass.
0228      *
0229      * When this function returns true "quit" is written to the process.
0230      */
0231     virtual bool nextLine(const QString &line) = 0;
0232     /**
0233      * @brief Called for every boolean question GnuPG answers
0234      * @param line the question GnuPG asked
0235      * @return what to answer GnuPG
0236      *
0237      * This is called instead of nextLine() if the line contains a boolean
0238      * question. Returning BA_UNKNOWN will cancel the current transaction
0239      * and will set the transaction result to TS_MSG_SEQUENCE.
0240      *
0241      * The default implementation will answer BA_UNKNOWN to every question.
0242      */
0243     virtual ts_boolanswer boolQuestion(const QString &line);
0244 
0245     /**
0246      * @brief called when GnuPG asks for confirmation for overwriting a file
0247      * @param currentFile fill in the current filename for the user dialog
0248      * @return what to answer to GnuPG
0249      * @retval BA_YES file will be overwritten, @p currentFile is ignored
0250      * @retval BA_NO file will not be overwritten, if @p currentFile is given this will automatically be provided as alternative to GnuPG
0251      * @retval BA_UNKNOWN ask the user for a choice or abort, @p currentFile is provided to the user as a hint about the original filename, if @p currentFile is empty the transaction is aborted
0252      *
0253      * The default implementation will just return BA_UNKNOWN without setting
0254      * a filename, causing a sequence error.
0255      */
0256     virtual ts_boolanswer confirmOverwrite(QUrl &currentFile);
0257 
0258     /**
0259      * @brief Called for a set of hint messages
0260      *
0261      * @param hint the hint type given by GnuPG
0262      * @param args the arguments given to the hint
0263      * @return if the hint was parsed correctly
0264      * @retval true everything is fine
0265      * @retval false something went wrong (e.g. syntax error)
0266      *
0267      * The default implementation will do nothing but checking for some
0268      * argument counts. Override this and handle all interesting hints
0269      * yourself. Don't forget to call the default implementation at the end.
0270      */
0271     virtual bool hintLine(const ts_hintType hint, const QString &args);
0272     /**
0273      * @brief Called when the gpg process finishes.
0274      *
0275      * You may reimplement this member if you need to do some special
0276      * operations after process completion. The provided one simply
0277      * does nothing which should be enough for most cases.
0278      */
0279     virtual void finish();
0280     /**
0281      * @brief called when the user entered a new passphrase
0282      *
0283      * This is called after askNewPassphrase() was called, the user has
0284      * entered a new passphrase and it was sent to the GnuPG process.
0285      *
0286      * The default implementation does nothing.
0287      */
0288     virtual void newPassphraseEntered();
0289     /**
0290      * @brief set the description returned in getDescription()
0291      * @param description the new description of this transaction
0292      */
0293     void setDescription(const QString &description);
0294 
0295     /**
0296      * @brief wait until the input transaction has finished
0297      */
0298     void waitForInputTransaction();
0299 
0300     /**
0301      * @brief notify of an unexpected line
0302      *
0303      * This will print out the line to the console to ease debugging.
0304      */
0305     void unexpectedLine(const QString &line);
0306 
0307     /**
0308      * @brief called when GnuPG asks for a passphrase
0309      * @return if the processing should continue
0310      * @retval true processing should continue
0311      * @retval false an error occurred, transaction should be aborted
0312      *
0313      * This allows a transaction to implement special handling for
0314      * passphrases, e.g. when both old and new passphrase must be
0315      * requested when changing it. The default implementation will just
0316      * call askPassphrase().
0317      */
0318     virtual bool passphraseRequested();
0319 
0320     /**
0321      * @brief called when GnuPG accepted the passphrase
0322      * @return if the input channel to GnuPG should be closed
0323      * @retval true close the input channel of the GnuPG process
0324      * @retval false keep the GnuPG input channel open
0325      *
0326      * This allows a transaction to handle passphrase success in a
0327      * special way. The default implementation will just return true.
0328      */
0329     virtual bool passphraseReceived();
0330 
0331 private:
0332     KGpgTransactionPrivate* const d;
0333 
0334 protected:
0335     /**
0336      * @brief Ask user for passphrase and send it to gpg process.
0337      *
0338      * If the gpg process asks for a new passphrase this function will do
0339      * all necessary steps for you: ask the user for the passphrase and write
0340      * it to the gpg process. If the passphrase is wrong the user is prompted
0341      * again for the correct passphrase. If the user aborts the passphrase
0342      * entry the gpg process will be killed and the transaction result will
0343      * be set to TS_USER_ABORTED.
0344      *
0345      * This is virtual so the tests can provide an alternate implementation.
0346      *
0347      * @see askPassphrase
0348      */
0349     virtual void askNewPassphrase(const QString &text);
0350 
0351     /**
0352      * @brief get the success value that will be returned with the done signal
0353      */
0354     int getSuccess() const;
0355     /**
0356      * @brief set the success value that will be returned with the done signal
0357      * @param v the new success value
0358      *
0359      * You should use 0 as success value. Other values can be defined as needed.
0360      */
0361     void setSuccess(const int v);
0362 
0363     /**
0364      * @brief add a userid hint
0365      * @param txt userid description
0366      *
0367      * Before GnuPG asks for a passphrase it usually sends out a hint message
0368      * for which key the passphrase will be needed. There may be several hint
0369      * messages, e.g. if a text was encrypted with multiple keys.
0370      */
0371     void addIdHint(QString txt);
0372     /**
0373      * @brief get string of all userid hints
0374      * @returns concatenation of all ids previously added with addIdHint().
0375      */
0376     QString getIdHints() const;
0377 
0378     /**
0379      * @brief get a reference to the gpg process object
0380      * @returns gpg process object
0381      *
0382      * This returns a reference to the gpg process object used internally.
0383      * In case you need to do some special things (e.g. changing the output
0384      * mode) you can modify this object.
0385      *
0386      * Usually you will not need this.
0387      *
0388      * @warning Never free this object!
0389      */
0390     GPGProc *getProcess();
0391     /**
0392      * @brief add command line arguments to gpg process
0393      * @param args new arguments
0394      *
0395      * This is a convenience function that allows adding additional
0396      * arguments to the command line of the process. This must be called
0397      * before start() is called.
0398      */
0399     void addArguments(const QStringList &args);
0400 
0401     /**
0402      * @brief replace the argument at the given position
0403      * @param pos position of old argument
0404      * @param arg new argument
0405      */
0406     void replaceArgument(const int pos, const QString &arg);
0407     /**
0408      * @brief make sure the reference to a specific argument is kept up to date
0409      * @param ref the value where the position is stored
0410      *
0411      * You might want to keep the position of a specific argument to
0412      * later be able to repace it easily. In that case put it into
0413      * this function too so every time someone mofifies the argument
0414      * list (especially by insertArgument() and insertArguments())
0415      * this reference will be kept up to date.
0416      */
0417     void addArgumentRef(int *ref);
0418     /**
0419      * @brief write data to standard input of gpg process
0420      * @param a data to write
0421      * @param lf if line feed should be appended to message
0422      *
0423      * Use this function to interact with the gpg process. A carriage
0424      * return is appended to the data automatically. Usually you will
0425      * call this function from nextLine().
0426      */
0427     void write(const QByteArray &a, const bool lf = true);
0428     /**
0429      * @brief write data to standard input of gpg process
0430      * @param i data to write
0431      *
0432      * @overload
0433      */
0434     void write(const int i);
0435     /**
0436      * @brief ask user for passphrase
0437      * @param message message to display to the user. If message is empty
0438      * "Enter passphrase for [UID]" will be used.
0439      * @return true if the authorization was successful
0440      *
0441      * This function handles user authorization for key operations. It will
0442      * take care to display the message asking the user for the passphrase
0443      * and the number of tries left.
0444      */
0445     bool askPassphrase(const QString &message = QString());
0446 
0447     /**
0448      * @brief set the fingerprints that are expected for this transaction
0449      *
0450      * This will skip any KEY_CONSIDERED messages from GnuPG that contain
0451      * any of the given fingerprints.
0452      */
0453     void setExpectedFingerprints(const QStringList &fingerprints);
0454 };
0455 
0456 #endif // KGPGTRANSACTION_H