1 /* -*- mode: c++; c-basic-offset:4 -*-
2 utils/output.cpp
3
4 This file is part of Kleopatra, the KDE keymanager
5 SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include <config-kleopatra.h>
11
12 #include "output.h"
13 #include "input_p.h"
14 #include "detail_p.h"
15 #include "kleo_assert.h"
16 #include "kdpipeiodevice.h"
17 #include "log.h"
18 #include "cached.h"
19
20 #include <Libkleo/KleoException>
21
22 #include <KLocalizedString>
23 #include <KMessageBox>
24 #include "kleopatra_debug.h"
25
26 #include <QFileInfo>
27 #include <QTemporaryFile>
28 #include <QString>
29 #include <QClipboard>
30 #include <QApplication>
31 #include <QBuffer>
32 #include <QPointer>
33 #include <QWidget>
34 #include <QDir>
35 #include <QProcess>
36 #include <QTimer>
37
38 #ifdef Q_OS_WIN
39 # include <windows.h>
40 #endif
41
42 #include <cerrno>
43
44 using namespace Kleo;
45 using namespace Kleo::_detail;
46
47 static const int PROCESS_MAX_RUNTIME_TIMEOUT = -1; // no timeout
48 static const int PROCESS_TERMINATE_TIMEOUT = 5 * 1000; // 5s
49
50 class OverwritePolicy::Private
51 {
52 public:
Private(QWidget * p,OverwritePolicy::Policy pol)53 Private(QWidget *p, OverwritePolicy::Policy pol) : policy(pol), widget(p) {}
54 OverwritePolicy::Policy policy;
55 QWidget *widget;
56 };
57
OverwritePolicy(QWidget * parent,Policy initialPolicy)58 OverwritePolicy::OverwritePolicy(QWidget *parent, Policy initialPolicy) : d(new Private(parent, initialPolicy))
59 {
60 }
61
~OverwritePolicy()62 OverwritePolicy::~OverwritePolicy() {}
63
policy() const64 OverwritePolicy::Policy OverwritePolicy::policy() const
65 {
66 return d->policy;
67 }
68
setPolicy(Policy policy)69 void OverwritePolicy::setPolicy(Policy policy)
70 {
71 d->policy = policy;
72 }
73
parentWidget() const74 QWidget *OverwritePolicy::parentWidget() const
75 {
76 return d->widget;
77 }
78
79 namespace
80 {
81
82 class TemporaryFile : public QTemporaryFile
83 {
84 public:
TemporaryFile()85 explicit TemporaryFile() : QTemporaryFile() {}
TemporaryFile(const QString & templateName)86 explicit TemporaryFile(const QString &templateName) : QTemporaryFile(templateName) {}
TemporaryFile(QObject * parent)87 explicit TemporaryFile(QObject *parent) : QTemporaryFile(parent) {}
TemporaryFile(const QString & templateName,QObject * parent)88 explicit TemporaryFile(const QString &templateName, QObject *parent) : QTemporaryFile(templateName, parent) {}
89
close()90 void close() override {
91 if (isOpen())
92 {
93 m_oldFileName = fileName();
94 }
95 QTemporaryFile::close();
96 }
97
openNonInheritable()98 bool openNonInheritable()
99 {
100 if (!QTemporaryFile::open()) {
101 return false;
102 }
103 #if defined(Q_OS_WIN)
104 //QTemporaryFile (tested with 4.3.3) creates the file handle as inheritable.
105 //The handle is then inherited by gpgsm, which prevents deletion of the temp file
106 //in FileOutput::doFinalize()
107 //There are no inheritable handles under wince
108 return SetHandleInformation((HANDLE)_get_osfhandle(handle()), HANDLE_FLAG_INHERIT, 0);
109 #endif
110 return true;
111 }
112
oldFileName() const113 QString oldFileName() const
114 {
115 return m_oldFileName;
116 }
117
118 private:
119 QString m_oldFileName;
120 };
121
122 template <typename T_IODevice>
123 struct inhibit_close : T_IODevice {
inhibit_close__anon472f3bd00111::inhibit_close124 explicit inhibit_close() : T_IODevice() {}
125 template <typename T1>
inhibit_close__anon472f3bd00111::inhibit_close126 explicit inhibit_close(T1 &t1) : T_IODevice(t1) {}
127
close__anon472f3bd00111::inhibit_close128 /* reimp */ void close() override {}
reallyClose__anon472f3bd00111::inhibit_close129 void reallyClose()
130 {
131 T_IODevice::close();
132 }
133 };
134
135 template <typename T_IODevice>
136 struct redirect_close : T_IODevice {
redirect_close__anon472f3bd00111::redirect_close137 explicit redirect_close() : T_IODevice(), m_closed(false) {}
138 template <typename T1>
redirect_close__anon472f3bd00111::redirect_close139 explicit redirect_close(T1 &t1) : T_IODevice(t1), m_closed(false) {}
140
close__anon472f3bd00111::redirect_close141 /* reimp */ void close() override
142 {
143 this->closeWriteChannel();
144 m_closed = true;
145 }
146
isClosed__anon472f3bd00111::redirect_close147 bool isClosed() const
148 {
149 return m_closed;
150 }
151 private:
152 bool m_closed;
153 };
154
155 class FileOutput;
156 class OutputInput : public InputImplBase
157 {
158 public:
159 OutputInput(const std::shared_ptr<FileOutput> &output);
160
classification() const161 unsigned int classification() const override
162 {
163 return 0;
164 }
165
outputFinalized()166 void outputFinalized()
167 {
168 if (!m_ioDevice->open(QIODevice::ReadOnly)) {
169 qCCritical(KLEOPATRA_LOG) << "Failed to open file for reading";
170 }
171 }
172
ioDevice() const173 std::shared_ptr<QIODevice> ioDevice() const override
174 {
175 return m_ioDevice;
176 }
177
size() const178 unsigned long long size() const override
179 {
180 return 0;
181 }
182
183 private:
184 std::shared_ptr<FileOutput> m_output;
185 mutable std::shared_ptr<QIODevice> m_ioDevice = nullptr;
186 };
187
188 class OutputImplBase : public Output
189 {
190 public:
OutputImplBase()191 OutputImplBase()
192 : Output(),
193 m_defaultLabel(),
194 m_customLabel(),
195 m_errorString(),
196 m_isFinalized(false),
197 m_isFinalizing(false),
198 m_cancelPending(false),
199 m_canceled(false),
200 m_binaryOpt(false)
201 {
202
203 }
204
label() const205 QString label() const override
206 {
207 return m_customLabel.isEmpty() ? m_defaultLabel : m_customLabel;
208 }
setLabel(const QString & label)209 void setLabel(const QString &label) override {
210 m_customLabel = label;
211 }
setDefaultLabel(const QString & l)212 void setDefaultLabel(const QString &l)
213 {
214 m_defaultLabel = l;
215 }
setBinaryOpt(bool value)216 void setBinaryOpt(bool value) override {
217 m_binaryOpt = value;
218 }
binaryOpt() const219 bool binaryOpt() const override
220 {
221 return m_binaryOpt;
222 }
223
errorString() const224 QString errorString() const override
225 {
226 if (m_errorString.dirty()) {
227 m_errorString = doErrorString();
228 }
229 return m_errorString;
230 }
231
isFinalized() const232 bool isFinalized() const override
233 {
234 return m_isFinalized;
235 }
finalize()236 void finalize() override {
237 qCDebug(KLEOPATRA_LOG) << this;
238 if (m_isFinalized || m_isFinalizing)
239 {
240 return;
241 }
242 m_isFinalizing = true;
243 try {
244 doFinalize();
245 } catch (...)
246 {
247 m_isFinalizing = false;
248 throw;
249 }
250 m_isFinalizing = false;
251 m_isFinalized = true;
252 if (m_cancelPending)
253 {
254 cancel();
255 }
256 }
257
cancel()258 void cancel() override {
259 qCDebug(KLEOPATRA_LOG) << this;
260 if (m_isFinalizing)
261 {
262 m_cancelPending = true;
263 } else if (!m_canceled)
264 {
265 m_isFinalizing = true;
266 try {
267 doCancel();
268 } catch (...) {}
269 m_isFinalizing = false;
270 m_isFinalized = false;
271 m_canceled = true;
272 }
273 }
274 private:
doErrorString() const275 virtual QString doErrorString() const
276 {
277 if (std::shared_ptr<QIODevice> io = ioDevice()) {
278 return io->errorString();
279 } else {
280 return i18n("No output device");
281 }
282 }
283 virtual void doFinalize() = 0;
284 virtual void doCancel() = 0;
285 private:
286 QString m_defaultLabel;
287 QString m_customLabel;
288 mutable cached<QString> m_errorString;
289 bool m_isFinalized : 1;
290 bool m_isFinalizing : 1;
291 bool m_cancelPending : 1;
292 bool m_canceled : 1;
293 bool m_binaryOpt : 1;
294 };
295
296 class PipeOutput : public OutputImplBase
297 {
298 public:
299 explicit PipeOutput(assuan_fd_t fd);
300
ioDevice() const301 std::shared_ptr<QIODevice> ioDevice() const override
302 {
303 return m_io;
304 }
doFinalize()305 void doFinalize() override {
306 m_io->reallyClose();
307 }
doCancel()308 void doCancel() override {
309 doFinalize();
310 }
311 private:
312 std::shared_ptr< inhibit_close<KDPipeIODevice> > m_io;
313 };
314
315 class ProcessStdInOutput : public OutputImplBase
316 {
317 public:
318 explicit ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd);
319
ioDevice() const320 std::shared_ptr<QIODevice> ioDevice() const override
321 {
322 return m_proc;
323 }
doFinalize()324 void doFinalize() override {
325 /*
326 Make sure the data is written in the output here. If this
327 is not done the output will be written in small chunks
328 trough the eventloop causing an unnecessary delay before
329 the process has even a chance to work and finish.
330 This delay is mainly noticeable on Windows where it can
331 take ~30 seconds to write out a 10MB file in the 512 byte
332 chunks gpgme serves. */
333 qCDebug(KLEOPATRA_LOG) << "Waiting for " << m_proc->bytesToWrite()
334 << " Bytes to be written";
335 while (m_proc->waitForBytesWritten(PROCESS_MAX_RUNTIME_TIMEOUT));
336
337 if (!m_proc->isClosed())
338 {
339 m_proc->close();
340 }
341 m_proc->waitForFinished(PROCESS_MAX_RUNTIME_TIMEOUT);
342 }
343
failed() const344 bool failed() const override
345 {
346 if (!m_proc) {
347 return false;
348 }
349 return !(m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0);
350 }
351
doCancel()352 void doCancel() override {
353 m_proc->terminate();
354 QTimer::singleShot(PROCESS_TERMINATE_TIMEOUT, m_proc.get(), &QProcess::kill);
355 }
356 QString label() const override;
357
358 private:
359 QString doErrorString() const override;
360
361 private:
362 const QString m_command;
363 const QStringList m_arguments;
364 const std::shared_ptr< redirect_close<QProcess> > m_proc;
365 };
366
367 class FileOutput : public OutputImplBase
368 {
369 public:
370 explicit FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy);
~FileOutput()371 ~FileOutput() override
372 {
373 qCDebug(KLEOPATRA_LOG) << this;
374 }
375
label() const376 QString label() const override
377 {
378 return QFileInfo(m_fileName).fileName();
379 }
ioDevice() const380 std::shared_ptr<QIODevice> ioDevice() const override
381 {
382 return m_tmpFile;
383 }
384 void doFinalize() override;
doCancel()385 void doCancel() override {
386 qCDebug(KLEOPATRA_LOG) << this;
387 }
fileName() const388 QString fileName() const
389 {
390 return m_fileName;
391 }
392
attachInput(const std::shared_ptr<OutputInput> & input)393 void attachInput(const std::shared_ptr<OutputInput> &input)
394 {
395 m_attachedInput = std::weak_ptr<OutputInput>(input);
396 }
397
398 private:
399 bool obtainOverwritePermission();
400
401 private:
402 const QString m_fileName;
403 std::shared_ptr< TemporaryFile > m_tmpFile;
404 const std::shared_ptr<OverwritePolicy> m_policy;
405 std::weak_ptr<OutputInput> m_attachedInput;
406 };
407
408 #ifndef QT_NO_CLIPBOARD
409 class ClipboardOutput : public OutputImplBase
410 {
411 public:
412 explicit ClipboardOutput(QClipboard::Mode mode);
413
414 QString label() const override;
ioDevice() const415 std::shared_ptr<QIODevice> ioDevice() const override
416 {
417 return m_buffer;
418 }
419 void doFinalize() override;
doCancel()420 void doCancel() override {}
421
422 private:
doErrorString() const423 QString doErrorString() const override
424 {
425 return QString();
426 }
427 private:
428 const QClipboard::Mode m_mode;
429 std::shared_ptr<QBuffer> m_buffer;
430 };
431 #endif // QT_NO_CLIPBOARD
432
433 class ByteArrayOutput: public OutputImplBase
434 {
435 public:
ByteArrayOutput(QByteArray * data)436 explicit ByteArrayOutput(QByteArray *data):
437 m_buffer(std::shared_ptr<QBuffer>(new QBuffer(data)))
438 {
439 if (!m_buffer->open(QIODevice::WriteOnly))
440 throw Exception(gpg_error(GPG_ERR_EIO),
441 QStringLiteral("Could not open bytearray for writing?!"));
442 }
443
label() const444 QString label() const override
445 {
446 return m_label;
447 }
448
setLabel(const QString & label)449 void setLabel(const QString &label) override
450 {
451 m_label = label;
452 }
453
ioDevice() const454 std::shared_ptr<QIODevice> ioDevice() const override
455 {
456 return m_buffer;
457 }
458
doFinalize()459 void doFinalize() override
460 {
461 m_buffer->close();
462 }
463
doCancel()464 void doCancel() override
465 {
466 m_buffer->close();
467 }
468
469 private:
doErrorString() const470 QString doErrorString() const override
471 {
472 return QString();
473 }
474 private:
475 QString m_label;
476 std::shared_ptr<QBuffer> m_buffer;
477 };
478
479 }
480
createFromPipeDevice(assuan_fd_t fd,const QString & label)481 std::shared_ptr<Output> Output::createFromPipeDevice(assuan_fd_t fd, const QString &label)
482 {
483 std::shared_ptr<PipeOutput> po(new PipeOutput(fd));
484 po->setDefaultLabel(label);
485 return po;
486 }
487
PipeOutput(assuan_fd_t fd)488 PipeOutput::PipeOutput(assuan_fd_t fd)
489 : OutputImplBase(),
490 m_io(new inhibit_close<KDPipeIODevice>)
491 {
492 errno = 0;
493 if (!m_io->open(fd, QIODevice::WriteOnly))
494 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
495 i18n("Could not open FD %1 for writing",
496 assuanFD2int(fd)));
497 }
498
createFromFile(const QString & fileName,bool forceOverwrite)499 std::shared_ptr<Output> Output::createFromFile(const QString &fileName, bool forceOverwrite)
500 {
501 return createFromFile(fileName, std::shared_ptr<OverwritePolicy>(new OverwritePolicy(nullptr, forceOverwrite ? OverwritePolicy::Allow : OverwritePolicy::Deny)));
502
503 }
createFromFile(const QString & fileName,const std::shared_ptr<OverwritePolicy> & policy)504 std::shared_ptr<Output> Output::createFromFile(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy)
505 {
506 std::shared_ptr<FileOutput> fo(new FileOutput(fileName, policy));
507 qCDebug(KLEOPATRA_LOG) << fo.get();
508 return fo;
509 }
510
FileOutput(const QString & fileName,const std::shared_ptr<OverwritePolicy> & policy)511 FileOutput::FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy)
512 : OutputImplBase(),
513 m_fileName(fileName),
514 m_tmpFile(new TemporaryFile(fileName)),
515 m_policy(policy)
516 {
517 Q_ASSERT(m_policy);
518 errno = 0;
519 if (!m_tmpFile->openNonInheritable())
520 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
521 i18n("Could not create temporary file for output \"%1\"", fileName));
522 }
523
obtainOverwritePermission()524 bool FileOutput::obtainOverwritePermission()
525 {
526 if (m_policy->policy() != OverwritePolicy::Ask) {
527 return m_policy->policy() == OverwritePolicy::Allow;
528 }
529 const int sel = KMessageBox::questionYesNoCancel(m_policy->parentWidget(), i18n("The file <b>%1</b> already exists.\n"
530 "Overwrite?", m_fileName),
531 i18n("Overwrite Existing File?"),
532 KStandardGuiItem::overwrite(),
533 KGuiItem(i18n("Overwrite All")),
534 KStandardGuiItem::cancel());
535 if (sel == KMessageBox::No) { //Overwrite All
536 m_policy->setPolicy(OverwritePolicy::Allow);
537 }
538 return sel == KMessageBox::Yes || sel == KMessageBox::No;
539 }
540
doFinalize()541 void FileOutput::doFinalize()
542 {
543 qCDebug(KLEOPATRA_LOG) << this;
544
545 struct Remover {
546 QString file;
547 ~Remover()
548 {
549 if (QFile::exists(file)) {
550 QFile::remove(file);
551 }
552 }
553 } remover;
554
555 kleo_assert(m_tmpFile);
556
557 if (m_tmpFile->isOpen()) {
558 m_tmpFile->close();
559 }
560
561 QString tmpFileName = remover.file = m_tmpFile->oldFileName();
562
563 m_tmpFile->setAutoRemove(false);
564 QPointer<QObject> guard = m_tmpFile.get();
565 m_tmpFile.reset(); // really close the file - needed on Windows for renaming :/
566 kleo_assert(!guard); // if this triggers, we need to audit for holder of std::shared_ptr<QIODevice>s.
567
568 const QFileInfo fi(tmpFileName);
569 bool qtbug83365_workaround = false;
570 if (!fi.exists()) {
571 /* QT Bug 83365 since qt 5.13 causes the filename of temporary files
572 * in UNC path name directories (unmounted samba shares) to start with
573 * UNC/ instead of the // that Qt can actually handle for things like
574 * rename and remove. So if we can't find our temporary file we try
575 * to workaround that bug. */
576 qCDebug(KLEOPATRA_LOG) << "failure to find " << tmpFileName;
577 if (tmpFileName.startsWith(QStringLiteral("UNC"))) {
578 tmpFileName.replace(QRegExp(QStringLiteral("^UNC")), QStringLiteral("/"));
579 qtbug83365_workaround = true;
580 }
581 const QFileInfo fi2(tmpFileName);
582 if (!fi2.exists()) {
583 throw Exception(gpg_error(GPG_ERR_EIO),
584 QStringLiteral("Could not find temporary file \"%1\".").arg(tmpFileName));
585 }
586 }
587 qCDebug(KLEOPATRA_LOG) << this << " renaming " << tmpFileName << "->" << m_fileName;
588
589 if (QFile::rename(tmpFileName, m_fileName)) {
590 qCDebug(KLEOPATRA_LOG) << this << "succeeded";
591
592 if (!m_attachedInput.expired()) {
593 m_attachedInput.lock()->outputFinalized();
594 }
595 return;
596 }
597
598 qCDebug(KLEOPATRA_LOG) << this << "failed";
599
600 if (!obtainOverwritePermission())
601 throw Exception(gpg_error(GPG_ERR_CANCELED),
602 i18n("Overwriting declined"));
603
604 qCDebug(KLEOPATRA_LOG) << this << "going to overwrite" << m_fileName;
605
606 if (!QFile::remove(m_fileName))
607 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
608 i18n("Could not remove file \"%1\" for overwriting.", m_fileName));
609
610 qCDebug(KLEOPATRA_LOG) << this << "succeeded, renaming " << tmpFileName << "->" << m_fileName;
611
612 if (QFile::rename(tmpFileName, m_fileName)) {
613 qCDebug(KLEOPATRA_LOG) << this << "succeeded";
614
615 if (!m_attachedInput.expired()) {
616 m_attachedInput.lock()->outputFinalized();
617 }
618 if (qtbug83365_workaround) {
619 QFile::remove(tmpFileName);
620 }
621 return;
622 }
623
624 qCDebug(KLEOPATRA_LOG) << this << "failed";
625
626 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
627 i18n(R"(Could not rename file "%1" to "%2")",
628 tmpFileName, m_fileName));
629 }
630
createFromProcessStdIn(const QString & command)631 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command)
632 {
633 return std::shared_ptr<Output>(new ProcessStdInOutput(command, QStringList(), QDir::current()));
634 }
635
createFromProcessStdIn(const QString & command,const QStringList & args)636 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args)
637 {
638 return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, QDir::current()));
639 }
640
createFromProcessStdIn(const QString & command,const QStringList & args,const QDir & wd)641 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args, const QDir &wd)
642 {
643 return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, wd));
644 }
645
ProcessStdInOutput(const QString & cmd,const QStringList & args,const QDir & wd)646 ProcessStdInOutput::ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd)
647 : OutputImplBase(),
648 m_command(cmd),
649 m_arguments(args),
650 m_proc(new redirect_close<QProcess>)
651 {
652 qCDebug(KLEOPATRA_LOG) << "cd" << wd.absolutePath() << '\n' << cmd << args;
653 if (cmd.isEmpty())
654 throw Exception(gpg_error(GPG_ERR_INV_ARG),
655 i18n("Command not specified"));
656 m_proc->setWorkingDirectory(wd.absolutePath());
657 m_proc->start(cmd, args);
658 m_proc->setReadChannel(QProcess::StandardError);
659 if (!m_proc->waitForStarted())
660 throw Exception(gpg_error(GPG_ERR_EIO),
661 i18n("Could not start %1 process: %2", cmd, m_proc->errorString()));
662 }
663
label() const664 QString ProcessStdInOutput::label() const
665 {
666 if (!m_proc) {
667 return OutputImplBase::label();
668 }
669 // output max. 3 arguments
670 const QString cmdline = (QStringList(m_command) + m_arguments.mid(0, 3)).join(QLatin1Char(' '));
671 if (m_arguments.size() > 3) {
672 return i18nc("e.g. \"Input to tar xf - file1 ...\"", "Input to %1 ...", cmdline);
673 } else {
674 return i18nc("e.g. \"Input to tar xf - file\"", "Input to %1", cmdline);
675 }
676 }
677
doErrorString() const678 QString ProcessStdInOutput::doErrorString() const
679 {
680 kleo_assert(m_proc);
681 if (m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0) {
682 return QString();
683 }
684 if (m_proc->error() == QProcess::UnknownError)
685 return i18n("Error while running %1: %2", m_command,
686 QString::fromLocal8Bit(m_proc->readAllStandardError().trimmed().constData()));
687 else {
688 return i18n("Failed to execute %1: %2", m_command, m_proc->errorString());
689 }
690 }
691
692 #ifndef QT_NO_CLIPBOARD
createFromClipboard()693 std::shared_ptr<Output> Output::createFromClipboard()
694 {
695 return std::shared_ptr<Output>(new ClipboardOutput(QClipboard::Clipboard));
696 }
697
ClipboardOutput(QClipboard::Mode mode)698 ClipboardOutput::ClipboardOutput(QClipboard::Mode mode)
699 : OutputImplBase(),
700 m_mode(mode),
701 m_buffer(new QBuffer)
702 {
703 errno = 0;
704 if (!m_buffer->open(QIODevice::WriteOnly))
705 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
706 i18n("Could not write to clipboard"));
707 }
708
label() const709 QString ClipboardOutput::label() const
710 {
711 switch (m_mode) {
712 case QClipboard::Clipboard:
713 return i18n("Clipboard");
714 case QClipboard::FindBuffer:
715 return i18n("Find buffer");
716 case QClipboard::Selection:
717 return i18n("Selection");
718 }
719 return QString();
720 }
721
doFinalize()722 void ClipboardOutput::doFinalize()
723 {
724 if (m_buffer->isOpen()) {
725 m_buffer->close();
726 }
727 if (QClipboard *const cb = QApplication::clipboard()) {
728 cb->setText(QString::fromUtf8(m_buffer->data()));
729 } else
730 throw Exception(gpg_error(GPG_ERR_EIO),
731 i18n("Could not find clipboard"));
732 }
733 #endif // QT_NO_CLIPBOARD
734
~Output()735 Output::~Output() {}
736
737
OutputInput(const std::shared_ptr<FileOutput> & output)738 OutputInput::OutputInput(const std::shared_ptr<FileOutput> &output)
739 : m_output(output)
740 , m_ioDevice(new QFile(output->fileName()))
741 {
742 }
743
744
745
createFromOutput(const std::shared_ptr<Output> & output)746 std::shared_ptr<Input> Input::createFromOutput(const std::shared_ptr<Output> &output)
747 {
748 if (auto fo = std::dynamic_pointer_cast<FileOutput>(output)) {
749 auto input = std::shared_ptr<OutputInput>(new OutputInput(fo));
750 fo->attachInput(input);
751 return input;
752 } else {
753 return {};
754 }
755 }
756
createFromByteArray(QByteArray * data,const QString & label)757 std::shared_ptr<Output> Output::createFromByteArray(QByteArray *data, const QString &label)
758 {
759 auto ret = std::shared_ptr<ByteArrayOutput>(new ByteArrayOutput(data));
760 ret->setLabel(label);
761 return ret;
762 }
763