File indexing completed on 2024-06-23 05:14:18
0001 /* -*- mode: c++; c-basic-offset:4 -*- 0002 utils/output.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <config-kleopatra.h> 0011 0012 #include "output.h" 0013 0014 #include "cached.h" 0015 #include "detail_p.h" 0016 #include "input_p.h" 0017 #include "kdpipeiodevice.h" 0018 #include "kleo_assert.h" 0019 #include "log.h" 0020 #include "overwritedialog.h" 0021 0022 #include <Libkleo/KleoException> 0023 0024 #include <KFileUtils> 0025 #include <KLocalizedString> 0026 #include <KMessageBox> 0027 0028 #include "kleopatra_debug.h" 0029 0030 #include <QApplication> 0031 #include <QBuffer> 0032 #include <QClipboard> 0033 #include <QDir> 0034 #include <QFileInfo> 0035 #include <QPointer> 0036 #include <QProcess> 0037 #include <QString> 0038 #include <QTemporaryFile> 0039 #include <QTimer> 0040 #include <QUrl> 0041 #include <QWidget> 0042 0043 #ifdef Q_OS_WIN 0044 #include <windows.h> 0045 #endif 0046 0047 #include <cerrno> 0048 0049 using namespace Kleo; 0050 using namespace Kleo::_detail; 0051 0052 static const int PROCESS_MAX_RUNTIME_TIMEOUT = -1; // no timeout 0053 static const int PROCESS_TERMINATE_TIMEOUT = 5 * 1000; // 5s 0054 0055 class OverwritePolicy::Private 0056 { 0057 public: 0058 Private(QWidget *p, OverwritePolicy::Options options_, OverwritePolicy::Policy pol) 0059 : policy(pol) 0060 , parentWidget(p) 0061 , options{options_} 0062 { 0063 } 0064 0065 OverwritePolicy::Policy policy; 0066 QWidget *parentWidget; 0067 OverwritePolicy::Options options; 0068 }; 0069 0070 OverwritePolicy::OverwritePolicy(Policy initialPolicy) 0071 : d{new Private{nullptr, {}, initialPolicy}} 0072 { 0073 } 0074 0075 OverwritePolicy::OverwritePolicy(QWidget *parent, OverwritePolicy::Options options) 0076 : d{new Private{parent, options, Ask}} 0077 { 0078 } 0079 0080 OverwritePolicy::~OverwritePolicy() = default; 0081 0082 OverwritePolicy::Policy OverwritePolicy::policy() const 0083 { 0084 return d->policy; 0085 } 0086 0087 void OverwritePolicy::setPolicy(Policy policy) 0088 { 0089 d->policy = policy; 0090 } 0091 0092 namespace 0093 { 0094 0095 class TemporaryFile : public QTemporaryFile 0096 { 0097 public: 0098 using QTemporaryFile::QTemporaryFile; 0099 0100 void close() override 0101 { 0102 if (isOpen()) { 0103 m_oldFileName = fileName(); 0104 } 0105 QTemporaryFile::close(); 0106 } 0107 0108 bool openNonInheritable() 0109 { 0110 if (!QTemporaryFile::open()) { 0111 return false; 0112 } 0113 #if defined(Q_OS_WIN) 0114 // QTemporaryFile (tested with 4.3.3) creates the file handle as inheritable. 0115 // The handle is then inherited by gpgsm, which prevents deletion of the temp file 0116 // in FileOutput::doFinalize() 0117 // There are no inheritable handles under wince 0118 return SetHandleInformation((HANDLE)_get_osfhandle(handle()), HANDLE_FLAG_INHERIT, 0); 0119 #endif 0120 return true; 0121 } 0122 0123 QString oldFileName() const 0124 { 0125 return m_oldFileName; 0126 } 0127 0128 private: 0129 QString m_oldFileName; 0130 }; 0131 0132 template<typename T_IODevice> 0133 struct inhibit_close : T_IODevice { 0134 explicit inhibit_close() 0135 : T_IODevice() 0136 { 0137 } 0138 template<typename T1> 0139 explicit inhibit_close(T1 &t1) 0140 : T_IODevice(t1) 0141 { 0142 } 0143 0144 /* reimp */ void close() override 0145 { 0146 } 0147 void reallyClose() 0148 { 0149 T_IODevice::close(); 0150 } 0151 }; 0152 0153 template<typename T_IODevice> 0154 struct redirect_close : T_IODevice { 0155 explicit redirect_close() 0156 : T_IODevice() 0157 , m_closed(false) 0158 { 0159 } 0160 template<typename T1> 0161 explicit redirect_close(T1 &t1) 0162 : T_IODevice(t1) 0163 , m_closed(false) 0164 { 0165 } 0166 0167 /* reimp */ void close() override 0168 { 0169 this->closeWriteChannel(); 0170 m_closed = true; 0171 } 0172 0173 bool isClosed() const 0174 { 0175 return m_closed; 0176 } 0177 0178 private: 0179 bool m_closed; 0180 }; 0181 0182 class FileOutput; 0183 class OutputInput : public InputImplBase 0184 { 0185 public: 0186 OutputInput(const std::shared_ptr<FileOutput> &output); 0187 0188 unsigned int classification() const override 0189 { 0190 return 0; 0191 } 0192 0193 void outputFinalized() 0194 { 0195 if (!m_ioDevice->open(QIODevice::ReadOnly)) { 0196 qCCritical(KLEOPATRA_LOG) << "Failed to open file for reading"; 0197 } 0198 } 0199 0200 std::shared_ptr<QIODevice> ioDevice() const override 0201 { 0202 return m_ioDevice; 0203 } 0204 0205 unsigned long long size() const override 0206 { 0207 return 0; 0208 } 0209 0210 private: 0211 std::shared_ptr<FileOutput> m_output; 0212 mutable std::shared_ptr<QIODevice> m_ioDevice = nullptr; 0213 }; 0214 0215 class OutputImplBase : public Output 0216 { 0217 public: 0218 OutputImplBase() 0219 : Output() 0220 , m_defaultLabel() 0221 , m_customLabel() 0222 , m_errorString() 0223 , m_isFinalized(false) 0224 , m_isFinalizing(false) 0225 , m_cancelPending(false) 0226 , m_canceled(false) 0227 , m_binaryOpt(false) 0228 { 0229 } 0230 0231 QString label() const override 0232 { 0233 return m_customLabel.isEmpty() ? m_defaultLabel : m_customLabel; 0234 } 0235 void setLabel(const QString &label) override 0236 { 0237 m_customLabel = label; 0238 } 0239 void setDefaultLabel(const QString &l) 0240 { 0241 m_defaultLabel = l; 0242 } 0243 void setBinaryOpt(bool value) override 0244 { 0245 m_binaryOpt = value; 0246 } 0247 bool binaryOpt() const override 0248 { 0249 return m_binaryOpt; 0250 } 0251 0252 QString errorString() const override 0253 { 0254 if (m_errorString.dirty()) { 0255 m_errorString = doErrorString(); 0256 } 0257 return m_errorString; 0258 } 0259 0260 bool isFinalized() const override 0261 { 0262 return m_isFinalized; 0263 } 0264 void finalize() override 0265 { 0266 qCDebug(KLEOPATRA_LOG) << this; 0267 if (m_isFinalized || m_isFinalizing) { 0268 return; 0269 } 0270 m_isFinalizing = true; 0271 try { 0272 doFinalize(); 0273 } catch (...) { 0274 m_isFinalizing = false; 0275 throw; 0276 } 0277 m_isFinalizing = false; 0278 m_isFinalized = true; 0279 if (m_cancelPending) { 0280 cancel(); 0281 } 0282 } 0283 0284 void cancel() override 0285 { 0286 qCDebug(KLEOPATRA_LOG) << this; 0287 if (m_isFinalizing) { 0288 m_cancelPending = true; 0289 } else if (!m_canceled) { 0290 m_isFinalizing = true; 0291 try { 0292 doCancel(); 0293 } catch (...) { 0294 } 0295 m_isFinalizing = false; 0296 m_isFinalized = false; 0297 m_canceled = true; 0298 } 0299 } 0300 0301 private: 0302 virtual QString doErrorString() const 0303 { 0304 if (std::shared_ptr<QIODevice> io = ioDevice()) { 0305 return io->errorString(); 0306 } else { 0307 return i18n("No output device"); 0308 } 0309 } 0310 virtual void doFinalize() = 0; 0311 virtual void doCancel() = 0; 0312 0313 private: 0314 QString m_defaultLabel; 0315 QString m_customLabel; 0316 mutable cached<QString> m_errorString; 0317 bool m_isFinalized : 1; 0318 bool m_isFinalizing : 1; 0319 bool m_cancelPending : 1; 0320 bool m_canceled : 1; 0321 bool m_binaryOpt : 1; 0322 }; 0323 0324 class PipeOutput : public OutputImplBase 0325 { 0326 public: 0327 explicit PipeOutput(assuan_fd_t fd); 0328 0329 std::shared_ptr<QIODevice> ioDevice() const override 0330 { 0331 return m_io; 0332 } 0333 void doFinalize() override 0334 { 0335 m_io->reallyClose(); 0336 } 0337 void doCancel() override 0338 { 0339 doFinalize(); 0340 } 0341 0342 private: 0343 std::shared_ptr<inhibit_close<KDPipeIODevice>> m_io; 0344 }; 0345 0346 class ProcessStdInOutput : public OutputImplBase 0347 { 0348 public: 0349 explicit ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd); 0350 0351 std::shared_ptr<QIODevice> ioDevice() const override 0352 { 0353 return m_proc; 0354 } 0355 void doFinalize() override 0356 { 0357 /* 0358 Make sure the data is written in the output here. If this 0359 is not done the output will be written in small chunks 0360 trough the eventloop causing an unnecessary delay before 0361 the process has even a chance to work and finish. 0362 This delay is mainly noticeable on Windows where it can 0363 take ~30 seconds to write out a 10MB file in the 512 byte 0364 chunks gpgme serves. */ 0365 qCDebug(KLEOPATRA_LOG) << "Waiting for " << m_proc->bytesToWrite() << " Bytes to be written"; 0366 while (m_proc->waitForBytesWritten(PROCESS_MAX_RUNTIME_TIMEOUT)) 0367 ; 0368 0369 if (!m_proc->isClosed()) { 0370 m_proc->close(); 0371 } 0372 m_proc->waitForFinished(PROCESS_MAX_RUNTIME_TIMEOUT); 0373 } 0374 0375 bool failed() const override 0376 { 0377 if (!m_proc) { 0378 return false; 0379 } 0380 return !(m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0); 0381 } 0382 0383 void doCancel() override 0384 { 0385 m_proc->terminate(); 0386 QTimer::singleShot(PROCESS_TERMINATE_TIMEOUT, m_proc.get(), &QProcess::kill); 0387 } 0388 QString label() const override; 0389 0390 private: 0391 QString doErrorString() const override; 0392 0393 private: 0394 const QString m_command; 0395 const QStringList m_arguments; 0396 const std::shared_ptr<redirect_close<QProcess>> m_proc; 0397 }; 0398 0399 class FileOutput : public OutputImplBase 0400 { 0401 public: 0402 explicit FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy); 0403 ~FileOutput() override 0404 { 0405 qCDebug(KLEOPATRA_LOG) << this; 0406 } 0407 0408 QString label() const override 0409 { 0410 return QFileInfo(m_fileName).fileName(); 0411 } 0412 std::shared_ptr<QIODevice> ioDevice() const override 0413 { 0414 return m_tmpFile; 0415 } 0416 void doFinalize() override; 0417 void doCancel() override 0418 { 0419 qCDebug(KLEOPATRA_LOG) << this; 0420 } 0421 QString fileName() const override 0422 { 0423 return m_fileName; 0424 } 0425 0426 void attachInput(const std::shared_ptr<OutputInput> &input) 0427 { 0428 m_attachedInput = std::weak_ptr<OutputInput>(input); 0429 } 0430 0431 private: 0432 QString m_fileName; 0433 std::shared_ptr<TemporaryFile> m_tmpFile; 0434 const std::shared_ptr<OverwritePolicy> m_policy; 0435 std::weak_ptr<OutputInput> m_attachedInput; 0436 }; 0437 0438 #ifndef QT_NO_CLIPBOARD 0439 class ClipboardOutput : public OutputImplBase 0440 { 0441 public: 0442 explicit ClipboardOutput(QClipboard::Mode mode); 0443 0444 QString label() const override; 0445 std::shared_ptr<QIODevice> ioDevice() const override 0446 { 0447 return m_buffer; 0448 } 0449 void doFinalize() override; 0450 void doCancel() override 0451 { 0452 } 0453 0454 private: 0455 QString doErrorString() const override 0456 { 0457 return QString(); 0458 } 0459 0460 private: 0461 const QClipboard::Mode m_mode; 0462 std::shared_ptr<QBuffer> m_buffer; 0463 }; 0464 #endif // QT_NO_CLIPBOARD 0465 0466 class ByteArrayOutput : public OutputImplBase 0467 { 0468 public: 0469 explicit ByteArrayOutput(QByteArray *data) 0470 : m_buffer(std::shared_ptr<QBuffer>(new QBuffer(data))) 0471 { 0472 if (!m_buffer->open(QIODevice::WriteOnly)) 0473 throw Exception(gpg_error(GPG_ERR_EIO), QStringLiteral("Could not open bytearray for writing?!")); 0474 } 0475 0476 QString label() const override 0477 { 0478 return m_label; 0479 } 0480 0481 void setLabel(const QString &label) override 0482 { 0483 m_label = label; 0484 } 0485 0486 std::shared_ptr<QIODevice> ioDevice() const override 0487 { 0488 return m_buffer; 0489 } 0490 0491 void doFinalize() override 0492 { 0493 m_buffer->close(); 0494 } 0495 0496 void doCancel() override 0497 { 0498 m_buffer->close(); 0499 } 0500 0501 private: 0502 QString doErrorString() const override 0503 { 0504 return QString(); 0505 } 0506 0507 private: 0508 QString m_label; 0509 std::shared_ptr<QBuffer> m_buffer; 0510 }; 0511 0512 } 0513 0514 std::shared_ptr<Output> Output::createFromPipeDevice(assuan_fd_t fd, const QString &label) 0515 { 0516 std::shared_ptr<PipeOutput> po(new PipeOutput(fd)); 0517 po->setDefaultLabel(label); 0518 return po; 0519 } 0520 0521 PipeOutput::PipeOutput(assuan_fd_t fd) 0522 : OutputImplBase() 0523 , m_io(new inhibit_close<KDPipeIODevice>) 0524 { 0525 errno = 0; 0526 if (!m_io->open(fd, QIODevice::WriteOnly)) 0527 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n("Could not open FD %1 for writing", assuanFD2int(fd))); 0528 } 0529 0530 std::shared_ptr<Output> Output::createFromFile(const QString &fileName, bool forceOverwrite) 0531 { 0532 return createFromFile(fileName, std::make_shared<OverwritePolicy>(forceOverwrite ? OverwritePolicy::Overwrite : OverwritePolicy::Skip)); 0533 } 0534 0535 std::shared_ptr<Output> Output::createFromFile(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy) 0536 { 0537 std::shared_ptr<FileOutput> fo(new FileOutput(fileName, policy)); 0538 qCDebug(KLEOPATRA_LOG) << fo.get(); 0539 return fo; 0540 } 0541 0542 FileOutput::FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy) 0543 : OutputImplBase() 0544 , m_fileName(fileName) 0545 , m_tmpFile(new TemporaryFile(fileName)) 0546 , m_policy(policy) 0547 { 0548 Q_ASSERT(m_policy); 0549 errno = 0; 0550 if (!m_tmpFile->openNonInheritable()) 0551 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n("Could not create temporary file for output \"%1\"", fileName)); 0552 } 0553 0554 static QString suggestFileName(const QString &fileName) 0555 { 0556 const QFileInfo fileInfo{fileName}; 0557 const QString path = fileInfo.absolutePath(); 0558 const QString newFileName = KFileUtils::suggestName(QUrl::fromLocalFile(path), fileInfo.fileName()); 0559 return path + QLatin1Char{'/'} + newFileName; 0560 } 0561 0562 QString OverwritePolicy::obtainOverwritePermission(const QString &fileName) 0563 { 0564 switch (d->policy) { 0565 case OverwritePolicy::Ask: 0566 break; 0567 case OverwritePolicy::Overwrite: 0568 return fileName; 0569 case OverwritePolicy::Rename: { 0570 return suggestFileName(fileName); 0571 } 0572 case OverwritePolicy::Skip: 0573 return {}; 0574 case OverwritePolicy::Cancel: 0575 qCDebug(KLEOPATRA_LOG) << __func__ << "Error: Called although user canceled operation"; 0576 return {}; 0577 } 0578 0579 OverwriteDialog::Options options = OverwriteDialog::AllowRename; 0580 if (d->options & MultipleFiles) { 0581 options |= OverwriteDialog::MultipleItems | OverwriteDialog::AllowSkip; 0582 } 0583 OverwriteDialog dialog{d->parentWidget, i18nc("@title:window", "File Already Exists"), fileName, options}; 0584 const auto result = static_cast<OverwriteDialog::Result>(dialog.exec()); 0585 qCDebug(KLEOPATRA_LOG) << __func__ << "result:" << static_cast<int>(result); 0586 switch (result) { 0587 case OverwriteDialog::Cancel: 0588 d->policy = OverwritePolicy::Cancel; 0589 return {}; 0590 case OverwriteDialog::AutoSkip: 0591 d->policy = OverwritePolicy::Skip; 0592 [[fallthrough]]; 0593 case OverwriteDialog::Skip: 0594 return {}; 0595 case OverwriteDialog::OverwriteAll: 0596 d->policy = OverwritePolicy::Overwrite; 0597 [[fallthrough]]; 0598 case OverwriteDialog::Overwrite: 0599 return fileName; 0600 case OverwriteDialog::Rename: 0601 return dialog.newFileName(); 0602 case OverwriteDialog::AutoRename: { 0603 d->policy = OverwritePolicy::Rename; 0604 return suggestFileName(fileName); 0605 } 0606 default: 0607 qCDebug(KLEOPATRA_LOG) << __func__ << "unexpected result:" << result; 0608 }; 0609 return {}; 0610 } 0611 0612 void FileOutput::doFinalize() 0613 { 0614 qCDebug(KLEOPATRA_LOG) << this; 0615 0616 struct Remover { 0617 QString file; 0618 ~Remover() 0619 { 0620 if (QFile::exists(file)) { 0621 QFile::remove(file); 0622 } 0623 } 0624 } remover; 0625 0626 kleo_assert(m_tmpFile); 0627 0628 if (m_tmpFile->isOpen()) { 0629 m_tmpFile->close(); 0630 } 0631 0632 QString tmpFileName = m_tmpFile->oldFileName(); 0633 remover.file = tmpFileName; 0634 0635 m_tmpFile->setAutoRemove(false); 0636 QPointer<QObject> guard = m_tmpFile.get(); 0637 m_tmpFile.reset(); // really close the file - needed on Windows for renaming :/ 0638 kleo_assert(!guard); // if this triggers, we need to audit for holder of std::shared_ptr<QIODevice>s. 0639 0640 const QFileInfo fi(tmpFileName); 0641 if (!fi.exists()) { 0642 /* QT Bug 83365 since qt 5.13 causes the filename of temporary files 0643 * in UNC path name directories (unmounted samba shares) to start with 0644 * UNC/ instead of the // that Qt can actually handle for things like 0645 * rename and remove. So if we can't find our temporary file we try 0646 * to workaround that bug. */ 0647 qCDebug(KLEOPATRA_LOG) << "failure to find " << tmpFileName; 0648 if (tmpFileName.startsWith(QLatin1StringView("UNC"))) { 0649 tmpFileName.replace(0, strlen("UNC"), QLatin1Char('/')); 0650 remover.file = tmpFileName; 0651 } 0652 const QFileInfo fi2(tmpFileName); 0653 if (!fi2.exists()) { 0654 throw Exception(gpg_error(GPG_ERR_EIO), QStringLiteral("Could not find temporary file \"%1\".").arg(tmpFileName)); 0655 } 0656 } 0657 0658 qCDebug(KLEOPATRA_LOG) << this << "renaming" << tmpFileName << "->" << m_fileName; 0659 if (QFile::rename(tmpFileName, m_fileName)) { 0660 qCDebug(KLEOPATRA_LOG) << this << "renaming succeeded"; 0661 0662 if (!m_attachedInput.expired()) { 0663 m_attachedInput.lock()->outputFinalized(); 0664 } 0665 return; 0666 } 0667 0668 qCDebug(KLEOPATRA_LOG) << this << "renaming failed"; 0669 0670 if (QFile::exists(m_fileName)) { 0671 const auto newFileName = m_policy->obtainOverwritePermission(m_fileName); 0672 if (newFileName.isEmpty()) { 0673 throw Exception(gpg_error(GPG_ERR_CANCELED), i18n("Overwriting declined")); 0674 } 0675 if (newFileName == m_fileName) { 0676 qCDebug(KLEOPATRA_LOG) << this << "going to remove file for overwriting" << m_fileName; 0677 if (!QFile::remove(m_fileName)) { 0678 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), 0679 xi18nc("@info", "Could not remove file <filename>%1</filename> for overwriting.", m_fileName)); 0680 } 0681 qCDebug(KLEOPATRA_LOG) << this << "removing file to overwrite succeeded"; 0682 } else { 0683 m_fileName = newFileName; 0684 } 0685 } 0686 0687 qCDebug(KLEOPATRA_LOG) << this << "renaming" << tmpFileName << "->" << m_fileName; 0688 if (QFile::rename(tmpFileName, m_fileName)) { 0689 qCDebug(KLEOPATRA_LOG) << this << "renaming succeeded"; 0690 0691 if (!m_attachedInput.expired()) { 0692 m_attachedInput.lock()->outputFinalized(); 0693 } 0694 return; 0695 } 0696 0697 qCDebug(KLEOPATRA_LOG) << this << "renaming failed"; 0698 0699 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n(R"(Could not rename file "%1" to "%2")", tmpFileName, m_fileName)); 0700 } 0701 0702 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command) 0703 { 0704 return std::shared_ptr<Output>(new ProcessStdInOutput(command, QStringList(), QDir::current())); 0705 } 0706 0707 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args) 0708 { 0709 return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, QDir::current())); 0710 } 0711 0712 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args, const QDir &wd) 0713 { 0714 return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, wd)); 0715 } 0716 0717 ProcessStdInOutput::ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd) 0718 : OutputImplBase() 0719 , m_command(cmd) 0720 , m_arguments(args) 0721 , m_proc(new redirect_close<QProcess>) 0722 { 0723 qCDebug(KLEOPATRA_LOG) << "cd" << wd.absolutePath() << '\n' << cmd << args; 0724 if (cmd.isEmpty()) 0725 throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Command not specified")); 0726 m_proc->setWorkingDirectory(wd.absolutePath()); 0727 m_proc->start(cmd, args); 0728 m_proc->setReadChannel(QProcess::StandardError); 0729 if (!m_proc->waitForStarted()) 0730 throw Exception(gpg_error(GPG_ERR_EIO), i18n("Could not start %1 process: %2", cmd, m_proc->errorString())); 0731 } 0732 0733 QString ProcessStdInOutput::label() const 0734 { 0735 if (!m_proc) { 0736 return OutputImplBase::label(); 0737 } 0738 // output max. 3 arguments 0739 const QString cmdline = (QStringList(m_command) + m_arguments.mid(0, 3)).join(QLatin1Char(' ')); 0740 if (m_arguments.size() > 3) { 0741 return i18nc("e.g. \"Input to tar xf - file1 ...\"", "Input to %1 ...", cmdline); 0742 } else { 0743 return i18nc("e.g. \"Input to tar xf - file\"", "Input to %1", cmdline); 0744 } 0745 } 0746 0747 QString ProcessStdInOutput::doErrorString() const 0748 { 0749 kleo_assert(m_proc); 0750 if (m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0) { 0751 return QString(); 0752 } 0753 if (m_proc->error() == QProcess::UnknownError) 0754 return i18n("Error while running %1: %2", m_command, QString::fromLocal8Bit(m_proc->readAllStandardError().trimmed().constData())); 0755 else { 0756 return i18n("Failed to execute %1: %2", m_command, m_proc->errorString()); 0757 } 0758 } 0759 0760 #ifndef QT_NO_CLIPBOARD 0761 std::shared_ptr<Output> Output::createFromClipboard() 0762 { 0763 return std::shared_ptr<Output>(new ClipboardOutput(QClipboard::Clipboard)); 0764 } 0765 0766 ClipboardOutput::ClipboardOutput(QClipboard::Mode mode) 0767 : OutputImplBase() 0768 , m_mode(mode) 0769 , m_buffer(new QBuffer) 0770 { 0771 errno = 0; 0772 if (!m_buffer->open(QIODevice::WriteOnly)) 0773 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n("Could not write to clipboard")); 0774 } 0775 0776 QString ClipboardOutput::label() const 0777 { 0778 switch (m_mode) { 0779 case QClipboard::Clipboard: 0780 return i18n("Clipboard"); 0781 case QClipboard::FindBuffer: 0782 return i18n("Find buffer"); 0783 case QClipboard::Selection: 0784 return i18n("Selection"); 0785 } 0786 return QString(); 0787 } 0788 0789 void ClipboardOutput::doFinalize() 0790 { 0791 if (m_buffer->isOpen()) { 0792 m_buffer->close(); 0793 } 0794 if (QClipboard *const cb = QApplication::clipboard()) { 0795 cb->setText(QString::fromUtf8(m_buffer->data())); 0796 } else 0797 throw Exception(gpg_error(GPG_ERR_EIO), i18n("Could not find clipboard")); 0798 } 0799 #endif // QT_NO_CLIPBOARD 0800 0801 Output::~Output() 0802 { 0803 } 0804 0805 OutputInput::OutputInput(const std::shared_ptr<FileOutput> &output) 0806 : m_output(output) 0807 , m_ioDevice(new QFile(output->fileName())) 0808 { 0809 } 0810 0811 std::shared_ptr<Input> Input::createFromOutput(const std::shared_ptr<Output> &output) 0812 { 0813 if (auto fo = std::dynamic_pointer_cast<FileOutput>(output)) { 0814 auto input = std::shared_ptr<OutputInput>(new OutputInput(fo)); 0815 fo->attachInput(input); 0816 return input; 0817 } else { 0818 return {}; 0819 } 0820 } 0821 0822 std::shared_ptr<Output> Output::createFromByteArray(QByteArray *data, const QString &label) 0823 { 0824 auto ret = std::shared_ptr<ByteArrayOutput>(new ByteArrayOutput(data)); 0825 ret->setLabel(label); 0826 return ret; 0827 }