File indexing completed on 2024-11-03 05:18:23

0001 /*
0002     SPDX-FileCopyrightText: 2002 Jean-Baptiste Mardelle <bj@altern.org>
0003     SPDX-FileCopyrightText: 2007-2022 Rolf Eike Beer <kde@opensource.sf-tec.de>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kgpgtextedit.h"
0008 
0009 #include "selectsecretkey.h"
0010 #include "kgpgsettings.h"
0011 #include "keyservers.h"
0012 #include "selectpublickeydialog.h"
0013 #include "detailedconsole.h"
0014 #include "keysmanager.h"
0015 #include "editor/kgpgeditor.h"
0016 #include "transactions/kgpgdecrypt.h"
0017 #include "transactions/kgpgencrypt.h"
0018 #include "transactions/kgpgimport.h"
0019 #include "transactions/kgpgsigntext.h"
0020 #include "transactions/kgpgverify.h"
0021 
0022 #include <KIO/Job>
0023 #include <KIO/FileCopyJob>
0024 #include <KJobWidgets>
0025 #include <KLocalizedString>
0026 #include <KMessageBox>
0027 #include <KUrlMimeData>
0028 
0029 #include <QDragEnterEvent>
0030 #include <QDropEvent>
0031 #include <QFile>
0032 #include <QMimeData>
0033 #include <QTemporaryFile>
0034 #include <QTextStream>
0035 
0036 #define SIGNEDMESSAGE_BEGIN  QLatin1String( "-----BEGIN PGP SIGNED MESSAGE-----" )
0037 #define SIGNEDMESSAGE_END    QLatin1String( "-----END PGP SIGNATURE-----" )
0038 
0039 KgpgTextEdit::KgpgTextEdit(QWidget *parent, KGpgItemModel *model, KeysManager *manager)
0040             : KTextEdit(parent),
0041             m_posstart(-1),
0042             m_posend(-1),
0043             m_model(model),
0044             m_keysmanager(manager)
0045 {
0046     setCheckSpellingEnabled(true);
0047     setAcceptDrops(true);
0048     setReadOnly(false);
0049     setUndoRedoEnabled(true);
0050     setAcceptRichText(false);
0051 }
0052 
0053 void KgpgTextEdit::dragEnterEvent(QDragEnterEvent *e)
0054 {
0055     // if a file is dragged into editor ...
0056     if (e->mimeData()->hasUrls() || e->mimeData()->hasText())
0057         e->acceptProposedAction();
0058 }
0059 
0060 void KgpgTextEdit::dropEvent(QDropEvent *e)
0061 {
0062     // decode dropped file or dropped text
0063     QList<QUrl> uriList = KUrlMimeData::urlsFromMimeData(e->mimeData());
0064     if (!uriList.isEmpty())
0065         slotDroppedFile(uriList.first());
0066     else
0067     if (e->mimeData()->hasText())
0068         insertPlainText(e->mimeData()->text());
0069 }
0070 
0071 void KgpgTextEdit::slotDroppedFile(const QUrl &url)
0072 {
0073     openDroppedFile(url, true);
0074 }
0075 
0076 void KgpgTextEdit::openDroppedFile(const QUrl &url, const bool probe)
0077 {
0078     QUrl tmpurl;
0079 
0080     QString checkFile;
0081     if (url.isLocalFile()) {
0082         checkFile = url.path();
0083         tmpurl = url;
0084     } else {
0085         if (KMessageBox::warningContinueCancel(this, i18n("<qt><b>Remote file dropped</b>.<br />The remote file will now be copied to a temporary file to process requested operation. This temporary file will be deleted after operation.</qt>"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QLatin1String( "RemoteFileWarning" )) != KMessageBox::Continue)
0086             return;
0087 
0088         QTemporaryFile tmpFile;
0089         tmpFile.open();
0090         auto copyJob = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()));
0091         KJobWidgets::setWindow(copyJob , this);
0092         copyJob->exec();
0093         if (copyJob->error()) {
0094             KMessageBox::error(this, i18n("Could not download file."));
0095             return;
0096         }
0097         tmpFile.setAutoRemove(false);
0098         checkFile = m_tempfile = tmpFile.fileName();
0099         tmpurl = QUrl::fromLocalFile(m_tempfile);
0100     }
0101 
0102     QString result;
0103 
0104     QFile qfile(checkFile);
0105     if (qfile.open(QIODevice::ReadOnly)) {
0106         QTextStream t(&qfile);
0107         result = t.readAll();
0108         qfile.close();
0109     }
0110 
0111     if (result.isEmpty())
0112         return;
0113 
0114     if (!probe || KGpgDecrypt::isEncryptedText(result, &m_posstart, &m_posend)) {
0115         // if pgp data found, decode it
0116         KGpgDecrypt *decr = new KGpgDecrypt(this, QList<QUrl>({tmpurl}));
0117         connect(decr, &KGpgDecrypt::done, this, &KgpgTextEdit::slotDecryptDone);
0118         decr->start();
0119         return;
0120     }
0121     // remove only here, as KGpgDecrypt will use and remove the file itself
0122     if(!m_tempfile.isEmpty())
0123         QFile::remove( m_tempfile );
0124     m_tempfile.clear();
0125 
0126     QString tmpinfo;
0127 
0128     switch (KGpgImport::isKey(result)) {
0129     case 1:
0130         tmpinfo = i18n("<qt>This file is a <b>public</b> key.<br />Do you want to import it instead of opening it in editor?</qt>");
0131         break;
0132     case 2:
0133         tmpinfo = i18n("<qt>This file is a <b>private</b> key.<br />Do you want to import it instead of opening it in editor?</qt>");
0134         break;
0135         }
0136 
0137     if (!tmpinfo.isEmpty()) {
0138         if (KMessageBox::questionTwoActions(this, tmpinfo, i18n("Key file dropped on Editor"), KGuiItem{i18nc("@action:button", "Yes")}, KGuiItem{i18nc("@action:button", "No")}) != KMessageBox::PrimaryAction) {
0139             setPlainText(result);
0140         } else {
0141             KGpgImport *imp = new KGpgImport(this, result);
0142             connect(imp, &KGpgImport::done, m_keysmanager, &KeysManager::slotImportDone);
0143             imp->start();
0144         }
0145     } else {
0146         if (m_posstart != -1) {
0147             Q_ASSERT(m_posend != -1);
0148             QString fullcontent = toPlainText();
0149             fullcontent.replace(m_posstart, m_posend - m_posstart, result);
0150             setPlainText(fullcontent);
0151             m_posstart = -1;
0152             m_posend = -1;
0153         } else {
0154             setPlainText(result);
0155         }
0156     }
0157 }
0158 
0159 void KgpgTextEdit::slotEncode()
0160 {
0161     QPointer<KgpgSelectPublicKeyDlg> dialog = new KgpgSelectPublicKeyDlg(this, m_model, m_keysmanager->goDefaultShortcut(), true);
0162     if (dialog->exec() == QDialog::Accepted) {
0163         QStringList options;
0164         KGpgEncrypt::EncryptOptions opts = KGpgEncrypt::DefaultEncryption;
0165 
0166         if (dialog->getArmor())
0167             opts |= KGpgEncrypt::AsciiArmored;
0168 
0169         if (dialog->getUntrusted())
0170             opts |= KGpgEncrypt::AllowUntrustedEncryption;
0171 
0172         if (dialog->getHideId())
0173             opts |= KGpgEncrypt::HideKeyId;
0174 
0175         if (KGpgSettings::allowCustomEncryptionOptions()) {
0176             const QString customoptions = dialog->getCustomOptions();
0177             if (!customoptions.isEmpty())
0178                 options << customoptions.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0179         }
0180 
0181         if (KGpgSettings::pgpCompatibility())
0182             options << QLatin1String( "--pgp6" );
0183 
0184         QStringList listkeys;
0185         if (!dialog->getSymmetric())
0186             listkeys = dialog->selectedKeys();
0187 
0188         KGpgEncrypt *encr = new KGpgEncrypt(this, listkeys, toPlainText(), opts, options);
0189         encr->start();
0190         connect(encr, &KGpgEncrypt::done, this, &KgpgTextEdit::slotEncodeUpdate);
0191     }
0192     delete dialog;
0193 }
0194 
0195 void KgpgTextEdit::slotDecode()
0196 {
0197     const QString fullcontent = toPlainText();
0198 
0199     if (!KGpgDecrypt::isEncryptedText(fullcontent, &m_posstart, &m_posend))
0200         return;
0201 
0202     KGpgDecrypt *decr = new KGpgDecrypt(this, fullcontent.mid(m_posstart, m_posend - m_posstart));
0203     connect(decr, &KGpgDecrypt::done, this, &KgpgTextEdit::slotDecryptDone);
0204     decr->start();
0205 }
0206 
0207 void KgpgTextEdit::slotSign(const QString &message)
0208 {
0209     QString signkeyid;
0210 
0211     QPointer<KgpgSelectSecretKey> opts = new KgpgSelectSecretKey(this, m_model);
0212     if (opts->exec() == QDialog::Accepted)
0213         signkeyid = opts->getKeyID();
0214     else
0215     {
0216         delete opts;
0217         return;
0218     }
0219 
0220     delete opts;
0221 
0222     KGpgSignText *signt = new KGpgSignText(this, signkeyid, message);
0223     connect(signt, &KGpgSignText::done, this, &KgpgTextEdit::slotSignUpdate);
0224     signt->start();
0225 }
0226 
0227 void KgpgTextEdit::slotVerify(const QString &message)
0228 {
0229     const QString startmsg(SIGNEDMESSAGE_BEGIN);
0230     const QString endmsg(SIGNEDMESSAGE_END);
0231 
0232     int posstart = message.indexOf(startmsg);
0233     if (posstart == -1)
0234         return;
0235 
0236     int posend = message.indexOf(endmsg, posstart);
0237     if (posend == -1)
0238         return;
0239     posend += endmsg.length();
0240 
0241     KGpgVerify *verify = new KGpgVerify(this, message.mid(posstart, posend - posstart));
0242     connect(verify, &KGpgVerify::done, this, &KgpgTextEdit::slotVerifyDone);
0243     verify->start();
0244 }
0245 
0246 void KgpgTextEdit::slotDecryptDone(int result)
0247 {
0248     KGpgDecrypt *decr = qobject_cast<KGpgDecrypt *>(sender());
0249     Q_ASSERT(decr != nullptr);
0250 
0251     if (!m_tempfile.isEmpty()) {
0252         QFile::remove(m_tempfile);
0253         m_tempfile.clear();
0254     }
0255 
0256     const QChar lf = QLatin1Char('\n');
0257     if (result == KGpgTransaction::TS_OK) {
0258         // FIXME choose codec
0259         setPlainText(decr->decryptedText().join(lf) + lf);
0260     } else if (result != KGpgTransaction::TS_USER_ABORTED) {
0261         KMessageBox::detailedError(this, i18n("Decryption failed."), decr->getMessages().join(lf));
0262     }
0263 
0264     decr->deleteLater();
0265 }
0266 
0267 void KgpgTextEdit::slotEncodeUpdate(int result)
0268 {
0269     KGpgEncrypt *enc = qobject_cast<KGpgEncrypt *>(sender());
0270     Q_ASSERT(enc != nullptr);
0271 
0272     if (result == KGpgTransaction::TS_OK) {
0273         const QChar lf = QLatin1Char('\n');
0274         setPlainText(enc->encryptedText().join(lf) + lf);
0275     } else {
0276         KMessageBox::error(this, i18n("The encryption failed with error code %1", result),
0277                 i18n("Encryption failed."));
0278     }
0279 
0280     sender()->deleteLater();
0281 }
0282 
0283 void KgpgTextEdit::slotSignUpdate(int result)
0284 {
0285     const KGpgSignText * const signt = qobject_cast<KGpgSignText *>(sender());
0286     sender()->deleteLater();
0287     Q_ASSERT(signt != nullptr);
0288 
0289     if (result != KGpgTransaction::TS_OK) {
0290         KMessageBox::error(this, i18n("Signing not possible: bad passphrase or missing key"));
0291         return;
0292     }
0293 
0294     const QChar lf = QLatin1Char('\n');
0295     const QString content = signt->signedText().join(lf) + lf;
0296 
0297     setPlainText(content);
0298     Q_EMIT resetEncoding(false);
0299 }
0300 
0301 void KgpgTextEdit::slotVerifyDone(int result)
0302 {
0303     const KGpgVerify * const verify = qobject_cast<KGpgVerify *>(sender());
0304     sender()->deleteLater();
0305     Q_ASSERT(verify != nullptr);
0306 
0307     Q_EMIT verifyFinished();
0308 
0309     if (result == KGpgVerify::TS_MISSING_KEY) {
0310         verifyKeyNeeded(verify->missingId());
0311         return;
0312     }
0313 
0314     const QStringList messages = verify->getMessages();
0315 
0316     if (messages.isEmpty())
0317         return;
0318 
0319     QStringList msglist;
0320     for (QString rawmsg : messages)
0321         msglist << rawmsg.replace(QLatin1Char('<'), QLatin1String("&lt;"));
0322 
0323     (void) new KgpgDetailedInfo(this, KGpgVerify::getReport(messages, m_model),
0324             msglist.join(QLatin1String("<br/>")),
0325             QStringList(), i18nc("Caption of message box", "Verification Finished"));
0326 }
0327 
0328 void KgpgTextEdit::verifyKeyNeeded(const QString &id)
0329 {
0330     KGuiItem importitem {
0331         i18n("&Import"),
0332         QIcon::fromTheme(QStringLiteral("checkbox")),
0333         i18n("Import key in your list")
0334     };
0335 
0336     KGuiItem noimportitem {
0337         i18n("Do &Not Import"),
0338         QIcon{},
0339         i18n("Will not import this key in your list")
0340     };
0341 
0342     if (KMessageBox::questionTwoActions(this, i18n("<qt><b>Missing signature:</b><br />Key id: %1<br /><br />Do you want to import this key from a keyserver?</qt>", id), i18n("Missing Key"), importitem, noimportitem) == KMessageBox::PrimaryAction)
0343     {
0344         KeyServer *kser = new KeyServer(nullptr, m_model, true);
0345         kser->slotSetText(id);
0346         kser->slotImport();
0347     }
0348 }
0349 
0350 void KgpgTextEdit::slotSignVerify()
0351 {
0352     signVerifyText(toPlainText());
0353 }
0354 
0355 void KgpgTextEdit::signVerifyText(const QString &message)
0356 {
0357     if (message.contains(SIGNEDMESSAGE_BEGIN))
0358         slotVerify(message);
0359     else
0360         slotSign(message);
0361 }
0362 
0363 void KgpgTextEdit::slotHighlightText(const QString &, const int matchingindex, const int matchedlength)
0364 {
0365     highlightWord(matchedlength, matchingindex);
0366 }