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