Page MenuHome GnuPG

No OneTemporary

diff --git a/uiserver/assuancommand.h b/uiserver/assuancommand.h
index 8033cc029..b96e9b6d5 100644
--- a/uiserver/assuancommand.h
+++ b/uiserver/assuancommand.h
@@ -1,352 +1,356 @@
/* -*- mode: c++; c-basic-offset:4 -*-
uiserver/assuancommand.h
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 Klarälvdalens Datakonsult AB
Kleopatra is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Kleopatra is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#ifndef __KLEOPATRA_UISERVER_ASSUANCOMMAND_H__
#define __KLEOPATRA_UISERVER_ASSUANCOMMAND_H__
#include <crypto/controller.h>
#include <utils/pimpl_ptr.h>
#include <gpgme++/global.h>
#include <gpgme++/error.h>
#include <kmime/kmime_header_parsing.h>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <string>
#include <map>
#include <vector>
class QVariant;
class QIODevice;
class QObject;
class QStringList;
class QDialog;
class QFile;
struct assuan_context_s;
namespace Kleo {
class Input;
class Output;
class AssuanCommandFactory;
/*!
\brief Base class for GnuPG UI Server commands
\note large parts of this are outdated by now!
<h3>Implementing a new AssuanCommand</h3>
You do not directly inherit AssuanCommand, unless you want to
deal with implementing low-level, repetetive things like name()
in terms of staticName(). Assuming you don't, then you inherit
your command class from AssuanCommandMixin, passing your class
as the template argument to AssuanCommandMixin, like this:
\code
class MyFooCommand : public AssuanCommandMixin<MyFooCommand> {
\endcode
(http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
You then choose a command name, and return that from the static
method staticName(), which is by convention queried by both
AssuanCommandMixin<> and GenericAssuanCommandFactory<>:
\code
static const char * staticName() { return "MYFOO"; }
\endcode
The string should be all-uppercase by convention, but the
UiServer implementation doesn't enforce this.
The next step is to implement start(), the starting point of
command execution:
<h3>Executing the command</h3>
\code
int start( const std::string & line ) {
\endcode
This should set everything up and check the parameters in \a
line and any options this command understands. If there's an
error, choose one the the gpg-error codes and create a
gpg_error_t from it using the protected makeError() function:
\code
return makeError( GPG_ERR_NOT_IMPLEMENTED );
\endcode
-
+
But usually, you will want to create a dialog, or call some
GpgME function from here. In case of errors from GpgME, you
shouldn't pipe them through makeError(), but return them
as-is. This will preserve the error source. Error created using
makeError() will have Kleopatra as their error source, so watch
out what you're doing :)
In addition to options and the command line, your command might
require \em{bulk data} input or output. That's what the bulk
input and output channels are for. You can check whether the
client handed you an input channel by checking that
bulkInputDevice() isn't NULL, likewise for bulkOutputDevice().
If everything is ok, you return 0. This indicates to the client
that the command has been accepted and is now in progress.
In this mode (start() returned 0), there are a bunch of options
for your command to do. Some commands may require additional
information from the client. The options passed to start() are
designed to be persistent across commands, and rather limited in
length (there's a strict line length limit in the assuan
protocol with no line continuation mechanism). The same is true
for command line arguments, which, in addition, you have to
parse yourself. Those usually apply only to this command, and
not to following ones.
If you need data that might be larger than the line length
limit, you can either expect it on the bulkInputDevice(), or, if
you have the need for more than one such data channel, or the
data is optional or conditional on some condition that can only
be determined during command execution, you can \em inquire the
missing information from the client.
As an example, a VERIFY command would expect the signed data on
the bulkInputDevice(). But if the input stream doesn't contain
an embedded (opaque) signature, indicating a \em detached
signature, it would go and inquire that data from the
client. Here's how it works:
\code
const int err = inquire( "DETACHED_SIGNATURE",
this, SLOT(slotDetachedSignature(int,QByteArray,QByteArray)) );
if ( err )
done( err );
\endcode
This should be self-explanatory: You give a slot to call when
the data has arrived. The slot's first argument is an error
code. The second the data (if any), and the third is just
repeating what you gave as inquire()'s first argument. As usual,
you can leave argument off of the end, if you are not interested
in them.
You can do as many inquiries as you want, but only one at a
time.
You should peridocally send status updates to the client. You do
that by calling sendStatus().
Once your command has finished executing, call done(). If it's
with an error code, call done(err) like above. \bold{Do not
forget to call done() when done!}. It will close
bulkInputDevice(), bulkOutputDevice(), and send an OK or ERR
message back to the client.
At that point, your command has finished executing, and a new
one can be accepted, or the connection closed.
Apropos connection closed. The only way for the client to cancel
an operation is to shut down the connection. In this case, the
canceled() function will be called. At that point, the
connection to the client will have been broken already, and all
you can do is pack your things and go down gracefully.
If _you_ detect that the user has canceled (your dialog contains
a cancel button, doesn't it?), then you should instead call
done( GPG_ERR_CANCELED ), like for normal operation.
<h3>Registering the command with UiServer</h3>
To register a command, you implement a AssuanCommandFactory for
your AssuanCommand subclass, and register it with the
UiServer. This can be made considerably easier using
GenericAssuanCommandFactory:
\code
UiServer server;
server.registerCommandFactory( shared_ptr<AssuanCommandFactory>( new GenericAssuanCommandFactory<MyFooCommand> ) );
// more registerCommandFactory calls...
server.start();
\endcode
*/
class AssuanCommand : public Crypto::ExecutionContext, public boost::enable_shared_from_this<AssuanCommand> {
// defined in assuanserverconnection.cpp!
public:
AssuanCommand();
virtual ~AssuanCommand();
int start();
void canceled();
virtual const char * name() const = 0;
class Memento {
public:
virtual ~Memento() {}
};
template <typename T>
class TypedMemento : public Memento {
T m_t;
public:
explicit TypedMemento( const T & t ) : m_t( t ) {}
const T & get() const { return m_t; }
T & get() { return m_t; }
};
template <typename T>
static boost::shared_ptr< TypedMemento<T> > make_typed_memento( const T & t ) {
return boost::shared_ptr< TypedMemento<T> >( new TypedMemento<T>( t ) );
}
static int makeError( int code );
// convenience methods:
enum Mode { NoMode, EMail, FileManager };
Mode checkMode() const;
- GpgME::Protocol checkProtocol( Mode mode ) const;
+ enum CheckProtocolOption {
+ AllowProtocolMissing=0x01
+ };
+
+ GpgME::Protocol checkProtocol( Mode mode, int options=0 ) const;
/* reimp */ void applyWindowID( QWidget* w ) const {
doApplyWindowID( w );
}
-
+
QString heuristicBaseDirectory() const;
void setNohup( bool on );
bool isNohup() const;
bool isDone() const;
QString sessionTitle() const;
bool informativeRecipients() const;
bool informativeSenders() const;
const std::vector<KMime::Types::Mailbox> & recipients() const;
const std::vector<KMime::Types::Mailbox> & senders() const;
bool hasMemento( const QByteArray & tag ) const;
boost::shared_ptr<Memento> memento( const QByteArray & tag ) const;
template <typename T>
boost::shared_ptr<T> mementoAs( const QByteArray & tag ) const {
return boost::dynamic_pointer_cast<T>( this->memento( tag ) );
}
const std::map< QByteArray, boost::shared_ptr<Memento> > & mementos() const;
QByteArray registerMemento( const boost::shared_ptr<Memento> & mem );
QByteArray registerMemento( const QByteArray & tag, const boost::shared_ptr<Memento> & mem );
void removeMemento( const QByteArray & tag );
template <typename T>
T mementoContent( const QByteArray & tag ) const {
if ( boost::shared_ptr< TypedMemento<T> > m = mementoAs< TypedMemento<T> >( tag ) )
return m->get();
else
return T();
}
bool hasOption( const char * opt ) const;
QVariant option( const char * opt ) const;
const std::map<std::string,QVariant> & options() const;
const std::vector< boost::shared_ptr<Input> > & inputs() const;
const std::vector< boost::shared_ptr<Input> > & messages() const;
const std::vector< boost::shared_ptr<Output> > & outputs() const;
QStringList fileNames() const;
std::vector< boost::shared_ptr<QFile> > files() const;
unsigned int numFiles() const;
void sendStatus( const char * keyword, const QString & text );
void sendStatusEncoded( const char * keyword, const std::string & text );
void sendData( const QByteArray & data, bool moreToCome=false );
int inquire( const char * keyword, QObject * receiver, const char * slot, unsigned int maxSize=0 );
void done( const GpgME::Error& err = GpgME::Error() );
void done( const GpgME::Error& err, const QString & details );
void done( int err ) { done( GpgME::Error(err) ); }
void done( int err, const QString & details ) { done( GpgME::Error(err), details ); }
private:
virtual void doCanceled() = 0;
virtual int doStart() = 0;
private:
void doApplyWindowID( QWidget * w ) const;
private:
friend class ::Kleo::AssuanCommandFactory;
class Private;
kdtools::pimpl_ptr<Private> d;
};
class AssuanCommandFactory {
public:
virtual ~AssuanCommandFactory() {}
virtual boost::shared_ptr<AssuanCommand> create() const = 0;
virtual const char * name() const = 0;
typedef int(*_Handler)( assuan_context_s*, char *);
virtual _Handler _handler() const = 0;
protected:
static int _handle( assuan_context_s*, char *, const char * );
};
template <typename Command>
class GenericAssuanCommandFactory : public AssuanCommandFactory {
/* reimp */ AssuanCommandFactory::_Handler _handler() const { return &GenericAssuanCommandFactory::_handle; }
static int _handle( assuan_context_s* _ctx, char * _line ) {
return AssuanCommandFactory::_handle( _ctx, _line, Command::staticName() );
}
/* reimp */ boost::shared_ptr<AssuanCommand> create() const { return make(); }
/* reimp */ const char * name() const { return Command::staticName(); }
public:
static boost::shared_ptr<Command> make() { return boost::shared_ptr<Command>( new Command ); }
};
template <typename Derived, typename Base=AssuanCommand>
class AssuanCommandMixin : public Base {
protected:
/* reimp */ const char * name() const { return Derived::staticName(); }
};
-
+
}
#endif /* __KLEOPATRA_UISERVER_ASSUANCOMMAND_H__ */
diff --git a/uiserver/assuanserverconnection.cpp b/uiserver/assuanserverconnection.cpp
index ac6cd4836..55b9fd82e 100644
--- a/uiserver/assuanserverconnection.cpp
+++ b/uiserver/assuanserverconnection.cpp
@@ -1,1423 +1,1423 @@
/* -*- mode: c++; c-basic-offset:4 -*-
uiserver/assuanserverconnection.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 Klarälvdalens Datakonsult AB
Kleopatra is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Kleopatra is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#ifndef QT_NO_CAST_TO_ASCII
# define QT_NO_CAST_TO_ASCII
#endif
#ifndef QT_NO_CAST_FROM_ASCII
# define QT_NO_CAST_FROM_ASCII
#endif
#include <config-kleopatra.h>
#include <version-kleopatra.h>
#include "assuanserverconnection.h"
#include "assuancommand.h"
#include <models/keycache.h> // :(
#include <utils/input.h>
#include <utils/output.h>
#include <utils/gnupg-helper.h>
#include <utils/detail_p.h>
#include <utils/hex.h>
#include <utils/log.h>
#include <utils/exception.h>
#include <utils/kleo_assert.h>
#include <utils/getpid.h>
#include <utils/stl_util.h>
#include <gpgme++/data.h>
#include <gpgme++/key.h>
#include <kmime/kmime_header_parsing.h>
#include <KLocalizedString>
#include <KWindowSystem>
#include <QSocketNotifier>
#include <QTimer>
#include <QVariant>
#include <QPointer>
#include <QFileInfo>
#include <QDebug>
#include <QStringList>
#include <QDialog>
#include <QRegExp>
#include <kleo-assuan.h>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <boost/mem_fn.hpp>
#include <boost/mpl/if.hpp>
#include <vector>
#include <map>
#include <string>
#include <memory>
#include <algorithm>
#include <errno.h>
#ifdef __GNUC__
# include <ext/algorithm> // for is_sorted
#endif
#ifdef Q_OS_WIN32
# include <io.h>
# include <process.h>
#else
# include <sys/types.h>
# include <unistd.h>
#endif
#ifdef Q_WS_X11
# include <qx11info_x11.h>
# include <X11/Xlib.h>
#endif
using namespace Kleo;
using namespace boost;
namespace {
struct IOF {
QString fileName;
shared_ptr<QFile> file;
};
static void close_all( const std::vector<IOF> & ios ) {
Q_FOREACH( const IOF & io, ios )
if ( io.file && io.file->isOpen() )
io.file->close();
}
}
static const unsigned int INIT_SOCKET_FLAGS = 3; // says info assuan...
//static int(*USE_DEFAULT_HANDLER)(assuan_context_t,char*) = 0;
static const int FOR_READING = 0;
static const unsigned int MAX_ACTIVE_FDS = 32;
// shared_ptr for assuan_context_t w/ deleter enforced to assuan_deinit_server:
typedef shared_ptr< remove_pointer<assuan_context_t>::type > AssuanContextBase;
struct AssuanContext : AssuanContextBase {
AssuanContext() : AssuanContextBase() {}
explicit AssuanContext( assuan_context_t ctx ) : AssuanContextBase( ctx, &assuan_deinit_server ) {}
void reset( assuan_context_t ctx=0 ) { AssuanContextBase::reset( ctx, &assuan_deinit_server ); }
};
static inline gpg_error_t assuan_process_done_msg( assuan_context_t ctx, gpg_error_t err, const char * err_msg ) {
return assuan_process_done( ctx, assuan_set_error( ctx, err, err_msg ) );
}
static inline gpg_error_t assuan_process_done_msg( assuan_context_t ctx, gpg_error_t err, const std::string & err_msg ) {
return assuan_process_done_msg( ctx, err, err_msg.c_str() );
}
static inline gpg_error_t assuan_process_done_msg( assuan_context_t ctx, gpg_error_t err, const QString & err_msg ) {
return assuan_process_done_msg( ctx, err, err_msg.toUtf8().constData() );
}
static std::map<std::string,std::string> upcase_option( const char * option, std::map<std::string,std::string> options ) {
std::string value;
bool value_found = false;
std::map<std::string,std::string>::iterator it = options.begin();
while ( it != options.end() )
if ( qstricmp( it->first.c_str(), option ) == 0 ) {
value = it->second;
options.erase( it++ );
value_found = true;
} else {
++it;
}
if ( value_found )
options[option] = value;
return options;
}
static std::map<std::string,std::string> parse_commandline( const char * line ) {
std::map<std::string,std::string> result;
if ( line ) {
const char * begin = line;
const char * lastEQ = 0;
while ( *line ) {
if ( *line == ' ' || *line == '\t' ) {
if ( begin != line ) {
if ( begin[0] == '-' && begin[1] == '-' )
begin += 2; // skip initial "--"
if ( lastEQ && lastEQ > begin )
result[ std::string( begin, lastEQ - begin ) ] = hexdecode( std::string( lastEQ+1, line - (lastEQ+1) ) );
else
result[ std::string( begin, line - begin ) ] = std::string();
}
begin = line + 1;
} else if ( *line == '=' ) {
if ( line == begin )
throw Exception( gpg_error( GPG_ERR_ASS_SYNTAX ),
i18n("No option name given") );
else
lastEQ = line;
}
++line;
}
if ( begin != line ) {
if ( begin[0] == '-' && begin[1] == '-' )
begin += 2; // skip initial "--"
if ( lastEQ && lastEQ > begin )
result[ std::string( begin, lastEQ - begin ) ] = hexdecode( std::string( lastEQ+1, line - (lastEQ+1 ) ) );
else
result[ begin ] = std::string();
}
}
return result;
}
//
//
// AssuanServerConnection:
//
//
class AssuanServerConnection::Private : public QObject {
Q_OBJECT
friend class ::Kleo::AssuanServerConnection;
friend class ::Kleo::AssuanCommandFactory;
friend class ::Kleo::AssuanCommand;
AssuanServerConnection * const q;
public:
Private( assuan_fd_t fd_, const std::vector< shared_ptr<AssuanCommandFactory> > & factories_, AssuanServerConnection * qq );
~Private();
Q_SIGNALS:
void startKeyManager();
public Q_SLOTS:
void slotReadActivity( int ) {
assert( ctx );
if ( const int err = assuan_process_next( ctx.get() ) ) {
//if ( err == -1 || gpg_err_code(err) == GPG_ERR_EOF ) {
topHalfDeletion();
if ( nohupedCommands.empty() )
bottomHalfDeletion();
//} else {
//assuan_process_done( ctx.get(), err );
//return;
//}
}
}
int startCommandBottomHalf();
private:
void nohupDone( AssuanCommand * cmd ) {
const std::vector< shared_ptr<AssuanCommand> >::iterator it
= std::find_if( nohupedCommands.begin(), nohupedCommands.end(),
bind( &shared_ptr<AssuanCommand>::get, _1 ) == cmd );
assert( it != nohupedCommands.end() );
nohupedCommands.erase( it );
if ( nohupedCommands.empty() && closed )
bottomHalfDeletion();
}
void commandDone( AssuanCommand * cmd ) {
if ( !cmd || cmd != currentCommand.get() )
return;
currentCommand.reset();
}
void topHalfDeletion() {
if ( currentCommand )
currentCommand->canceled();
if ( fd != ASSUAN_INVALID_FD ) {
#ifdef Q_OS_WIN32
CloseHandle( fd );
#else
::close( fd );
#endif
}
notifiers.clear();
closed = true;
}
void bottomHalfDeletion() {
cleanup();
const QPointer<Private> that = this;
emit q->closed( q );
if ( that ) // still there
q->deleteLater();
}
private:
static void reset_handler( assuan_context_t ctx_ ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
conn.reset();
}
static int option_handler( assuan_context_t ctx_, const char * key, const char * value ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
if ( key && key[0] == '-' && key[1] == '-' )
key += 2; // skip "--"
conn.options[key] = QString::fromUtf8( value );
return 0;
//return gpg_error( GPG_ERR_UNKNOWN_OPTION );
}
static int session_handler( assuan_context_t ctx_, char * line ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
const QString str = QString::fromUtf8( line );
QRegExp rx( QLatin1String( "(?:\\d+)(?:\\s+(.*))?" ) );
if ( !rx.exactMatch( str ) ) {
static const QString errorString = i18n("Parse error");
return assuan_process_done_msg( ctx_, gpg_error( GPG_ERR_ASS_SYNTAX ), errorString );
}
if ( !rx.cap( 1 ).isEmpty() )
conn.sessionTitle = rx.cap( 1 );
return assuan_process_done( ctx_, 0 );
}
static int capabilities_handler( assuan_context_t ctx_, char * line ) {
if ( !QByteArray( line ).trimmed().isEmpty() ) {
static const QString errorString = i18n("CAPABILITIES doesn't take arguments");
return assuan_process_done_msg( ctx_, gpg_error( GPG_ERR_ASS_PARAMETER ), errorString );
}
static const char capabilities[] =
"SENDER=info\n"
"RECIPIENT=info\n"
"SESSION\n"
;
return assuan_process_done( ctx_, assuan_send_data( ctx_, capabilities, sizeof capabilities - 1 ) );
}
static int getinfo_handler( assuan_context_t ctx_, char * line ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
if ( qstrcmp( line, "version" ) == 0 ) {
static const char version[] = "Kleopatra " KLEOPATRA_VERSION_STRING ;
return assuan_process_done( ctx_, assuan_send_data( ctx_, version, sizeof version - 1 ) );
}
QByteArray ba;
if ( qstrcmp( line, "pid" ) == 0 )
ba = QByteArray::number( mygetpid() );
else if ( qstrcmp( line, "options" ) == 0 )
ba = conn.dumpOptions();
else if ( qstrcmp( line, "x-mementos" ) == 0 )
ba = conn.dumpMementos();
else if ( qstrcmp( line, "senders" ) == 0 )
ba = conn.dumpSenders();
else if ( qstrcmp( line, "recipients" ) == 0 )
ba = conn.dumpRecipients();
else if ( qstrcmp( line, "x-files" ) == 0 )
ba = conn.dumpFiles();
else {
static const QString errorString = i18n("Unknown value for WHAT");
return assuan_process_done_msg( ctx_, gpg_error( GPG_ERR_ASS_PARAMETER ), errorString );
}
return assuan_process_done( ctx_, assuan_send_data( ctx_, ba.constData(), ba.size() ) );
}
static int start_keymanager_handler( assuan_context_t ctx_, char * line ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
if ( line && *line ) {
static const QString errorString = i18n("START_KEYMANAGER does not take arguments");
return assuan_process_done_msg( ctx_, gpg_error( GPG_ERR_ASS_PARAMETER ), errorString );
}
emit conn.q->startKeyManagerRequested();
return assuan_process_done( ctx_, 0 );
}
static int start_confdialog_handler( assuan_context_t ctx_, char * line ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
if ( line && *line ) {
static const QString errorString = i18n("START_CONFDIALOG does not take arguments");
return assuan_process_done_msg( ctx_, gpg_error( GPG_ERR_ASS_PARAMETER ), errorString );
}
emit conn.q->startConfigDialogRequested();
return assuan_process_done( ctx_, 0 );
}
template <bool in> struct Input_or_Output : mpl::if_c<in,Input,Output> {};
// format: TAG (FD|FD=\d+|FILE=...)
template <bool in, typename T_memptr>
static int IO_handler( assuan_context_t ctx_, char * line_, T_memptr which ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
try {
/*const*/ std::map<std::string,std::string> options = upcase_option( "FD", upcase_option( "FILE", parse_commandline( line_ ) ) );
if ( options.size() < 1 || options.size() > 2 )
throw gpg_error( GPG_ERR_ASS_SYNTAX );
shared_ptr< typename Input_or_Output<in>::type > io;
if ( options.count( "FD" ) ) {
if ( options.count( "FILE" ) )
throw gpg_error( GPG_ERR_CONFLICT );
assuan_fd_t fd = ASSUAN_INVALID_FD;
const std::string fdstr = options["FD"];
if ( fdstr.empty() ) {
if ( const gpg_error_t err = assuan_receivefd( conn.ctx.get(), &fd ) )
throw err;
} else {
#ifdef Q_OS_WIN32
fd = (assuan_fd_t)lexical_cast<intptr_t>( fdstr );
#else
fd = lexical_cast<assuan_fd_t>( fdstr );
#endif
}
io = Input_or_Output<in>::type::createFromPipeDevice( fd, i18n( "Message #%1", (conn.*which).size() + 1 ) );
options.erase( "FD" );
} else if ( options.count( "FILE" ) ) {
if ( options.count( "FD" ) )
throw gpg_error( GPG_ERR_CONFLICT );
const QString filePath = QFile::decodeName( options["FILE"].c_str() );
if ( filePath.isEmpty() )
throw Exception( gpg_error( GPG_ERR_ASS_SYNTAX ), i18n("Empty file path") );
const QFileInfo fi( filePath );
if ( !fi.isAbsolute() )
throw Exception( gpg_error( GPG_ERR_INV_ARG ), i18n("Only absolute file paths are allowed") );
if ( fi.isDir() )
io = Input_or_Output<in>::type::createFromDir( fi.absoluteFilePath() );
else
io = Input_or_Output<in>::type::createFromFile( fi.absoluteFilePath(), true );
options.erase( "FILE" );
} else {
throw gpg_error( GPG_ERR_ASS_PARAMETER );
}
if ( options.size() )
throw gpg_error( GPG_ERR_UNKNOWN_OPTION );
(conn.*which).push_back( io );
qDebug() << "AssuanServerConnection: added" << io->label();
return assuan_process_done( conn.ctx.get(), 0 );
} catch ( const GpgME::Exception & e ) {
return assuan_process_done_msg( conn.ctx.get(), e.error().encodedError(), e.message().c_str() );
} catch ( const std::exception & ) {
return assuan_process_done( conn.ctx.get(), gpg_error( GPG_ERR_ASS_SYNTAX ) );
} catch ( const gpg_error_t e ) {
return assuan_process_done( conn.ctx.get(), e );
} catch ( ... ) {
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), "unknown exception caught" );
}
}
static int input_handler( assuan_context_t ctx, char * line ) {
return IO_handler<true>( ctx, line, &Private::inputs );
}
static int output_handler( assuan_context_t ctx, char * line ) {
return IO_handler<false>( ctx, line, &Private::outputs );
}
static int message_handler( assuan_context_t ctx, char * line ) {
return IO_handler<true>( ctx, line, &Private::messages );
}
static int file_handler( assuan_context_t ctx_, char * line ) {
assert( assuan_get_pointer( ctx_ ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx_ ) );
try {
const QFileInfo fi( QFile::decodeName( hexdecode( line ).c_str() ) );
if ( !fi.isAbsolute() )
throw Exception( gpg_error( GPG_ERR_INV_ARG ), i18n("Only absolute file paths are allowed") );
const QString filePath = fi.absoluteFilePath();
if ( fi.exists() && fi.isDir() ) {
if ( !fi.isReadable() || !fi.isExecutable() )
throw Exception( gpg_error( GPG_ERR_INV_ARG ), i18n("Could not access directory \"%1\" for reading", filePath ) );
conn.dirs.push_back( filePath );
return assuan_process_done( conn.ctx.get(), 0 );
}
const shared_ptr<QFile> file( new QFile( filePath ) );
if ( !file->open( QIODevice::ReadOnly ) )
throw Exception( gpg_error_from_errno( errno ), i18n("Could not open file \"%1\" for reading", filePath) );
const IOF io = {
filePath, file
};
conn.files.push_back( io );
return assuan_process_done( conn.ctx.get(), 0 );
} catch ( const Exception & e ) {
return assuan_process_done_msg( conn.ctx.get(), e.error().encodedError(), e.message().toUtf8().constData() );
} catch ( ... ) {
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), i18n("unknown exception caught").toUtf8().constData() );
}
}
static bool parse_informative( const char * & begin ) {
bool informative = false;
const char * pos = begin;
while ( true ) {
while ( *pos == ' ' || *pos == '\t' )
++pos;
if ( qstrnicmp( pos, "--info", strlen("--info") ) == 0 ) {
informative = true;
pos += strlen("--info");
if ( *pos == '=' ) {
++pos;
break;
}
} else if ( qstrncmp( pos, "-- ", strlen("-- ") ) == 0 ) {
pos += 3;
while ( *pos == ' ' || *pos == '\t' )
++pos;
break;
} else
break;
}
begin = pos;
return informative;
}
template <typename T_memptr, typename T_memptr2>
static int recipient_sender_handler( T_memptr mp, T_memptr2 info, assuan_context_t ctx, char * line, bool sender=false ) {
assert( assuan_get_pointer( ctx ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx ) );
if ( !line || !*line )
return assuan_process_done( conn.ctx.get(), gpg_error( GPG_ERR_INV_ARG ) );
const char * begin = line;
const char * const end = begin + qstrlen( line );
const bool informative = parse_informative( begin );
if ( !(conn.*mp).empty() && informative != (conn.*info) )
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_CONFLICT ),
i18n("Cannot mix --info with non-info SENDER or RECIPIENT").toUtf8().constData() );
KMime::Types::Mailbox mb;
if ( !KMime::HeaderParsing::parseMailbox( begin, end, mb ) )
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_INV_ARG ),
i18n("Argument is not a valid RFC-2822 mailbox").toUtf8().constData() );
if ( begin != end )
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_INV_ARG ),
i18n("Garbage after valid RFC-2822 mailbox detected").toUtf8().constData() );
(conn.*info) = informative;
(conn.*mp).push_back( mb );
// ### we should wait for the KeyCache to become ready here,
// ### but we don't have time anymore to turn SENDER into a
// ### command, so we just ignore this for now:
const QString email = mb.addrSpec().asString();
(void)assuan_write_line( conn.ctx.get(), qPrintable( QString().sprintf( "# ok, parsed as \"%s\"", qPrintable( email ) ) ) );
if ( sender ) {
const std::vector<GpgME::Key> seckeys =
kdtools::copy_if<std::vector<GpgME::Key> >( KeyCache::instance()->findByEMailAddress( email.toStdString() ),
mem_fn( &GpgME::Key::hasSecret ) );
if ( seckeys.empty() )
(void)assuan_write_line( conn.ctx.get(), "# no matching keys found" );
else
Q_FOREACH( const GpgME::Key & key, seckeys )
(void)assuan_write_line( conn.ctx.get(), qPrintable( QString().sprintf( "# matching %s key %s", key.protocolAsString(), key.primaryFingerprint() ) ) );
const bool pgp = kdtools::any( seckeys, bind( &GpgME::Key::protocol, _1 ) == GpgME::OpenPGP );
const bool cms = kdtools::any( seckeys, bind( &GpgME::Key::protocol, _1 ) == GpgME::CMS );
if ( pgp != cms ) // only send PROTOCOL status line if there's a clesar preference
(void)assuan_write_status( conn.ctx.get(), "PROTOCOL", pgp ? "OpenPGP" : "CMS" );
else if ( pgp && cms )
(void)assuan_write_line( conn.ctx.get(), "# pick freely" );
- }
+ }
return assuan_process_done( ctx, 0 );
}
static int recipient_handler( assuan_context_t ctx, char * line ) {
return recipient_sender_handler( &Private::recipients, &Private::informativeRecipients, ctx, line );
}
static int sender_handler( assuan_context_t ctx, char * line ) {
return recipient_sender_handler( &Private::senders, &Private::informativeSenders, ctx, line, true );
}
QByteArray dumpOptions() const {
QByteArray result;
for ( std::map<std::string,QVariant>::const_iterator it = options.begin(), end = options.end() ; it != end ; ++it )
result += it->first.c_str() + it->second.toString().toUtf8() + '\n';
return result;
}
static QByteArray dumpStringList( const QStringList & sl ) {
return sl.join( QLatin1String( "\n" ) ).toUtf8();
}
template <typename T_container>
static QByteArray dumpStringList( const T_container & c ) {
QStringList sl;
std::copy( c.begin(), c.end(), std::back_inserter( sl ) );
return dumpStringList( sl );
}
template <typename T_container>
static QByteArray dumpMailboxes( const T_container & c ) {
QStringList sl;
std::transform( c.begin(), c.end(),
std::back_inserter( sl ),
bind( &KMime::Types::Mailbox::prettyAddress, _1 ) );
return dumpStringList( sl );
}
QByteArray dumpSenders() const {
return dumpMailboxes( senders );
}
QByteArray dumpRecipients() const {
return dumpMailboxes( recipients );
}
QByteArray dumpMementos() const {
QByteArray result;
for ( std::map< QByteArray, shared_ptr<AssuanCommand::Memento> >::const_iterator it = mementos.begin(), end = mementos.end() ; it != end ; ++it ) {
char buf[2 + 2*sizeof(void*) + 2];
sprintf( buf, "0x%p\n", it->second.get() );
buf[sizeof(buf)-1] = '\0';
result += it->first + QByteArray::fromRawData( buf, sizeof buf );
}
return result;
}
QByteArray dumpFiles() const {
QStringList sl;
std::transform( files.begin(), files.end(), std::back_inserter( sl ),
bind( &IOF::fileName, _1 ) );
return dumpStringList( sl );
}
void cleanup();
void reset() {
options.clear();
senders.clear();
informativeSenders = false;
recipients.clear();
informativeRecipients = false;
sessionTitle.clear();
mementos.clear();
close_all( files );
files.clear();
std::for_each( inputs.begin(), inputs.end(),
bind( &Input::finalize, _1 ) );
inputs.clear();
std::for_each( outputs.begin(), outputs.end(),
bind( &Output::finalize, _1 ) );
outputs.clear();
std::for_each( messages.begin(), messages.end(),
bind( &Input::finalize, _1 ) );
messages.clear();
}
assuan_fd_t fd;
AssuanContext ctx;
bool closed : 1;
bool cryptoCommandsEnabled : 1;
bool commandWaitingForCryptoCommandsEnabled : 1;
bool currentCommandIsNohup : 1;
bool informativeSenders; // address taken, so no : 1
bool informativeRecipients; // address taken, so no : 1
QString sessionTitle;
std::vector< shared_ptr<QSocketNotifier> > notifiers;
std::vector< shared_ptr<AssuanCommandFactory> > factories; // sorted: _detail::ByName<std::less>
shared_ptr<AssuanCommand> currentCommand;
std::vector< shared_ptr<AssuanCommand> > nohupedCommands;
std::map<std::string,QVariant> options;
std::vector<KMime::Types::Mailbox> senders, recipients;
std::vector< shared_ptr<Input> > inputs, messages;
std::vector< shared_ptr<Output> > outputs;
std::vector<IOF> files;
QStringList dirs;
std::map< QByteArray, shared_ptr<AssuanCommand::Memento> > mementos;
};
void AssuanServerConnection::Private::cleanup() {
assert( nohupedCommands.empty() );
reset();
currentCommand.reset();
currentCommandIsNohup = false;
commandWaitingForCryptoCommandsEnabled = false;
notifiers.clear();
ctx.reset();
fd = ASSUAN_INVALID_FD;
}
AssuanServerConnection::Private::Private( assuan_fd_t fd_, const std::vector< shared_ptr<AssuanCommandFactory> > & factories_, AssuanServerConnection * qq )
: QObject(),
q( qq ),
fd( fd_ ),
closed( false ),
cryptoCommandsEnabled( false ),
commandWaitingForCryptoCommandsEnabled( false ),
currentCommandIsNohup( false ),
informativeSenders( false ),
informativeRecipients( false ),
factories( factories_ )
{
#ifdef __GNUC__
assert( __gnu_cxx::is_sorted( factories_.begin(), factories_.end(), _detail::ByName<std::less>() ) );
#endif
if ( fd == ASSUAN_INVALID_FD )
throw Exception( gpg_error( GPG_ERR_INV_ARG ), "pre-assuan_init_socket_server_ext" );
assuan_context_t naked_ctx = 0;
if ( const gpg_error_t err = assuan_init_socket_server_ext( &naked_ctx, fd, INIT_SOCKET_FLAGS ) )
throw Exception( err, "assuan_init_socket_server_ext" );
ctx.reset( naked_ctx ); naked_ctx = 0;
// for callbacks, associate the context with this connection:
assuan_set_pointer( ctx.get(), this );
FILE* const logFile = Log::instance()->logFile();
assuan_set_log_stream( ctx.get(), logFile ? logFile : stderr );
// register FDs with the event loop:
assuan_fd_t fds[MAX_ACTIVE_FDS];
const int numFDs = assuan_get_active_fds( ctx.get(), FOR_READING, fds, MAX_ACTIVE_FDS );
assert( numFDs != -1 ); // == 1
if ( !numFDs || fds[0] != fd ) {
const shared_ptr<QSocketNotifier> sn( new QSocketNotifier( (int)fd, QSocketNotifier::Read ), mem_fn( &QObject::deleteLater ) );
connect( sn.get(), SIGNAL(activated(int)), this, SLOT(slotReadActivity(int)) );
notifiers.push_back( sn );
}
notifiers.reserve( notifiers.size() + numFDs );
for ( int i = 0 ; i < numFDs ; ++i ) {
const shared_ptr<QSocketNotifier> sn( new QSocketNotifier( (int)fds[i], QSocketNotifier::Read ), mem_fn( &QObject::deleteLater ) );
connect( sn.get(), SIGNAL(activated(int)), this, SLOT(slotReadActivity(int)) );
notifiers.push_back( sn );
}
// register our INPUT/OUTPUT/MESSGAE/FILE handlers:
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "INPUT", input_handler ) )
throw Exception( err, "register \"INPUT\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "MESSAGE", message_handler ) )
throw Exception( err, "register \"MESSAGE\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "OUTPUT", output_handler ) )
throw Exception( err, "register \"OUTPUT\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "FILE", file_handler ) )
throw Exception( err, "register \"FILE\" handler" );
// register user-defined commands:
Q_FOREACH( shared_ptr<AssuanCommandFactory> fac, factories )
if ( const gpg_error_t err = assuan_register_command( ctx.get(), fac->name(), fac->_handler() ) )
throw Exception( err, std::string( "register \"" ) + fac->name() + "\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "GETINFO", getinfo_handler ) )
throw Exception( err, "register \"GETINFO\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "START_KEYMANAGER", start_keymanager_handler ) )
throw Exception( err, "register \"START_KEYMANAGER\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "START_CONFDIALOG", start_confdialog_handler ) )
throw Exception( err, "register \"START_CONFDIALOG\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "RECIPIENT", recipient_handler ) )
throw Exception( err, "register \"RECIPIENT\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "SENDER", sender_handler ) )
throw Exception( err, "register \"SENDER\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "SESSION", session_handler ) )
throw Exception( err, "register \"SESSION\" handler" );
if ( const gpg_error_t err = assuan_register_command( ctx.get(), "CAPABILITIES", capabilities_handler ) )
throw Exception( err, "register \"CAPABILITIES\" handler" );
assuan_set_hello_line( ctx.get(), "GPG UI server (Kleopatra/" KLEOPATRA_VERSION_STRING ") ready to serve" );
//assuan_set_hello_line( ctx.get(), GPG UI server (qApp->applicationName() + " v" + kapp->applicationVersion() + "ready to serve" )
// some notifiers we're interested in:
if ( const gpg_error_t err = assuan_register_reset_notify( ctx.get(), reset_handler ) )
throw Exception( err, "register reset notify" );
if ( const gpg_error_t err = assuan_register_option_handler( ctx.get(), option_handler ) )
throw Exception( err, "register option handler" );
// and last, we need to call assuan_accept, which doesn't block
// (d/t INIT_SOCKET_FLAGS), but performs vital connection
// establishing handling:
if ( const gpg_error_t err = assuan_accept( ctx.get() ) )
throw Exception( err, "assuan_accept" );
}
AssuanServerConnection::Private::~Private() {
cleanup();
}
AssuanServerConnection::AssuanServerConnection( assuan_fd_t fd, const std::vector< shared_ptr<AssuanCommandFactory> > & factories, QObject * p )
: QObject( p ), d( new Private( fd, factories, this ) )
{
}
AssuanServerConnection::~AssuanServerConnection() {}
void AssuanServerConnection::enableCryptoCommands( bool on ) {
if ( on == d->cryptoCommandsEnabled )
return;
d->cryptoCommandsEnabled = on;
if ( d->commandWaitingForCryptoCommandsEnabled )
QTimer::singleShot( 0, d.get(), SLOT(startCommandBottomHalf()) );
}
//
//
// AssuanCommand:
//
//
namespace Kleo {
class InquiryHandler : public QObject {
Q_OBJECT
public:
#ifdef HAVE_ASSUAN_INQUIRE_EXT
explicit InquiryHandler( const char * keyword_, QObject * p=0 )
: QObject( p ),
# ifndef HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT
buffer( 0 ),
buflen( 0 ),
# endif
keyword( keyword_ )
{
}
# ifdef HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT
static int handler( void * cb_data, int rc, unsigned char * buffer, size_t buflen )
{
assert( cb_data );
InquiryHandler * this_ = static_cast<InquiryHandler*>(cb_data);
emit this_->signal( rc, QByteArray::fromRawData( reinterpret_cast<const char*>(buffer), buflen ), this_->keyword );
std::free( buffer );
delete this_;
return 0;
}
# else
static int handler( void * cb_data, int rc )
{
assert( cb_data );
InquiryHandler * this_ = static_cast<InquiryHandler*>(cb_data);
emit this_->signal( rc, QByteArray::fromRawData( reinterpret_cast<const char*>(this_->buffer), this_->buflen ), this_->keyword );
std::free( this_->buffer );
delete this_;
return 0;
}
# endif
private:
# ifndef HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT
friend class ::Kleo::AssuanCommand;
unsigned char * buffer;
size_t buflen;
# endif
const char * keyword;
#endif // HAVE_ASSUAN_INQUIRE_EXT
Q_SIGNALS:
void signal( int rc, const QByteArray & data, const QByteArray & keyword );
};
} // namespace Kleo
class AssuanCommand::Private {
public:
Private()
: informativeRecipients( false ),
informativeSenders( false ),
done( false ),
nohup( false )
{
}
std::map<std::string,QVariant> options;
std::vector< shared_ptr<Input> > inputs, messages;
std::vector< shared_ptr<Output> > outputs;
std::vector<IOF> files;
std::vector<KMime::Types::Mailbox> recipients, senders;
bool informativeRecipients, informativeSenders;
QString sessionTitle;
QByteArray utf8ErrorKeepAlive;
AssuanContext ctx;
bool done;
bool nohup;
};
AssuanCommand::AssuanCommand()
: d( new Private )
{
}
AssuanCommand::~AssuanCommand() {
}
int AssuanCommand::start() {
try {
if ( const int err = doStart() )
if ( !d->done )
done( err );
return 0;
} catch ( const Exception & e ) {
if ( !d->done )
done( e.error_code(), e.message() );
return 0;
} catch ( const GpgME::Exception & e ) {
if ( !d->done )
done( e.error(), QString::fromLocal8Bit( e.message().c_str() ) );
return 0;
} catch ( const std::exception & e ) {
if ( !d->done )
done( makeError( GPG_ERR_INTERNAL ), i18n("Caught unexpected exception: %1", QString::fromLocal8Bit( e.what() ) ) );
return 0;
} catch ( ... ) {
if ( !d->done )
done( makeError( GPG_ERR_INTERNAL ), i18n("Caught unknown exception - fix the program!" ) );
return 0;
}
}
void AssuanCommand::canceled() {
d->done = true;
doCanceled();
}
// static
int AssuanCommand::makeError( int code ) {
return makeGnuPGError( code );
}
bool AssuanCommand::hasOption( const char * opt ) const {
return d->options.count( opt );
}
QVariant AssuanCommand::option( const char * opt ) const {
const std::map<std::string,QVariant>::const_iterator it = d->options.find( opt );
if ( it == d->options.end() )
return QVariant();
else
return it->second;
}
const std::map<std::string,QVariant> & AssuanCommand::options() const {
return d->options;
}
namespace {
template <typename U, typename V>
std::vector<U> keys( const std::map<U,V> & map ) {
std::vector<U> result;
result.resize( map.size() );
for ( typename std::map<U,V>::const_iterator it = map.begin(), end = map.end() ; it != end ; ++it )
result.push_back( it->first );
return result;
}
}
const std::map< QByteArray, shared_ptr<AssuanCommand::Memento> > & AssuanCommand::mementos() const {
// oh, hack :(
assert( assuan_get_pointer( d->ctx.get() ) );
const AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( d->ctx.get() ) );
return conn.mementos;
}
bool AssuanCommand::hasMemento( const QByteArray & tag ) const {
return mementos().count( tag );
}
shared_ptr<AssuanCommand::Memento> AssuanCommand::memento( const QByteArray & tag ) const {
const std::map< QByteArray, shared_ptr<Memento> >::const_iterator it = mementos().find( tag );
if ( it == mementos().end() )
return shared_ptr<Memento>();
else
return it->second;
}
QByteArray AssuanCommand::registerMemento( const shared_ptr<Memento> & mem ) {
const QByteArray tag = QByteArray::number( reinterpret_cast<qulonglong>( mem.get() ), 36 );
return registerMemento( tag, mem );
}
QByteArray AssuanCommand::registerMemento( const QByteArray & tag, const shared_ptr<Memento> & mem ) {
// oh, hack :(
assert( assuan_get_pointer( d->ctx.get() ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( d->ctx.get() ) );
conn.mementos[tag] = mem;
return tag;
}
void AssuanCommand::removeMemento( const QByteArray & tag ) {
// oh, hack :(
assert( assuan_get_pointer( d->ctx.get() ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( d->ctx.get() ) );
conn.mementos.erase( tag );
}
const std::vector< shared_ptr<Input> > & AssuanCommand::inputs() const {
return d->inputs;
}
const std::vector< shared_ptr<Input> > & AssuanCommand::messages() const {
return d->messages;
}
const std::vector< shared_ptr<Output> > & AssuanCommand::outputs() const {
return d->outputs;
}
QStringList AssuanCommand::fileNames() const {
QStringList result;
Q_FOREACH( const IOF & io, d->files )
result.push_back( io.fileName );
return result;
}
std::vector< shared_ptr<QFile> > AssuanCommand::files() const {
std::vector< shared_ptr<QFile> > result;
Q_FOREACH( const IOF & io, d->files )
result.push_back( io.file );
return result;
}
unsigned int AssuanCommand::numFiles() const {
return d->files.size();
}
#if 0
QString AssuanCommand::bulkInputDeviceFileName( unsigned int idx ) const {
return d->inputs.at( idx ).fileName;
}
shared_ptr<QIODevice> AssuanCommand::bulkInputDevice( unsigned int idx ) const {
return d->inputs.at( idx ).iodev;
}
unsigned int AssuanCommand::numBulkInputDevices() const {
return d->inputs.size();
}
QString AssuanCommand::bulkMessageDeviceFileName( unsigned int idx ) const {
return d->messages.at( idx ).fileName;
}
shared_ptr<QIODevice> AssuanCommand::bulkMessageDevice( unsigned int idx ) const {
return d->messages.at( idx ).iodev;
}
unsigned int AssuanCommand::numBulkMessageDevices() const {
return d->messages.size();
}
QString AssuanCommand::bulkOutputDeviceFileName( unsigned int idx ) const {
return d->outputs.at( idx ).fileName;
}
shared_ptr<QIODevice> AssuanCommand::bulkOutputDevice( unsigned int idx ) const {
return d->outputs.at( idx ).iodev;
}
unsigned int AssuanCommand::numBulkOutputDevices() const {
return d->outputs.size();
}
#endif
void AssuanCommand::sendStatus( const char * keyword, const QString & text ) {
sendStatusEncoded( keyword, text.toUtf8().constData() );
}
void AssuanCommand::sendStatusEncoded( const char * keyword, const std::string & text ) {
if ( d->nohup )
return;
if ( const int err = assuan_write_status( d->ctx.get(), keyword, text.c_str() ) )
throw Exception( err, i18n( "Can't send \"%1\" status", QString::fromLatin1( keyword ) ) );
}
void AssuanCommand::sendData( const QByteArray & data, bool moreToCome ) {
if ( d->nohup )
return;
if ( const gpg_error_t err = assuan_send_data( d->ctx.get(), data.constData(), data.size() ) )
throw Exception( err, i18n( "Can't send data" ) );
if ( !moreToCome )
if ( const gpg_error_t err = assuan_send_data( d->ctx.get(), 0, 0 ) ) // flush
throw Exception( err, i18n( "Can't flush data" ) );
}
int AssuanCommand::inquire( const char * keyword, QObject * receiver, const char * slot, unsigned int maxSize ) {
assert( keyword );
assert( receiver );
assert( slot );
if ( d->nohup )
return makeError( GPG_ERR_INV_OP );
#ifdef HAVE_ASSUAN_INQUIRE_EXT
std::auto_ptr<InquiryHandler> ih( new InquiryHandler( keyword, receiver ) );
receiver->connect( ih.get(), SIGNAL(signal(int,QByteArray,QByteArray)), slot );
if ( const gpg_error_t err = assuan_inquire_ext( d->ctx.get(), keyword,
# ifndef HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT
&ih->buffer, &ih->buflen,
# endif
maxSize, InquiryHandler::handler, ih.get() ) )
return err;
ih.release();
return 0;
#else
return makeError( GPG_ERR_NOT_SUPPORTED ); // libassuan too old
#endif // HAVE_ASSUAN_INQUIRE_EXT
}
void AssuanCommand::done( const GpgME::Error& err, const QString & details ) {
if ( d->ctx && !d->done && !details.isEmpty() ) {
qDebug() << "AssuanCommand::done(): Error: " << details;
d->utf8ErrorKeepAlive = details.toUtf8();
if ( !d->nohup )
assuan_set_error( d->ctx.get(), err.encodedError(), d->utf8ErrorKeepAlive.constData() );
}
done( err );
}
void AssuanCommand::done( const GpgME::Error& err ) {
if ( !d->ctx ) {
qDebug( "AssuanCommand::done( %s ): called with NULL ctx.", err.asString() );
return;
}
if ( d->done ) {
qDebug( "AssuanCommand::done( %s ): called twice!", err.asString() );
return;
}
d->done = true;
std::for_each( d->messages.begin(), d->messages.end(),
bind( &Input::finalize, _1 ) );
std::for_each( d->inputs.begin(), d->inputs.end(),
bind( &Input::finalize, _1 ) );
std::for_each( d->outputs.begin(), d->outputs.end(),
bind( &Output::finalize, _1 ) );
d->messages.clear();
d->inputs.clear();
d->outputs.clear();
close_all( d->files ); // ### ???
// oh, hack :(
assert( assuan_get_pointer( d->ctx.get() ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( d->ctx.get() ) );
if ( d->nohup ) {
conn.nohupDone( this );
return;
}
const gpg_error_t rc = assuan_process_done( d->ctx.get(), err.encodedError() );
if ( gpg_err_code( rc ) != GPG_ERR_NO_ERROR )
qFatal( "AssuanCommand::done: assuan_process_done returned error %d (%s)",
static_cast<int>(rc), gpg_strerror(rc) );
d->utf8ErrorKeepAlive.clear();
conn.commandDone( this );
}
void AssuanCommand::setNohup( bool nohup ) {
d->nohup = nohup;
}
bool AssuanCommand::isNohup() const {
return d->nohup;
}
bool AssuanCommand::isDone() const {
return d->done;
}
QString AssuanCommand::sessionTitle() const {
return d->sessionTitle;
}
bool AssuanCommand::informativeSenders() const {
return d->informativeSenders;
}
bool AssuanCommand::informativeRecipients() const {
return d->informativeRecipients;
}
const std::vector<KMime::Types::Mailbox> & AssuanCommand::recipients() const {
return d->recipients;
}
const std::vector<KMime::Types::Mailbox> & AssuanCommand::senders() const {
return d->senders;
}
int AssuanCommandFactory::_handle( assuan_context_t ctx, char * line, const char * commandName ) {
assert( assuan_get_pointer( ctx ) );
AssuanServerConnection::Private & conn = *static_cast<AssuanServerConnection::Private*>( assuan_get_pointer( ctx ) );
try {
const std::vector< shared_ptr<AssuanCommandFactory> >::const_iterator it
= std::lower_bound( conn.factories.begin(), conn.factories.end(), commandName, _detail::ByName<std::less>() );
kleo_assert( it != conn.factories.end() );
kleo_assert( *it );
kleo_assert( qstricmp( (*it)->name(), commandName ) == 0 );
const shared_ptr<AssuanCommand> cmd = (*it)->create();
kleo_assert( cmd );
cmd->d->ctx = conn.ctx;
cmd->d->options = conn.options;
cmd->d->inputs.swap( conn.inputs ); kleo_assert( conn.inputs.empty() );
cmd->d->messages.swap( conn.messages ); kleo_assert( conn.messages.empty() );
cmd->d->outputs.swap( conn.outputs ); kleo_assert( conn.outputs.empty() );
cmd->d->files.swap( conn.files ); kleo_assert( conn.files.empty() );
cmd->d->senders.swap( conn.senders ); kleo_assert( conn.senders.empty() );
cmd->d->recipients.swap( conn.recipients ); kleo_assert( conn.recipients.empty() );
cmd->d->informativeRecipients = conn.informativeRecipients;
cmd->d->informativeSenders = conn.informativeSenders;
cmd->d->sessionTitle = conn.sessionTitle;
const std::map<std::string,std::string> cmdline_options = parse_commandline( line );
for ( std::map<std::string,std::string>::const_iterator it = cmdline_options.begin(), end = cmdline_options.end() ; it != end ; ++it )
cmd->d->options[it->first] = QString::fromUtf8( it->second.c_str() );
bool nohup = false;
if ( cmd->d->options.count( "nohup" ) ) {
if ( !cmd->d->options["nohup"].toString().isEmpty() )
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_ASS_PARAMETER ), "--nohup takes no argument" );
nohup = true;
cmd->d->options.erase( "nohup" );
}
conn.currentCommand = cmd;
conn.currentCommandIsNohup = nohup;
QTimer::singleShot( 0, &conn, SLOT(startCommandBottomHalf()) );
return 0;
} catch ( const Exception & e ) {
return assuan_process_done_msg( conn.ctx.get(), e.error_code(), e.message() );
} catch ( const std::exception & e ) {
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), e.what() );
} catch ( ... ) {
return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), i18n("Caught unknown exception") );
}
}
int AssuanServerConnection::Private::startCommandBottomHalf() {
commandWaitingForCryptoCommandsEnabled = currentCommand && !cryptoCommandsEnabled;
if ( !cryptoCommandsEnabled )
return 0;
const shared_ptr<AssuanCommand> cmd = currentCommand;
if ( !cmd )
return 0;
currentCommand.reset();
const bool nohup = currentCommandIsNohup;
currentCommandIsNohup = false;
try {
if ( const int err = cmd->start() )
if ( cmd->isDone() )
return err;
else
return assuan_process_done( ctx.get(), err );
if ( cmd->isDone() )
return 0;
if ( nohup ) {
cmd->setNohup( true );
nohupedCommands.push_back( cmd );
return assuan_process_done_msg( ctx.get(), 0, "Command put in the background to continue executing after connection end." );
} else {
currentCommand = cmd;
return 0;
}
} catch ( const Exception & e ) {
return assuan_process_done_msg( ctx.get(), e.error_code(), e.message() );
} catch ( const std::exception & e ) {
return assuan_process_done_msg( ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), e.what() );
} catch ( ... ) {
return assuan_process_done_msg( ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), i18n("Caught unknown exception") );
}
}
//
//
// AssuanCommand convenience methods
//
//
/*!
Checks the \c --mode parameter.
\returns The parameter as an AssuanCommand::Mode enum value.
If no \c --mode was given, or it's value wasn't recognized, throws
an Kleo::Exception.
*/
AssuanCommand::Mode AssuanCommand::checkMode() const {
if ( !hasOption( "mode" ) )
throw Exception( makeError( GPG_ERR_MISSING_VALUE ), i18n( "Required --mode option missing" ) );
const QString modeString = option("mode").toString().toLower();
if ( modeString == QLatin1String( "filemanager" ) )
return FileManager;
if ( modeString == QLatin1String( "email" ) )
return EMail;
throw Exception( makeError( GPG_ERR_INV_ARG ), i18n( "invalid mode: \"%1\"", modeString ) );
}
/*!
Checks the \c --protocol parameter.
\returns The parameter as a GpgME::Protocol enum value.
If \c --protocol was given, but has an invalid value, throws an
Kleo::Exception.
If no \c --protocol was given, in FileManager mode, returns
GpgME::UnknownProtocol, but if \a mode == \c EMail, throws an
Kleo::Exception instead.
*/
-GpgME::Protocol AssuanCommand::checkProtocol( Mode mode ) const {
+GpgME::Protocol AssuanCommand::checkProtocol( Mode mode, int options ) const {
if ( !hasOption("protocol") )
- if ( mode == AssuanCommand::EMail )
+ if ( mode == AssuanCommand::EMail && ( options & AllowProtocolMissing ) == 0 )
throw Exception( makeError( GPG_ERR_MISSING_VALUE ), i18n( "Required --protocol option missing" ) );
else
return GpgME::UnknownProtocol;
else
if ( mode == AssuanCommand::FileManager )
throw Exception( makeError( GPG_ERR_INV_FLAG ), i18n("--protocol is not allowed here") );
const QString protocolString = option("protocol").toString().toLower();
if ( protocolString == QLatin1String( "openpgp" ) )
return GpgME::OpenPGP;
if ( protocolString == QLatin1String( "cms" ) )
return GpgME::CMS;
throw Exception( makeError( GPG_ERR_INV_ARG ), i18n( "invalid protocol \"%1\"", protocolString ) );
}
void AssuanCommand::doApplyWindowID( QWidget * widget ) const {
if ( !widget || !hasOption( "window-id" ) )
return;
const QString winIdStr = option("window-id").toString();
bool ok = false;
#ifdef Q_OS_WIN32
const WId wid = reinterpret_cast<WId>( winIdStr.toULongLong( &ok, 16 ) );
#else
const WId wid = static_cast<WId>( winIdStr.toULongLong( &ok, 16 ) );
#endif
if ( !ok ) {
qDebug() << "window-id value" << wid << "doesn't look like a number";
return;
}
if ( QWidget * pw = QWidget::find( wid ) )
widget->setParent( pw, widget->windowFlags() );
else {
KWindowSystem::setMainWindow( widget, wid );
}
}
static QString commonPrefix( const QString & s1, const QString & s2 ) {
return QString( s1.data(), std::mismatch( s1.data(), s1.data() + std::min( s1.size(), s2.size() ), s2.data() ).first - s1.data() );
}
static QString longestCommonPrefix( const QStringList & sl ) {
if ( sl.empty() )
return QString();
QString result = sl.front();
Q_FOREACH( const QString & s, sl )
result = commonPrefix( s, result );
return result;
}
QString AssuanCommand::heuristicBaseDirectory() const {
const QString candidate = longestCommonPrefix( fileNames() );
const QFileInfo fi( candidate );
if ( fi.isDir() )
return candidate;
else
return fi.absolutePath();
}
#include "assuanserverconnection.moc"
#include "moc_assuanserverconnection.cpp"
diff --git a/uiserver/signcommand.cpp b/uiserver/signcommand.cpp
index 6520c23c0..92d8eead2 100644
--- a/uiserver/signcommand.cpp
+++ b/uiserver/signcommand.cpp
@@ -1,190 +1,190 @@
/* -*- mode: c++; c-basic-offset:4 -*-
uiserver/signcommand.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 Klarälvdalens Datakonsult AB
Kleopatra is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Kleopatra is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
- your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
+ your version of the file, but you are not obligated to do so. If
your version.
*/
#include <config-kleopatra.h>
#include "signcommand.h"
#include <crypto/signemailcontroller.h>
#include <utils/exception.h>
#include <utils/input.h>
#include <utils/output.h>
#include <KLocale>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace boost;
class SignCommand::Private : public QObject {
Q_OBJECT
private:
friend class ::Kleo::SignCommand;
SignCommand * const q;
public:
explicit Private( SignCommand * qq )
: q( qq ), controller()
{
}
private:
void checkForErrors() const;
private Q_SLOTS:
void slotSignersResolved();
void slotMicAlgDetermined( const QString & );
void slotDone();
void slotError( int, const QString & );
private:
shared_ptr<SignEMailController> controller;
};
SignCommand::SignCommand()
: AssuanCommandMixin<SignCommand>(), d( new Private( this ) )
{
}
SignCommand::~SignCommand() {}
void SignCommand::Private::checkForErrors() const {
if ( q->numFiles() )
throw Exception( makeError( GPG_ERR_CONFLICT ),
i18n( "SIGN is an email mode command, connection seems to be in filemanager mode" ) );
if ( !q->recipients().empty() && !q->informativeRecipients() )
throw Exception( makeError( GPG_ERR_CONFLICT ),
i18n( "RECIPIENT may not be given prior to SIGN, except with --info" ) );
if ( q->inputs().empty() )
throw Exception( makeError( GPG_ERR_ASS_NO_INPUT ),
i18n( "At least one INPUT must be present" ) );
if ( q->outputs().size() != q->inputs().size() )
throw Exception( makeError( GPG_ERR_ASS_NO_INPUT ),
i18n( "INPUT/OUTPUT count mismatch" ) );
if ( !q->messages().empty() )
throw Exception( makeError( GPG_ERR_INV_VALUE ),
i18n( "MESSAGE command is not allowed before SIGN" ) );
}
int SignCommand::doStart() {
d->checkForErrors();
d->controller.reset( new SignEMailController( shared_from_this(), SignEMailController::GpgOLMode ) );
- d->controller->setProtocol( checkProtocol( EMail ) );
+ d->controller->setProtocol( checkProtocol( EMail, AssuanCommand::AllowProtocolMissing ) );
QObject::connect( d->controller.get(), SIGNAL(signersResolved()), d.get(), SLOT(slotSignersResolved() ) );
QObject::connect( d->controller.get(), SIGNAL(reportMicAlg(QString)), d.get(), SLOT(slotMicAlgDetermined(QString)) );
QObject::connect( d->controller.get(), SIGNAL(done()), d.get(), SLOT(slotDone()) );
QObject::connect( d->controller.get(), SIGNAL(error(int,QString)), d.get(), SLOT(slotError(int,QString)) );
d->controller->startResolveSigners( senders() );
return 0;
}
void SignCommand::Private::slotSignersResolved() {
//hold local shared_ptr to member as q->done() deletes *this
const shared_ptr<SignEMailController> cont( controller );
try {
const QString sessionTitle = q->sessionTitle();
if ( !sessionTitle.isNull() ) {
Q_FOREACH ( const shared_ptr<Input> & i, q->inputs() )
i->setLabel( sessionTitle );
Q_FOREACH ( const shared_ptr<Output> & i, q->outputs() )
i->setLabel( sessionTitle );
}
controller->setDetachedSignature( q->hasOption("detached" ) );
controller->setInputsAndOutputs( q->inputs(), q->outputs() );
controller->start();
return;
} catch ( const Exception & e ) {
q->done( e.error(), e.message() );
} catch ( const std::exception & e ) {
q->done( makeError( GPG_ERR_UNEXPECTED ),
i18n("Caught unexpected exception in SignCommand::Private::slotRecipientsResolved: %1",
QString::fromLocal8Bit( e.what() ) ) );
} catch ( ... ) {
q->done( makeError( GPG_ERR_UNEXPECTED ),
i18n("Caught unknown exception in SignCommand::Private::slotRecipientsResolved") );
}
cont->cancel();
}
void SignCommand::Private::slotMicAlgDetermined( const QString & micalg ) {
//hold local shared_ptr to member as q->done() deletes *this
const shared_ptr<SignEMailController> cont( controller );
try {
q->sendStatus( "MICALG", micalg );
return;
} catch ( const Exception & e ) {
q->done( e.error(), e.message() );
} catch ( const std::exception & e ) {
q->done( makeError( GPG_ERR_UNEXPECTED ),
i18n("Caught unexpected exception in SignCommand::Private::slotMicAlgDetermined: %1",
QString::fromLocal8Bit( e.what() ) ) );
} catch ( ... ) {
q->done( makeError( GPG_ERR_UNEXPECTED ),
i18n("Caught unknown exception in SignCommand::Private::slotMicAlgDetermined") );
}
cont->cancel();
}
void SignCommand::Private::slotDone() {
q->done();
}
void SignCommand::Private::slotError( int err, const QString & details ) {
q->done( err, details );
}
void SignCommand::doCanceled() {
if ( d->controller )
d->controller->cancel();
}
#include "signcommand.moc"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:42 PM (16 h, 11 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d7/65/22537f11f7ac2f4dbe176726b1a9

Event Timeline