File indexing completed on 2023-12-03 09:19:03
0001 /* 0002 SPDX-FileCopyrightText: 2002 Jean-Baptiste Mardelle <bj@altern.org> 0003 SPDX-FileCopyrightText: 2008, 2009, 2010, 2011, 2012, 2013 Rolf Eike Beer <kde@opensource.sf-tec.de> 0004 SPDX-FileCopyrightText: 2016 Andrius Ć tikoans <andrius@stikonas.eu> 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kgpgexternalactions.h" 0009 0010 #include "detailedconsole.h" 0011 #include "foldercompressjob.h" 0012 #include "keyservers.h" 0013 #include "keysmanager.h" 0014 #include "kgpgfirstassistant.h" 0015 #include "kgpginterface.h" 0016 #include "kgpgsettings.h" 0017 #include "kgpgtextinterface.h" 0018 #include "selectpublickeydialog.h" 0019 #include "selectsecretkey.h" 0020 #include "core/images.h" 0021 #include "editor/kgpgeditor.h" 0022 #include "editor/kgpgtextedit.h" 0023 #include "transactions/kgpgdecrypt.h" 0024 #include "transactions/kgpgencrypt.h" 0025 #include "transactions/kgpgsigntext.h" 0026 #include "transactions/kgpgtransactionjob.h" 0027 #include "transactions/kgpgverify.h" 0028 0029 #include <KActionCollection> 0030 #include <KHelpClient> 0031 #include <KMessageBox> 0032 0033 #include <KJobTrackerInterface> 0034 #include <QComboBox> 0035 #include <QFont> 0036 #include <QHBoxLayout> 0037 #include <QProcess> 0038 #include <QStringListModel> 0039 #include <QTemporaryFile> 0040 #include <kio/global.h> 0041 #include <kio/renamedialog.h> 0042 0043 KGpgExternalActions::KGpgExternalActions(KeysManager *parent, KGpgItemModel *model) 0044 : QObject(parent), 0045 compressionScheme(0), 0046 m_model(model), 0047 m_kgpgfoldertmp(nullptr), 0048 m_keysmanager(parent) 0049 { 0050 readOptions(); 0051 } 0052 0053 KGpgExternalActions::~KGpgExternalActions() 0054 { 0055 delete m_kgpgfoldertmp; 0056 } 0057 0058 void KGpgExternalActions::encryptFiles(KeysManager *parent, const QList<QUrl> &urls) 0059 { 0060 Q_ASSERT(!urls.isEmpty()); 0061 0062 KGpgExternalActions *encActions = new KGpgExternalActions(parent, parent->getModel()); 0063 0064 KgpgSelectPublicKeyDlg *dialog = new KgpgSelectPublicKeyDlg(parent, parent->getModel(), encActions->goDefaultKey(), false, urls); 0065 connect(dialog, &KgpgSelectPublicKeyDlg::accepted, encActions, &KGpgExternalActions::slotEncryptionKeySelected); 0066 connect(dialog, &KgpgSelectPublicKeyDlg::rejected, dialog, &KgpgSelectPublicKeyDlg::deleteLater); 0067 connect(dialog, &KgpgSelectPublicKeyDlg::rejected, encActions, &KGpgExternalActions::deleteLater); 0068 dialog->show(); 0069 } 0070 0071 void KGpgExternalActions::slotEncryptionKeySelected() 0072 { 0073 KgpgSelectPublicKeyDlg *dialog = qobject_cast<KgpgSelectPublicKeyDlg *>(sender()); 0074 Q_ASSERT(dialog != nullptr); 0075 sender()->deleteLater(); 0076 0077 QStringList opts; 0078 QString defaultKey; 0079 0080 if (KGpgSettings::encryptFilesTo()) { 0081 if (KGpgSettings::pgpCompatibility()) 0082 opts << QLatin1String( "--pgp6" ); 0083 0084 defaultKey = KGpgSettings::fileEncryptionKey(); 0085 } 0086 0087 KGpgEncrypt::EncryptOptions eopt = KGpgEncrypt::DefaultEncryption; 0088 0089 if (dialog->getUntrusted()) 0090 eopt |= KGpgEncrypt::AllowUntrustedEncryption; 0091 if (dialog->getArmor()) 0092 eopt |= KGpgEncrypt::AsciiArmored; 0093 if (dialog->getHideId()) 0094 eopt |= KGpgEncrypt::HideKeyId; 0095 0096 if (KGpgSettings::allowCustomEncryptionOptions()) { 0097 const QString customopts = dialog->getCustomOptions(); 0098 0099 if (!customopts.isEmpty()) 0100 opts << customopts.split(QLatin1Char(' '), Qt::SkipEmptyParts); 0101 } 0102 0103 QStringList keys = dialog->selectedKeys(); 0104 0105 if (!defaultKey.isEmpty() && !keys.contains(defaultKey)) 0106 keys.append(defaultKey); 0107 0108 if (dialog->getSymmetric()) 0109 keys.clear(); 0110 0111 KGpgEncrypt *enc = new KGpgEncrypt(dialog->parent(), keys, dialog->getFiles(), eopt, opts); 0112 KGpgTransactionJob *encjob = new KGpgTransactionJob(enc); 0113 0114 KIO::getJobTracker()->registerJob(encjob); 0115 encjob->start(); 0116 0117 deleteLater(); 0118 } 0119 0120 void KGpgExternalActions::encryptFolders(KeysManager *parent, const QList<QUrl> &urls) 0121 { 0122 QTemporaryFile *tmpfolder = new QTemporaryFile(); 0123 0124 if (!tmpfolder->open()) { 0125 delete tmpfolder; 0126 KMessageBox::error(parent, i18n("Cannot create temporary file for folder compression."), i18n("Temporary File Creation")); 0127 return; 0128 } 0129 0130 if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parent, 0131 i18n("<qt>KGpg will now create a temporary archive file:<br /><b>%1</b> to process the encryption. " 0132 "The file will be deleted after the encryption is finished.</qt>", 0133 tmpfolder->fileName()), i18n("Temporary File Creation"), KStandardGuiItem::cont(), 0134 KStandardGuiItem::cancel(), QLatin1String( "FolderTmpFile" ))) { 0135 delete tmpfolder; 0136 return; 0137 } 0138 0139 KGpgExternalActions *encActions = new KGpgExternalActions(parent, parent->getModel()); 0140 KgpgSelectPublicKeyDlg *dialog = new KgpgSelectPublicKeyDlg(parent, parent->getModel(), encActions->goDefaultKey(), false, urls); 0141 encActions->m_kgpgfoldertmp = tmpfolder; 0142 0143 QWidget *bGroup = new QWidget(dialog->optionsbox); 0144 QHBoxLayout *bGroupHBoxLayout = new QHBoxLayout(bGroup); 0145 bGroupHBoxLayout->setContentsMargins(0, 0, 0, 0); 0146 0147 (void) new QLabel(i18n("Compression method for archive:"), bGroup); 0148 0149 QComboBox *optionbx = new QComboBox(bGroup); 0150 bGroupHBoxLayout->addWidget(optionbx); 0151 optionbx->setModel(new QStringListModel(FolderCompressJob::archiveNames(), bGroup)); 0152 0153 connect(optionbx, QOverload<int>::of(&QComboBox::activated), encActions, &KGpgExternalActions::slotSetCompression); 0154 connect(dialog, &KgpgSelectPublicKeyDlg::accepted, encActions, &KGpgExternalActions::startFolderEncode); 0155 connect(dialog, &KgpgSelectPublicKeyDlg::rejected, encActions, &KGpgExternalActions::deleteLater); 0156 connect(dialog, &KgpgSelectPublicKeyDlg::rejected, dialog, &KgpgSelectPublicKeyDlg::deleteLater); 0157 0158 dialog->show(); 0159 } 0160 0161 void KGpgExternalActions::slotSetCompression(int cp) 0162 { 0163 compressionScheme = cp; 0164 } 0165 0166 void KGpgExternalActions::startFolderEncode() 0167 { 0168 KgpgSelectPublicKeyDlg *dialog = qobject_cast<KgpgSelectPublicKeyDlg *>(sender()); 0169 Q_ASSERT(dialog != nullptr); 0170 dialog->deleteLater(); 0171 0172 const QList<QUrl> urls = dialog->getFiles(); 0173 0174 QStringList selec = dialog->selectedKeys(); 0175 KGpgEncrypt::EncryptOptions encOptions = KGpgEncrypt::DefaultEncryption; 0176 const QStringList encryptOptions = dialog->getCustomOptions().split(QLatin1Char(' '), Qt::SkipEmptyParts); 0177 if (dialog->getSymmetric()) { 0178 selec.clear(); 0179 } else { 0180 Q_ASSERT(!selec.isEmpty()); 0181 } 0182 0183 QString extension = FolderCompressJob::extensionForArchive(compressionScheme); 0184 0185 if (dialog->getArmor()) 0186 extension += QLatin1String( ".asc" ); 0187 else if (KGpgSettings::pgpExtension()) 0188 extension += QLatin1String( ".pgp" ); 0189 else 0190 extension += QLatin1String( ".gpg" ); 0191 0192 if (dialog->getArmor()) 0193 encOptions |= KGpgEncrypt::AsciiArmored; 0194 if (dialog->getHideId()) 0195 encOptions |= KGpgEncrypt::HideKeyId; 0196 if (dialog->getUntrusted()) 0197 encOptions |= KGpgEncrypt::AllowUntrustedEncryption; 0198 0199 QUrl encryptedFile(QUrl::fromLocalFile(urls.first().adjusted(QUrl::StripTrailingSlash).path() + extension)); 0200 QFile encryptedFolder(encryptedFile.path()); 0201 dialog->hide(); 0202 if (encryptedFolder.exists()) { 0203 QPointer<KIO::RenameDialog> over = new KIO::RenameDialog(m_keysmanager, i18n("File Already Exists"), 0204 QUrl(), encryptedFile, KIO::RenameDialog_Overwrite); 0205 if (over->exec() == QDialog::Rejected) { 0206 dialog = nullptr; 0207 delete over; 0208 deleteLater(); 0209 return; 0210 } 0211 encryptedFile = over->newDestUrl(); 0212 delete over; 0213 } 0214 0215 FolderCompressJob *trayinfo = new FolderCompressJob(m_keysmanager, urls, encryptedFile, m_kgpgfoldertmp, 0216 selec, encryptOptions, encOptions, compressionScheme); 0217 connect(trayinfo, &FolderCompressJob::result, this, &KGpgExternalActions::slotFolderFinished); 0218 KIO::getJobTracker()->registerJob(trayinfo); 0219 trayinfo->start(); 0220 } 0221 0222 void KGpgExternalActions::slotFolderFinished(KJob *job) 0223 { 0224 FolderCompressJob *trayinfo = qobject_cast<FolderCompressJob *>(job); 0225 Q_ASSERT(trayinfo != nullptr); 0226 0227 if (trayinfo->error()) 0228 KMessageBox::error(m_keysmanager, trayinfo->errorString()); 0229 0230 deleteLater(); 0231 } 0232 0233 void KGpgExternalActions::verifyFile(QUrl url) 0234 { 0235 // check file signature 0236 if (url.isEmpty()) 0237 return; 0238 0239 QString sigfile; 0240 // try to find detached signature. 0241 if (!url.fileName().endsWith(QLatin1String(".sig"))) { 0242 sigfile = url.path() + QLatin1String( ".sig" ); 0243 if (!QFile::exists(sigfile)) { 0244 sigfile = url.path() + QLatin1String( ".asc" ); 0245 // if no .asc or .sig signature file included, assume the file is internally signed 0246 if (!QFile::exists(sigfile)) 0247 sigfile.clear(); 0248 } 0249 } else { 0250 sigfile = url.path(); 0251 sigfile.chop(4); 0252 } 0253 0254 KGpgVerify *kgpv = new KGpgVerify(parent(), QList<QUrl>({QUrl(sigfile)})); 0255 connect(kgpv, &KGpgVerify::done, this, &KGpgExternalActions::slotVerificationDone); 0256 kgpv->start(); 0257 } 0258 0259 void KGpgExternalActions::slotVerificationDone(int result) 0260 { 0261 KGpgVerify *kgpv = qobject_cast<KGpgVerify *>(sender()); 0262 Q_ASSERT(kgpv != nullptr); 0263 kgpv->deleteLater(); 0264 0265 if (result == KGpgVerify::TS_MISSING_KEY) { 0266 KeyServer *kser = new KeyServer(m_keysmanager, m_model); 0267 kser->slotSetText(kgpv->missingId()); 0268 kser->slotImport(); 0269 } else { 0270 const QStringList messages = kgpv->getMessages(); 0271 0272 if (messages.isEmpty()) 0273 return; 0274 0275 QStringList msglist; 0276 for (QString rawmsg : messages) 0277 msglist << rawmsg.replace(QLatin1Char('<'), QLatin1String("<")); 0278 0279 (void) new KgpgDetailedInfo(m_keysmanager, KGpgVerify::getReport(messages, m_model), 0280 msglist.join(QLatin1String("<br/>")), 0281 QStringList(), i18nc("Caption of message box", "Verification Finished")); 0282 } 0283 } 0284 0285 void KGpgExternalActions::signFiles(KeysManager* parent, const QList<QUrl>& urls) 0286 { 0287 Q_ASSERT(!urls.isEmpty()); 0288 0289 KGpgExternalActions *signActions = new KGpgExternalActions(parent, parent->getModel()); 0290 0291 signActions->droppedUrls = urls; 0292 0293 KgpgSelectSecretKey *keydlg = new KgpgSelectSecretKey(parent, parent->getModel(), false); 0294 connect(keydlg, &KgpgSelectSecretKey::accepted, signActions, &KGpgExternalActions::slotSignFiles); 0295 connect(keydlg, &KgpgSelectSecretKey::rejected, keydlg, &KgpgSelectSecretKey::deleteLater); 0296 connect(keydlg, &KgpgSelectSecretKey::rejected, signActions, &KGpgExternalActions::deleteLater); 0297 keydlg->show(); 0298 } 0299 0300 void KGpgExternalActions::slotSignFiles() 0301 { 0302 KgpgSelectSecretKey *keydlg = qobject_cast<KgpgSelectSecretKey *>(sender()); 0303 Q_ASSERT(keydlg != nullptr); 0304 sender()->deleteLater(); 0305 0306 const QString signKeyID = keydlg->getKeyID(); 0307 0308 QStringList Options; 0309 KGpgSignText::SignOptions sopts = KGpgSignText::DetachedSignature; 0310 if (KGpgSettings::asciiArmor()) { 0311 Options << QLatin1String( "--armor" ); 0312 sopts |= KGpgSignText::AsciiArmored; 0313 } 0314 if (KGpgSettings::pgpCompatibility()) 0315 Options << QLatin1String( "--pgp6" ); 0316 0317 if (droppedUrls.count() > 1) { 0318 KGpgTextInterface *signFileProcess = new KGpgTextInterface(parent(), signKeyID, Options); 0319 connect(signFileProcess, &KGpgTextInterface::fileSignFinished, signFileProcess, &KGpgTextInterface::deleteLater); 0320 signFileProcess->signFiles(droppedUrls); 0321 } else { 0322 KGpgSignText *signt = new KGpgSignText(parent(), signKeyID, droppedUrls, sopts); 0323 connect(signt, &KGpgSignText::done, signt, &KGpgSignText::deleteLater); 0324 signt->start(); 0325 } 0326 0327 deleteLater(); 0328 } 0329 0330 void KGpgExternalActions::decryptFiles(KeysManager* parent, const QList<QUrl> &urls) 0331 { 0332 KGpgExternalActions *decActions = new KGpgExternalActions(parent, parent->getModel()); 0333 0334 decActions->decryptFile(urls); 0335 } 0336 0337 void KGpgExternalActions::decryptFile(QList<QUrl> urls) 0338 { 0339 if (urls.isEmpty()) { 0340 deleteLater(); 0341 return; 0342 } 0343 0344 while (!urls.first().isLocalFile()) { 0345 showDroppedFile(urls.takeFirst()); 0346 } 0347 0348 QUrl first = urls.first(); 0349 0350 QString oldname(first.fileName()); 0351 if (oldname.endsWith(QLatin1String(".gpg"), Qt::CaseInsensitive) || 0352 oldname.endsWith(QLatin1String(".asc"), Qt::CaseInsensitive) || 0353 oldname.endsWith(QLatin1String(".pgp"), Qt::CaseInsensitive)) 0354 oldname.chop(4); 0355 else 0356 oldname.append(QLatin1String( ".clear" )); 0357 0358 QUrl swapname = QUrl::fromLocalFile(first.adjusted(QUrl::RemoveFilename).path() + oldname); 0359 QFile fgpg(swapname.path()); 0360 if (fgpg.exists()) { 0361 QPointer<KIO::RenameDialog> over = new KIO::RenameDialog(m_keysmanager, 0362 i18n("File Already Exists"), QUrl(), swapname, KIO::RenameDialog_Overwrite); 0363 if (over->exec() != QDialog::Accepted) { 0364 delete over; 0365 urls.pop_front(); 0366 decryptFile(urls); 0367 return; 0368 } 0369 0370 swapname = over->newDestUrl(); 0371 delete over; 0372 } 0373 0374 droppedUrls = urls; 0375 KGpgDecrypt *decr = new KGpgDecrypt(this, droppedUrls.first(), swapname); 0376 connect(decr, &KGpgDecrypt::done, this, &KGpgExternalActions::slotDecryptionDone); 0377 decr->start(); 0378 } 0379 0380 void KGpgExternalActions::slotDecryptionDone(int status) 0381 { 0382 KGpgDecrypt *decr = qobject_cast<KGpgDecrypt *>(sender()); 0383 Q_ASSERT(decr != nullptr); 0384 0385 if (status != KGpgTransaction::TS_OK) 0386 m_decryptionFailed << droppedUrls.first(); 0387 0388 decr->deleteLater(); 0389 0390 droppedUrls.pop_front(); 0391 0392 if (!droppedUrls.isEmpty()) { 0393 decryptFile(droppedUrls); 0394 } else { 0395 if (!m_decryptionFailed.isEmpty()) { 0396 QStringList failedFiles; 0397 for (const QUrl &url : qAsConst(m_decryptionFailed)) 0398 failedFiles.append(url.toDisplayString()); 0399 KMessageBox::errorList(nullptr, 0400 i18np("Decryption of this file failed:", "Decryption of these files failed:", 0401 m_decryptionFailed.count()), failedFiles, 0402 i18n("Decryption failed.")); 0403 } 0404 deleteLater(); 0405 } 0406 } 0407 0408 void KGpgExternalActions::showDroppedFile(const QUrl &file) 0409 { 0410 KgpgEditor *kgpgtxtedit = new KgpgEditor(m_keysmanager, m_model, {}); 0411 connect(m_keysmanager, &KeysManager::fontChanged, kgpgtxtedit, &KgpgEditor::slotSetFont); 0412 0413 kgpgtxtedit->m_editor->openDroppedFile(file, false); 0414 0415 kgpgtxtedit->show(); 0416 } 0417 0418 void KGpgExternalActions::readOptions() 0419 { 0420 if (KGpgSettings::firstRun()) { 0421 firstRun(); 0422 } else if (KGpgSettings::gpgConfigPath().isEmpty()) { 0423 if (KMessageBox::Yes == KMessageBox::questionYesNo(nullptr, 0424 i18n("<qt>You have not set a path to your GnuPG config file.<br />This may cause some surprising results in KGpg's execution." 0425 "<br />Would you like to start KGpg's assistant to fix this problem?</qt>"), 0426 QString(), KGuiItem(i18n("Start Assistant")), KGuiItem(i18n("Do Not Start")))) 0427 startAssistant(); 0428 } 0429 } 0430 0431 void KGpgExternalActions::firstRun() 0432 { 0433 QProcess *createConfigProc = new QProcess(this); 0434 QStringList args; 0435 args << QLatin1String( "--no-tty" ) << QLatin1String( "--list-secret-keys" ); 0436 createConfigProc->start(QLatin1String( "gpg" ), args); // start GnuPG so that it will create a config file 0437 createConfigProc->waitForFinished(); 0438 startAssistant(); 0439 } 0440 0441 void KGpgExternalActions::startAssistant() 0442 { 0443 if (m_assistant.isNull()) { 0444 m_assistant = new KGpgFirstAssistant(m_keysmanager); 0445 0446 connect(m_assistant.data(), &KGpgFirstAssistant::accepted, this, &KGpgExternalActions::slotSaveOptionsPath); 0447 connect(m_assistant.data(), &KGpgFirstAssistant::rejected, m_assistant.data(), &KGpgFirstAssistant::deleteLater); 0448 connect(m_assistant->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &KGpgExternalActions::help); 0449 } 0450 0451 m_assistant->show(); 0452 } 0453 0454 void KGpgExternalActions::slotSaveOptionsPath() 0455 { 0456 KGpgSettings::setAutoStart(m_assistant->getAutoStart()); 0457 KGpgSettings::setGpgConfigPath(m_assistant->getConfigPath()); 0458 KGpgSettings::setFirstRun(false); 0459 0460 const QString gpgConfServer(KgpgInterface::getGpgSetting(QLatin1String( "keyserver" ), KGpgSettings::gpgConfigPath())); 0461 if (!gpgConfServer.isEmpty()) { 0462 // The user already had configured a keyserver, set this one as default. 0463 QStringList serverList(KGpgSettings::keyServers()); 0464 serverList.prepend(gpgConfServer); 0465 KGpgSettings::setKeyServers(serverList); 0466 } 0467 0468 const QString defaultID(m_assistant->getDefaultKey()); 0469 0470 KGpgSettings::self()->save(); 0471 Q_EMIT updateDefault(defaultID); 0472 if (m_assistant->runKeyGenerate()) 0473 Q_EMIT createNewKey(); 0474 m_assistant->deleteLater(); 0475 } 0476 0477 void KGpgExternalActions::help() 0478 { 0479 KHelpClient::invokeHelp(QString(), QLatin1String( "kgpg" )); 0480 } 0481 0482 QKeySequence KGpgExternalActions::goDefaultKey() const 0483 { 0484 return QKeySequence(qobject_cast<QAction *>(m_keysmanager->actionCollection()->action(QLatin1String( "go_default_key" )))->shortcut()); 0485 }