• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kleopatra

  • sources
  • kde-4.12
  • kdepim
  • kleopatra
  • utils
output.cpp
Go to the documentation of this file.
1 /* -*- mode: c++; c-basic-offset:4 -*-
2  utils/output.cpp
3 
4  This file is part of Kleopatra, the KDE keymanager
5  Copyright (c) 2007 Klarälvdalens Datakonsult AB
6 
7  Kleopatra is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  Kleopatra is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21  In addition, as a special exception, the copyright holders give
22  permission to link the code of this program with any edition of
23  the Qt library by Trolltech AS, Norway (or with modified versions
24  of Qt that use the same license as Qt), and distribute linked
25  combinations including the two. You must obey the GNU General
26  Public License in all respects for all of the code used other than
27  Qt. If you modify this file, you may extend this exception to
28  your version of the file, but you are not obligated to do so. If
29  you do not wish to do so, delete this exception statement from
30  your version.
31 */
32 
33 #include <config-kleopatra.h>
34 
35 #include "output.h"
36 
37 #include "detail_p.h"
38 #include "kleo_assert.h"
39 #include "kdpipeiodevice.h"
40 #include "log.h"
41 #include "cached.h"
42 
43 #include <kleo/exception.h>
44 
45 #include <KLocale>
46 #include <KMessageBox>
47 #include <kdebug.h>
48 
49 #include <QFileInfo>
50 #include <QTemporaryFile>
51 #include <QString>
52 #include <QClipboard>
53 #include <QApplication>
54 #include <QBuffer>
55 #include <QPointer>
56 #include <QWidget>
57 #include <QDir>
58 #include <QProcess>
59 #include <QTimer>
60 
61 #ifdef Q_OS_WIN
62 # include <windows.h>
63 #endif
64 
65 #include <errno.h>
66 
67 using namespace Kleo;
68 using namespace Kleo::_detail;
69 using namespace boost;
70 
71 static const int PROCESS_MAX_RUNTIME_TIMEOUT = -1; // no timeout
72 static const int PROCESS_TERMINATE_TIMEOUT = 5*1000; // 5s
73 
74 class OverwritePolicy::Private {
75 public:
76  Private( QWidget* p, OverwritePolicy::Policy pol ) : policy( pol ), widget( p ) {}
77  OverwritePolicy::Policy policy;
78  QWidget* widget;
79 };
80 
81 OverwritePolicy::OverwritePolicy( QWidget * parent, Policy initialPolicy ) : d( new Private( parent, initialPolicy ) ) {
82 }
83 
84 OverwritePolicy::~OverwritePolicy() {}
85 
86 OverwritePolicy::Policy OverwritePolicy::policy() const {
87  return d->policy;
88 }
89 
90 void OverwritePolicy::setPolicy( Policy policy ) {
91  d->policy = policy;
92 }
93 
94 QWidget * OverwritePolicy::parentWidget() const {
95  return d->widget;
96 }
97 
98 namespace {
99 
100  class TemporaryFile : public QTemporaryFile {
101  public:
102  explicit TemporaryFile() : QTemporaryFile() {}
103  explicit TemporaryFile( const QString & templateName ) : QTemporaryFile( templateName ) {}
104  explicit TemporaryFile( QObject * parent ) : QTemporaryFile( parent ) {}
105  explicit TemporaryFile( const QString & templateName, QObject * parent ) : QTemporaryFile( templateName, parent ) {}
106 
107  /* reimp */ void close() {
108  if ( isOpen() )
109  m_oldFileName = fileName();
110  QTemporaryFile::close();
111  }
112 
113  bool openNonInheritable() {
114  if ( !QTemporaryFile::open() )
115  return false;
116 #if defined(Q_OS_WIN) && !defined(_WIN32_WCE)
117  //QTemporaryFile (tested with 4.3.3) creates the file handle as inheritable.
118  //The handle is then inherited by gpgsm, which prevents deletion of the temp file
119  //in FileOutput::doFinalize()
120  //There are no inheritable handles under wince
121  return SetHandleInformation( (HANDLE)_get_osfhandle( handle() ), HANDLE_FLAG_INHERIT, 0 );
122 #endif
123  return true;
124  }
125 
126  QString oldFileName() const { return m_oldFileName; }
127 
128  private:
129  QString m_oldFileName;
130  };
131 
132 
133  template <typename T_IODevice>
134  struct inhibit_close : T_IODevice {
135  explicit inhibit_close() : T_IODevice() {}
136  template <typename T1>
137  explicit inhibit_close( T1 & t1 ) : T_IODevice( t1 ) {}
138 
139  /* reimp */ void close() {}
140  void reallyClose() { T_IODevice::close(); }
141  };
142 
143  template <typename T_IODevice>
144  struct redirect_close : T_IODevice {
145  explicit redirect_close() : T_IODevice(), m_closed( false ) {}
146  template <typename T1>
147  explicit redirect_close( T1 & t1 ) : T_IODevice( t1 ), m_closed( false ) {}
148 
149  /* reimp */ void close() { this->closeWriteChannel(); m_closed = true; }
150 
151  bool isClosed() const { return m_closed; }
152  private:
153  bool m_closed;
154  };
155 
156 
157  class OutputImplBase : public Output {
158  public:
159  OutputImplBase()
160  : Output(),
161  m_defaultLabel(),
162  m_customLabel(),
163  m_errorString(),
164  m_isFinalized( false ),
165  m_isFinalizing( false ),
166  m_cancelPending( false ),
167  m_canceled( false ),
168  m_binaryOpt( false )
169  {
170 
171  }
172 
173  /* reimp */ QString label() const { return m_customLabel.isEmpty() ? m_defaultLabel : m_customLabel; }
174  /* reimp */ void setLabel( const QString & label ) { m_customLabel = label; }
175  void setDefaultLabel( const QString & l ) { m_defaultLabel = l; }
176  /* reimp */ void setBinaryOpt( bool value ) { m_binaryOpt = value; }
177  /* reimp */ bool binaryOpt() const { return m_binaryOpt; }
178 
179  /* reimp */ QString errorString() const {
180  if ( m_errorString.dirty() )
181  m_errorString = doErrorString();
182  return m_errorString;
183  }
184 
185  /* reimp */ bool isFinalized() const { return m_isFinalized; }
186  /* reimp */ void finalize() {
187  kDebug() << this;
188  if ( m_isFinalized || m_isFinalizing )
189  return;
190  m_isFinalizing = true;
191  try { doFinalize(); } catch ( ... ) { m_isFinalizing = false; throw; }
192  m_isFinalizing = false;
193  m_isFinalized = true;
194  if ( m_cancelPending )
195  cancel();
196  }
197 
198  /* reimp */ void cancel() {
199  kDebug() << this;
200  if ( m_isFinalizing ) {
201  m_cancelPending = true;
202  } else if ( !m_canceled ) {
203  m_isFinalizing = true;
204  try { doCancel(); } catch ( ... ) {}
205  m_isFinalizing = false;
206  m_isFinalized = true;
207  m_canceled = true;
208  }
209  }
210  private:
211  virtual QString doErrorString() const {
212  if ( shared_ptr<QIODevice> io = ioDevice() )
213  return io->errorString();
214  else
215  return i18n("No output device");
216  }
217  virtual void doFinalize() = 0;
218  virtual void doCancel() = 0;
219  private:
220  QString m_defaultLabel;
221  QString m_customLabel;
222  mutable cached<QString> m_errorString;
223  bool m_isFinalized : 1;
224  bool m_isFinalizing : 1;
225  bool m_cancelPending : 1;
226  bool m_canceled : 1;
227  bool m_binaryOpt : 1;
228  };
229 
230 
231  class PipeOutput : public OutputImplBase {
232  public:
233  explicit PipeOutput( assuan_fd_t fd );
234 
235  /* reimp */ shared_ptr<QIODevice> ioDevice() const { return m_io; }
236  /* reimp */ void doFinalize() { m_io->reallyClose(); }
237  /* reimp */ void doCancel() { doFinalize(); }
238  private:
239  shared_ptr< inhibit_close<KDPipeIODevice> > m_io;
240  };
241 
242 
243  class ProcessStdInOutput : public OutputImplBase {
244  public:
245  explicit ProcessStdInOutput( const QString & cmd, const QStringList & args, const QDir & wd );
246 
247  /* reimp */ shared_ptr<QIODevice> ioDevice() const { return m_proc; }
248  /* reimp */ void doFinalize() {
249  /*
250  Make sure the data is written in the output here. If this
251  is not done the output will be written in small chunks
252  trough the eventloop causing an uncessary delay before
253  the process has even a chance to work and finish.
254  This delay is mainly noticabe on Windows where it can
255  take ~30 seconds to write out a 10MB file in the 512 byte
256  chunks gpgme serves. */
257  kDebug(5151) << "Waiting for " << m_proc->bytesToWrite()
258  << " Bytes to be written";
259  while ( m_proc->waitForBytesWritten( PROCESS_MAX_RUNTIME_TIMEOUT ) );
260 
261  if ( !m_proc->isClosed() ) {
262  m_proc->close();
263  }
264  m_proc->waitForFinished( PROCESS_MAX_RUNTIME_TIMEOUT );
265  }
266  /* reimp */ void doCancel() {
267  m_proc->terminate();
268  QTimer::singleShot( PROCESS_TERMINATE_TIMEOUT, m_proc.get(), SLOT(kill()) );
269  }
270  /* reimp */ QString label() const;
271 
272  private:
273  /* reimp */ QString doErrorString() const;
274 
275  private:
276  const QString m_command;
277  const QStringList m_arguments;
278  const shared_ptr< redirect_close<QProcess> > m_proc;
279  };
280 
281 
282  class FileOutput : public OutputImplBase {
283  public:
284  explicit FileOutput( const QString & fileName, const shared_ptr<OverwritePolicy> & policy );
285  ~FileOutput() { kDebug() << this; }
286 
287  /* reimp */ QString label() const { return QFileInfo( m_fileName ).fileName(); }
288  /* reimp */ shared_ptr<QIODevice> ioDevice() const { return m_tmpFile; }
289  /* reimp */ void doFinalize();
290  /* reimp */ void doCancel() { kDebug() << this; }
291  private:
292  bool obtainOverwritePermission();
293 
294  private:
295  const QString m_fileName;
296  shared_ptr< TemporaryFile > m_tmpFile;
297  const shared_ptr<OverwritePolicy> m_policy;
298  };
299 
300 #ifndef QT_NO_CLIPBOARD
301  class ClipboardOutput : public OutputImplBase {
302  public:
303  explicit ClipboardOutput( QClipboard::Mode mode );
304 
305  /* reimp */ QString label() const;
306  /* reimp */ shared_ptr<QIODevice> ioDevice() const { return m_buffer; }
307  /* reimp */ void doFinalize();
308  /* reimp */ void doCancel() {}
309 
310  private:
311  /* reimp */ QString doErrorString() const { return QString(); }
312  private:
313  const QClipboard::Mode m_mode;
314  shared_ptr<QBuffer> m_buffer;
315  };
316 #endif // QT_NO_CLIPBOARD
317 
318 }
319 
320 
321 shared_ptr<Output> Output::createFromPipeDevice( assuan_fd_t fd, const QString & label ) {
322  shared_ptr<PipeOutput> po( new PipeOutput( fd ) );
323  po->setDefaultLabel( label );
324  return po;
325 }
326 
327 PipeOutput::PipeOutput( assuan_fd_t fd )
328  : OutputImplBase(),
329  m_io( new inhibit_close<KDPipeIODevice> )
330 {
331  errno = 0;
332  if ( !m_io->open( fd, QIODevice::WriteOnly ) )
333  throw Exception( errno ? gpg_error_from_errno( errno ) : gpg_error( GPG_ERR_EIO ),
334  i18n( "Could not open FD %1 for writing",
335  assuanFD2int( fd ) ) );
336 }
337 
338 
339 shared_ptr<Output> Output::createFromFile( const QString & fileName, bool forceOverwrite ) {
340  return createFromFile( fileName, shared_ptr<OverwritePolicy>( new OverwritePolicy( 0, forceOverwrite ? OverwritePolicy::Allow : OverwritePolicy::Deny ) ) );
341 
342 }
343 shared_ptr<Output> Output::createFromFile( const QString & fileName, const shared_ptr<OverwritePolicy> & policy ) {
344  shared_ptr<FileOutput> fo( new FileOutput( fileName, policy ) );
345  kDebug() << fo.get();
346  return fo;
347 }
348 
349 FileOutput::FileOutput( const QString & fileName, const shared_ptr<OverwritePolicy> & policy )
350  : OutputImplBase(),
351  m_fileName( fileName ),
352  m_tmpFile( new TemporaryFile( fileName ) ),
353  m_policy( policy )
354 {
355  assert( m_policy );
356  errno = 0;
357  if ( !m_tmpFile->openNonInheritable() )
358  throw Exception( errno ? gpg_error_from_errno( errno ) : gpg_error( GPG_ERR_EIO ),
359  i18n( "Could not create temporary file for output \"%1\"", fileName ) );
360 }
361 
362 bool FileOutput::obtainOverwritePermission() {
363  if ( m_policy->policy() != OverwritePolicy::Ask )
364  return m_policy->policy() == OverwritePolicy::Allow;
365  const int sel = KMessageBox::questionYesNoCancel( m_policy->parentWidget(), i18n("The file <b>%1</b> already exists.\n"
366  "Overwrite?", m_fileName ),
367  i18n("Overwrite Existing File?"),
368  KStandardGuiItem::overwrite(),
369  KGuiItem( i18n( "Overwrite All" ) ),
370  KStandardGuiItem::cancel() );
371  if ( sel == KMessageBox::No ) //Overwrite All
372  m_policy->setPolicy( OverwritePolicy::Allow );
373  return sel == KMessageBox::Yes || sel == KMessageBox::No;
374 }
375 
376 void FileOutput::doFinalize() {
377  kDebug() << this;
378 
379  struct Remover {
380  QString file;
381  ~Remover() { if ( QFile::exists( file ) ) QFile::remove( file ); }
382  } remover;
383 
384  kleo_assert( m_tmpFile );
385 
386  if ( m_tmpFile->isOpen() )
387  m_tmpFile->close();
388 
389  const QString tmpFileName = remover.file = m_tmpFile->oldFileName();
390 
391  m_tmpFile->setAutoRemove( false );
392  QPointer<QObject> guard = m_tmpFile.get();
393  m_tmpFile.reset(); // really close the file - needed on Windows for renaming :/
394  kleo_assert( !guard ); // if this triggers, we need to audit for holder of shared_ptr<QIODevice>s.
395 
396  kDebug() << this << " renaming " << tmpFileName << "->" << m_fileName ;
397 
398  if ( QFile::rename( tmpFileName, m_fileName ) ) {
399  kDebug() << this << "succeeded";
400  return;
401  }
402 
403  kDebug() << this << "failed";
404 
405  if ( !obtainOverwritePermission() )
406  throw Exception( gpg_error( GPG_ERR_CANCELED ),
407  i18n( "Overwriting declined" ) );
408 
409  kDebug() << this << "going to overwrite" << m_fileName ;
410 
411  if ( !QFile::remove( m_fileName ) )
412  throw Exception( errno ? gpg_error_from_errno( errno ) : gpg_error( GPG_ERR_EIO ),
413  i18n("Could not remove file \"%1\" for overwriting.", m_fileName ) );
414 
415  kDebug() << this << "succeeded, renaming " << tmpFileName << "->" << m_fileName;
416 
417  if ( QFile::rename( tmpFileName, m_fileName ) ) {
418  kDebug() << this << "succeeded";
419  return;
420  }
421 
422  kDebug() << this << "failed";
423 
424  throw Exception( errno ? gpg_error_from_errno( errno ) : gpg_error( GPG_ERR_EIO ),
425  i18n( "Could not rename file \"%1\" to \"%2\"",
426  tmpFileName, m_fileName ) );
427 }
428 
429 
430 shared_ptr<Output> Output::createFromProcessStdIn( const QString & command ) {
431  return shared_ptr<Output>( new ProcessStdInOutput( command, QStringList(), QDir::current() ) );
432 }
433 
434 shared_ptr<Output> Output::createFromProcessStdIn( const QString & command, const QStringList & args ) {
435  return shared_ptr<Output>( new ProcessStdInOutput( command, args, QDir::current() ) );
436 }
437 
438 shared_ptr<Output> Output::createFromProcessStdIn( const QString & command, const QStringList & args, const QDir & wd ) {
439  return shared_ptr<Output>( new ProcessStdInOutput( command, args, wd ) );
440 }
441 
442 ProcessStdInOutput::ProcessStdInOutput( const QString & cmd, const QStringList & args, const QDir & wd )
443  : OutputImplBase(),
444  m_command( cmd ),
445  m_arguments( args ),
446  m_proc( new redirect_close<QProcess> )
447 {
448  kDebug() << "cd" << wd.absolutePath() << endl << cmd << args;
449  if ( cmd.isEmpty() )
450  throw Exception( gpg_error( GPG_ERR_INV_ARG ),
451  i18n("Command not specified") );
452  m_proc->setWorkingDirectory( wd.absolutePath() );
453  m_proc->start( cmd, args );
454  m_proc->setReadChannel( QProcess::StandardError );
455  if ( !m_proc->waitForStarted() )
456  throw Exception( gpg_error( GPG_ERR_EIO ),
457  i18n( "Could not start %1 process: %2", cmd, m_proc->errorString() ) );
458 }
459 
460 QString ProcessStdInOutput::label() const {
461  if ( !m_proc )
462  return OutputImplBase::label();
463  // output max. 3 arguments
464  const QString cmdline = ( QStringList( m_command ) + m_arguments.mid(0,3) ).join( QLatin1String(" ") );
465  if ( m_arguments.size() > 3 )
466  return i18nc( "e.g. \"Input to tar xf - file1 ...\"", "Input to %1 ...", cmdline );
467  else
468  return i18nc( "e.g. \"Input to tar xf - file\"", "Input to %1", cmdline );
469 }
470 
471 QString ProcessStdInOutput::doErrorString() const {
472  kleo_assert( m_proc );
473  if ( m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0 )
474  return QString();
475  if ( m_proc->error() == QProcess::UnknownError )
476  return i18n( "Error while running %1: %2", m_command,
477  QString::fromLocal8Bit( m_proc->readAllStandardError().trimmed().constData() ) );
478  else
479  return i18n( "Failed to execute %1: %2", m_command, m_proc->errorString() );
480 }
481 
482 #ifndef QT_NO_CLIPBOARD
483 shared_ptr<Output> Output::createFromClipboard() {
484  return shared_ptr<Output>( new ClipboardOutput( QClipboard::Clipboard ) );
485 }
486 
487 ClipboardOutput::ClipboardOutput( QClipboard::Mode mode )
488  : OutputImplBase(),
489  m_mode( mode ),
490  m_buffer( new QBuffer )
491 {
492  errno = 0;
493  if ( !m_buffer->open( QIODevice::WriteOnly ) )
494  throw Exception( errno ? gpg_error_from_errno( errno ) : gpg_error( GPG_ERR_EIO ),
495  i18n( "Could not write to clipboard" ) );
496 }
497 
498 QString ClipboardOutput::label() const {
499  switch ( m_mode ) {
500  case QClipboard::Clipboard:
501  return i18n( "Clipboard" );
502  case QClipboard::FindBuffer:
503  return i18n( "Find buffer" );
504  case QClipboard::Selection:
505  return i18n( "Selection" );
506  }
507  return QString();
508 }
509 
510 void ClipboardOutput::doFinalize() {
511  if ( m_buffer->isOpen() )
512  m_buffer->close();
513  if ( QClipboard * const cb = QApplication::clipboard() )
514  cb->setText( QString::fromUtf8( m_buffer->data() ) );
515  else
516  throw Exception( gpg_error( GPG_ERR_EIO ),
517  i18n( "Could not find clipboard" ) );
518 }
519 #endif // QT_NO_CLIPBOARD
520 
521 
522 
523 Output::~Output() {}
output.h
Kleo::OverwritePolicy::Policy
Policy
Definition: output.h:52
QWidget
Kleo::OverwritePolicy
Definition: output.h:50
Kleo::Output::createFromClipboard
static boost::shared_ptr< Output > createFromClipboard()
Definition: output.cpp:483
kleo_assert.h
boost::shared_ptr< QIODevice >
d
#define d
Definition: adduseridcommand.cpp:90
cached.h
Kleo::Output::createFromPipeDevice
static boost::shared_ptr< Output > createFromPipeDevice(assuan_fd_t fd, const QString &label)
Definition: output.cpp:321
KDPipeIODevice
Definition: kdpipeiodevice.h:29
Kleo::Output
Definition: output.h:71
Kleo::Output::~Output
virtual ~Output()
Definition: output.cpp:523
Kleo::_detail::assuanFD2int
static qulonglong assuanFD2int(assuan_fd_t fd)
Definition: detail_p.h:101
Kleo::OverwritePolicy::OverwritePolicy
OverwritePolicy(QWidget *parent, Policy initialPolicy=Ask)
Definition: output.cpp:81
Kleo::OverwritePolicy::setPolicy
void setPolicy(Policy)
Definition: output.cpp:90
Kleo::OverwritePolicy::~OverwritePolicy
~OverwritePolicy()
Definition: output.cpp:84
Kleo::Output::createFromFile
static boost::shared_ptr< Output > createFromFile(const QString &fileName, const boost::shared_ptr< OverwritePolicy > &)
Definition: output.cpp:343
Kleo::OverwritePolicy::policy
Policy policy() const
Definition: output.cpp:86
kleo_assert
#define kleo_assert(cond)
Definition: kleo_assert.h:84
Kleo::cached
Definition: cached.h:41
Kleo::OverwritePolicy::Deny
Definition: output.h:54
Kleo::Output::createFromProcessStdIn
static boost::shared_ptr< Output > createFromProcessStdIn(const QString &command)
Definition: output.cpp:430
Kleo::OverwritePolicy::Ask
Definition: output.h:55
Kleo::OverwritePolicy::parentWidget
QWidget * parentWidget() const
Definition: output.cpp:94
kdpipeiodevice.h
detail_p.h
Kleo::OverwritePolicy::Allow
Definition: output.h:53
assuan_fd_t
int assuan_fd_t
Definition: kleo-assuan.h:72
PROCESS_MAX_RUNTIME_TIMEOUT
static const int PROCESS_MAX_RUNTIME_TIMEOUT
Definition: output.cpp:71
log.h
PROCESS_TERMINATE_TIMEOUT
static const int PROCESS_TERMINATE_TIMEOUT
Definition: output.cpp:72
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:56:41 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kleopatra

Skip menu "kleopatra"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal