33 #include <config-kleopatra.h> 
   43 #include <kleo/exception.h> 
   46 #include <KMessageBox> 
   50 #include <QTemporaryFile> 
   53 #include <QApplication> 
   68 using namespace Kleo::_detail;
 
   69 using namespace boost;
 
   74 class OverwritePolicy::Private {
 
  100     class TemporaryFile : 
public QTemporaryFile {
 
  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 ) {}
 
  109                 m_oldFileName = fileName();
 
  110             QTemporaryFile::close();
 
  113         bool openNonInheritable() {
 
  114             if ( !QTemporaryFile::open() )
 
  116 #if defined(Q_OS_WIN) && !defined(_WIN32_WCE) 
  121             return SetHandleInformation( (HANDLE)_get_osfhandle( handle() ), HANDLE_FLAG_INHERIT, 0 );
 
  126         QString oldFileName()
 const { 
return m_oldFileName; }
 
  129         QString m_oldFileName;
 
  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 ) {}
 
  140         void reallyClose() { T_IODevice::close(); }
 
  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 ) {}
 
  149          void close() { this->closeWriteChannel(); m_closed = 
true; }
 
  151         bool isClosed()
 const { 
return m_closed; }
 
  157     class OutputImplBase : 
public Output {
 
  164               m_isFinalized( false ),
 
  165               m_isFinalizing( false ),
 
  166               m_cancelPending( false ),
 
  173          QString label()
 const { 
return m_customLabel.isEmpty() ? m_defaultLabel : m_customLabel; }
 
  174          void setLabel( 
const QString & label ) { m_customLabel = label; }
 
  175         void setDefaultLabel( 
const QString & l ) { m_defaultLabel = l; }
 
  176          void setBinaryOpt( 
bool value ) { m_binaryOpt = value; }
 
  177          bool binaryOpt()
 const { 
return m_binaryOpt; }
 
  179          QString errorString()
 const {
 
  180             if ( m_errorString.dirty() )
 
  181                 m_errorString = doErrorString();
 
  182             return m_errorString;
 
  185          bool isFinalized()
 const { 
return m_isFinalized; }
 
  188             if ( m_isFinalized || m_isFinalizing )
 
  190             m_isFinalizing = 
true;
 
  191             try { doFinalize(); } 
catch ( ... ) { m_isFinalizing = 
false; 
throw; }
 
  192             m_isFinalizing = 
false;
 
  193             m_isFinalized = 
true;
 
  194             if ( m_cancelPending )
 
  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;
 
  211         virtual QString doErrorString()
 const {
 
  213                 return io->errorString();
 
  215                 return i18n(
"No output device");
 
  217         virtual void doFinalize() = 0;
 
  218         virtual void doCancel() = 0;
 
  220         QString m_defaultLabel;
 
  221         QString m_customLabel;
 
  223         bool m_isFinalized   : 1;
 
  224         bool m_isFinalizing  : 1;
 
  225         bool m_cancelPending : 1;
 
  227         bool m_binaryOpt     : 1;
 
  231     class PipeOutput : 
public OutputImplBase {
 
  236          void doFinalize() { m_io->reallyClose(); }
 
  237          void doCancel() { doFinalize(); }
 
  243     class ProcessStdInOutput : 
public OutputImplBase {
 
  245         explicit ProcessStdInOutput( 
const QString & cmd, 
const QStringList & args, 
const QDir & wd );
 
  257             kDebug(5151) << 
"Waiting for " << m_proc->bytesToWrite()
 
  258                          << 
" Bytes to be written";
 
  261             if ( !m_proc->isClosed() ) {
 
  270          QString label() 
const;
 
  273          QString doErrorString() 
const;
 
  276         const QString m_command;
 
  277         const QStringList m_arguments;
 
  282     class FileOutput : 
public OutputImplBase {
 
  285         ~FileOutput() { kDebug() << 
this; }
 
  287          QString label()
 const { 
return QFileInfo( m_fileName ).fileName(); }
 
  290          void doCancel() { kDebug() << 
this; }
 
  292         bool obtainOverwritePermission();
 
  295         const QString m_fileName;
 
  300 #ifndef QT_NO_CLIPBOARD 
  301     class ClipboardOutput : 
public OutputImplBase {
 
  303         explicit ClipboardOutput( QClipboard::Mode mode );
 
  305          QString label() 
const;
 
  311          QString doErrorString()
 const { 
return QString(); }
 
  313         const QClipboard::Mode m_mode;
 
  316 #endif // QT_NO_CLIPBOARD 
  323     po->setDefaultLabel( label );
 
  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",
 
  345     kDebug() << fo.get();
 
  351       m_fileName( fileName ),
 
  352       m_tmpFile( new TemporaryFile( fileName ) ),
 
  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 ) );
 
  362 bool FileOutput::obtainOverwritePermission() {
 
  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 ) 
 
  373     return sel == KMessageBox::Yes || sel == KMessageBox::No;
 
  376 void FileOutput::doFinalize() {
 
  381         ~Remover() { 
if ( QFile::exists( file ) ) QFile::remove( file ); }
 
  386     if ( m_tmpFile->isOpen() )
 
  389     const QString tmpFileName = remover.file = m_tmpFile->oldFileName();
 
  391     m_tmpFile->setAutoRemove( 
false );
 
  392     QPointer<QObject> guard = m_tmpFile.get();
 
  396     kDebug() << 
this << 
" renaming " << tmpFileName << 
"->" << m_fileName ;
 
  398     if ( QFile::rename( tmpFileName, m_fileName ) ) {
 
  399         kDebug() << 
this << 
"succeeded";
 
  403     kDebug() << 
this << 
"failed";
 
  405     if ( !obtainOverwritePermission() )
 
  406         throw Exception( gpg_error( GPG_ERR_CANCELED ),
 
  407                          i18n( 
"Overwriting declined" ) );
 
  409     kDebug() << 
this << 
"going to overwrite" << m_fileName ;
 
  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 ) );
 
  415     kDebug() << 
this << 
"succeeded, renaming " << tmpFileName << 
"->" << m_fileName;
 
  417     if ( QFile::rename( tmpFileName, m_fileName ) ) {
 
  418         kDebug() << 
this << 
"succeeded";
 
  422     kDebug() << 
this << 
"failed";
 
  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 ) );
 
  431     return shared_ptr<Output>( 
new ProcessStdInOutput( command, QStringList(), QDir::current() ) );
 
  435     return shared_ptr<Output>( 
new ProcessStdInOutput( command, args, QDir::current() ) );
 
  442 ProcessStdInOutput::ProcessStdInOutput( 
const QString & cmd, 
const QStringList & args, 
const QDir & wd )
 
  446       m_proc( new redirect_close<QProcess> )
 
  448     kDebug() << 
"cd" << wd.absolutePath() << endl << cmd << args;
 
  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() ) );
 
  460 QString ProcessStdInOutput::label()
 const {
 
  462         return OutputImplBase::label();
 
  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 );
 
  468         return i18nc( 
"e.g. \"Input to tar xf - file\"",      
"Input to %1",     cmdline );
 
  471 QString ProcessStdInOutput::doErrorString()
 const {
 
  473     if ( m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0 )
 
  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() ) );
 
  479         return i18n( 
"Failed to execute %1: %2", m_command, m_proc->errorString() );
 
  482 #ifndef QT_NO_CLIPBOARD 
  487 ClipboardOutput::ClipboardOutput( QClipboard::Mode mode )
 
  490       m_buffer( new QBuffer )
 
  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" ) );
 
  498 QString ClipboardOutput::label()
 const {
 
  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" );
 
  510 void ClipboardOutput::doFinalize() {
 
  511     if ( m_buffer->isOpen() )
 
  513     if ( QClipboard * 
const cb = QApplication::clipboard() )
 
  514         cb->setText( QString::fromUtf8( m_buffer->data() ) );
 
  516         throw Exception( gpg_error( GPG_ERR_EIO ),
 
  517                          i18n( 
"Could not find clipboard" ) );
 
  519 #endif // QT_NO_CLIPBOARD 
static boost::shared_ptr< Output > createFromClipboard()
 
static boost::shared_ptr< Output > createFromPipeDevice(assuan_fd_t fd, const QString &label)
 
static qulonglong assuanFD2int(assuan_fd_t fd)
 
OverwritePolicy(QWidget *parent, Policy initialPolicy=Ask)
 
static boost::shared_ptr< Output > createFromFile(const QString &fileName, const boost::shared_ptr< OverwritePolicy > &)
 
#define kleo_assert(cond)
 
static boost::shared_ptr< Output > createFromProcessStdIn(const QString &command)
 
QWidget * parentWidget() const 
 
static const int PROCESS_MAX_RUNTIME_TIMEOUT
 
static const int PROCESS_TERMINATE_TIMEOUT