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("<")); 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 }