32 #ifndef QT_NO_CAST_TO_ASCII
33 # define QT_NO_CAST_TO_ASCII
35 #ifndef QT_NO_CAST_FROM_ASCII
36 # define QT_NO_CAST_FROM_ASCII
39 #include <config-kleopatra.h>
40 #include <version-kleopatra.h>
57 #include <kleo/stl_util.h>
58 #include <kleo/exception.h>
60 #include <gpgme++/data.h>
61 #include <gpgme++/key.h>
63 #include <kmime/kmime_header_parsing.h>
66 #include <KLocalizedString>
67 #include <KWindowSystem>
68 #include <KMessageBox>
70 #include <QSocketNotifier>
75 #include <QStringList>
81 #ifndef Q_MOC_RUN // QTBUG-22829
82 #include <boost/type_traits/remove_pointer.hpp>
84 #include <boost/lexical_cast.hpp>
86 #include <boost/bind.hpp>
87 #include <boost/mem_fn.hpp>
88 #include <boost/mpl/if.hpp>
100 # include <ext/algorithm>
105 # include <process.h>
107 # include <sys/types.h>
112 # include <qx11info_x11.h>
113 # include <X11/Xlib.h>
116 using namespace Kleo;
117 using namespace boost;
125 static void my_assuan_release( assuan_context_t ctx ) {
127 assuan_release( ctx );
136 explicit AssuanContext( assuan_context_t ctx ) :
AssuanContextBase( ctx, &assuan_deinit_server ) {}
138 explicit AssuanContext( assuan_context_t ctx ) :
AssuanContextBase( ctx, &my_assuan_release ) {}
142 void reset( assuan_context_t ctx=0 ) { AssuanContextBase::reset( ctx, &assuan_deinit_server ); }
144 void reset( assuan_context_t ctx=0 ) { AssuanContextBase::reset( ctx, &my_assuan_release ); }
149 return assuan_process_done( ctx, assuan_set_error( ctx, err, err_msg ) );
160 static std::map<std::string,std::string>
upcase_option(
const char *
option, std::map<std::string,std::string> options ) {
162 bool value_found =
false;
163 std::map<std::string,std::string>::iterator it = options.begin();
164 while ( it != options.end() )
165 if ( qstricmp( it->first.c_str(),
option ) == 0 ) {
167 options.erase( it++ );
178 std::map<std::string,std::string> result;
180 const char * begin = line;
181 const char * lastEQ = 0;
183 if ( *line ==
' ' || *line ==
'\t' ) {
184 if ( begin != line ) {
185 if ( begin[0] ==
'-' && begin[1] ==
'-' )
187 if ( lastEQ && lastEQ > begin )
193 }
else if ( *line ==
'=' ) {
195 throw Exception( gpg_error( GPG_ERR_ASS_SYNTAX ),
196 i18n(
"No option name given") );
202 if ( begin != line ) {
203 if ( begin[0] ==
'-' && begin[1] ==
'-' )
205 if ( lastEQ && lastEQ > begin )
217 #if defined(Q_OS_WIN32) || defined(_WIN32_WCE)
218 reinterpret_cast<WId
>
222 ( winIdStr.toULongLong( ok, 16 ) );
226 if ( !widget || winIdStr.isEmpty() )
231 kDebug() <<
"window-id value" << wid <<
"doesn't look like a number";
234 if (
QWidget * pw = QWidget::find( wid ) )
235 widget->setParent( pw, widget->windowFlags() );
237 KWindowSystem::setMainWindow( widget, wid );
247 class AssuanServerConnection::Private :
public QObject {
249 friend class ::Kleo::AssuanServerConnection;
250 friend class ::Kleo::AssuanCommandFactory;
251 friend class ::Kleo::AssuanCommand;
258 void startKeyManager();
261 void slotReadActivity(
int ) {
264 if (
const int err = assuan_process_next( ctx.get() ) ) {
267 if (
const int err = assuan_process_next( ctx.get(), &done ) || done ) {
271 if ( nohupedCommands.empty() )
272 bottomHalfDeletion();
280 int startCommandBottomHalf();
284 const std::vector< shared_ptr<AssuanCommand> >::iterator it
285 = std::find_if( nohupedCommands.begin(), nohupedCommands.end(),
287 assert( it != nohupedCommands.end() );
288 nohupedCommands.erase( it );
289 if ( nohupedCommands.empty() && closed )
290 bottomHalfDeletion();
294 if ( !cmd || cmd != currentCommand.get() )
296 currentCommand.reset();
299 void topHalfDeletion() {
300 if ( currentCommand )
301 currentCommand->canceled();
304 CloseHandle( (HANDLE)fd );
305 #elif defined(Q_OS_WIN32)
315 void bottomHalfDeletion() {
319 const QPointer<Private> that =
this;
327 static void reset_handler( assuan_context_t ctx_ ) {
329 static gpg_error_t reset_handler( assuan_context_t ctx_,
char * ) {
331 assert( assuan_get_pointer( ctx_ ) );
333 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
343 static int option_handler( assuan_context_t ctx_,
const char * key,
const char * value ) {
345 static gpg_error_t option_handler( assuan_context_t ctx_,
const char * key,
const char * value ) {
347 assert( assuan_get_pointer( ctx_ ) );
349 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
351 if ( key && key[0] ==
'-' && key[1] ==
'-' )
353 conn.options[key] = QString::fromUtf8( value );
360 static int session_handler( assuan_context_t ctx_,
char * line ) {
362 static gpg_error_t session_handler( assuan_context_t ctx_,
char * line ) {
364 assert( assuan_get_pointer( ctx_ ) );
365 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
367 const QString str = QString::fromUtf8( line );
368 QRegExp rx( QLatin1String(
"(\\d+)(?:\\s+(.*))?" ) );
369 if ( !rx.exactMatch( str ) ) {
370 static const QString errorString = i18n(
"Parse error");
374 if (
const qulonglong
id = rx.cap( 1 ).toULongLong( &ok ) ) {
375 if ( ok &&
id <= std::numeric_limits<unsigned int>::max() ) {
379 static const QString errorString = i18n(
"Parse error: numeric session id too large");
383 if ( !rx.cap( 2 ).isEmpty() )
384 conn.sessionTitle = rx.cap( 2 );
385 kDebug() <<
"session_handler: "
386 <<
"id=" <<
static_cast<unsigned long>( conn.sessionId )
387 <<
", title=" << qPrintable( conn.sessionTitle );
388 return assuan_process_done( ctx_, 0 );
392 static int capabilities_handler( assuan_context_t ctx_,
char * line ) {
394 static gpg_error_t capabilities_handler( assuan_context_t ctx_,
char * line ) {
396 if ( !QByteArray( line ).trimmed().isEmpty() ) {
397 static const QString errorString = i18n(
"CAPABILITIES does not take arguments");
400 static const char capabilities[] =
405 return assuan_process_done( ctx_, assuan_send_data( ctx_, capabilities,
sizeof capabilities - 1 ) );
409 static int getinfo_handler( assuan_context_t ctx_,
char * line ) {
411 static gpg_error_t getinfo_handler( assuan_context_t ctx_,
char * line ) {
413 assert( assuan_get_pointer( ctx_ ) );
414 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
416 if ( qstrcmp( line,
"version" ) == 0 ) {
417 static const char version[] =
"Kleopatra " KLEOPATRA_VERSION_STRING ;
418 return assuan_process_done( ctx_, assuan_send_data( ctx_, version,
sizeof version - 1 ) );
422 if ( qstrcmp( line,
"pid" ) == 0 )
423 ba = QByteArray::number(
mygetpid() );
424 else if ( qstrcmp( line,
"options" ) == 0 )
425 ba = conn.dumpOptions();
426 else if ( qstrcmp( line,
"x-mementos" ) == 0 )
427 ba = conn.dumpMementos();
428 else if ( qstrcmp( line,
"senders" ) == 0 )
429 ba = conn.dumpSenders();
430 else if ( qstrcmp( line,
"recipients" ) == 0 )
431 ba = conn.dumpRecipients();
432 else if ( qstrcmp( line,
"x-files" ) == 0 )
433 ba = conn.dumpFiles();
435 static const QString errorString = i18n(
"Unknown value for WHAT");
438 return assuan_process_done( ctx_, assuan_send_data( ctx_, ba.constData(), ba.size() ) );
442 static int start_keymanager_handler( assuan_context_t ctx_,
char * line ) {
444 static gpg_error_t start_keymanager_handler( assuan_context_t ctx_,
char * line ) {
446 assert( assuan_get_pointer( ctx_ ) );
447 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
449 if ( line && *line ) {
450 static const QString errorString = i18n(
"START_KEYMANAGER does not take arguments");
454 emit conn.q->startKeyManagerRequested();
456 return assuan_process_done( ctx_, 0 );
460 static int start_confdialog_handler( assuan_context_t ctx_,
char * line ) {
462 static gpg_error_t start_confdialog_handler( assuan_context_t ctx_,
char * line ) {
464 assert( assuan_get_pointer( ctx_ ) );
465 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
467 if ( line && *line ) {
468 static const QString errorString = i18n(
"START_CONFDIALOG does not take arguments");
472 emit conn.q->startConfigDialogRequested();
474 return assuan_process_done( ctx_, 0 );
477 template <
bool in>
struct Input_or_Output : mpl::if_c<in,Input,Output> {};
480 template <
bool in,
typename T_memptr>
482 static int IO_handler( assuan_context_t ctx_,
char * line_, T_memptr which ) {
484 static gpg_error_t IO_handler( assuan_context_t ctx_,
char * line_, T_memptr which ) {
486 assert( assuan_get_pointer( ctx_ ) );
487 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
489 char *binOpt = strstr ( line_,
"--binary" );
491 if ( binOpt && !in ) {
495 memset (binOpt,
' ', 8 );
501 if ( options.size() < 1 || options.size() > 2 )
502 throw gpg_error( GPG_ERR_ASS_SYNTAX );
506 if ( options.count(
"FD" ) ) {
508 if ( options.count(
"FILE" ) )
509 throw gpg_error( GPG_ERR_CONFLICT );
515 if ( fdstr.empty() ) {
516 if (
const gpg_error_t err = assuan_receivefd( conn.ctx.get(), &fd ) )
521 #elif defined(Q_OS_WIN32)
528 io = Input_or_Output<in>::type::createFromPipeDevice( fd, in ? i18n(
"Message #%1", (conn.*which).size() + 1 ) : QString() );
530 options.erase(
"FD" );
532 }
else if ( options.count(
"FILE" ) ) {
534 if ( options.count(
"FD" ) )
535 throw gpg_error( GPG_ERR_CONFLICT );
537 const QString filePath = QFile::decodeName( options[
"FILE"].c_str() );
538 if ( filePath.isEmpty() )
539 throw Exception( gpg_error( GPG_ERR_ASS_SYNTAX ), i18n(
"Empty file path") );
540 const QFileInfo fi( filePath );
541 if ( !fi.isAbsolute() )
542 throw Exception( gpg_error( GPG_ERR_INV_ARG ), i18n(
"Only absolute file paths are allowed") );
544 throw Exception( gpg_error( GPG_ERR_INV_ARG ), i18n(
"Only files are allowed in INPUT/OUTPUT FILE") );
546 io = Input_or_Output<in>::type::createFromFile( fi.absoluteFilePath(), true );
548 options.erase(
"FILE" );
552 throw gpg_error( GPG_ERR_ASS_PARAMETER );
556 if ( options.size() )
557 throw gpg_error( GPG_ERR_UNKNOWN_OPTION );
559 (conn.*which).push_back( io );
561 if ( binOpt && !in ) {
564 kDebug() <<
"Configured output for binary data";
567 kDebug() <<
"AssuanServerConnection: added" << io->label();
569 return assuan_process_done( conn.ctx.get(), 0 );
570 }
catch (
const GpgME::Exception & e ) {
572 }
catch (
const std::exception & ) {
573 return assuan_process_done( conn.ctx.get(), gpg_error( GPG_ERR_ASS_SYNTAX ) );
574 }
catch (
const gpg_error_t e ) {
575 return assuan_process_done( conn.ctx.get(), e );
583 static int input_handler( assuan_context_t ctx,
char * line ) {
585 static gpg_error_t input_handler( assuan_context_t ctx,
char * line ) {
587 return IO_handler<true>( ctx, line, &Private::inputs );
591 static int output_handler( assuan_context_t ctx,
char * line ) {
593 static gpg_error_t output_handler( assuan_context_t ctx,
char * line ) {
595 return IO_handler<false>( ctx, line, &Private::outputs );
599 static int message_handler( assuan_context_t ctx,
char * line ) {
601 static gpg_error_t message_handler( assuan_context_t ctx,
char * line ) {
603 return IO_handler<true>( ctx, line, &Private::messages );
607 static int file_handler( assuan_context_t ctx_,
char * line ) {
609 static gpg_error_t file_handler( assuan_context_t ctx_,
char * line ) {
611 assert( assuan_get_pointer( ctx_ ) );
612 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx_ ) );
615 const QFileInfo fi( QFile::decodeName(
hexdecode( line ).c_str() ) );
616 if ( !fi.isAbsolute() )
617 throw Exception( gpg_error( GPG_ERR_INV_ARG ), i18n(
"Only absolute file paths are allowed") );
619 throw gpg_error( GPG_ERR_ENOENT );
620 if ( !fi.isReadable() || ( fi.isDir() && !fi.isExecutable() ) )
621 throw gpg_error( GPG_ERR_EPERM );
623 conn.files.push_back( fi.absoluteFilePath() );
625 return assuan_process_done( conn.ctx.get(), 0 );
626 }
catch (
const Exception & e ) {
628 }
catch (
const gpg_error_t e ) {
629 return assuan_process_done( conn.ctx.get(), e );
631 return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), i18n(
"unknown exception caught").toUtf8().constData() );
635 static bool parse_informative(
const char * & begin, GpgME::Protocol & protocol ) {
636 protocol = GpgME::UnknownProtocol;
637 bool informative =
false;
638 const char * pos = begin;
640 while ( *pos ==
' ' || *pos ==
'\t' )
642 if ( qstrnicmp( pos,
"--info", strlen(
"--info") ) == 0 ) {
644 pos += strlen(
"--info");
649 }
else if ( qstrnicmp( pos,
"--protocol=", strlen(
"--protocol=") ) == 0 ) {
650 pos += strlen(
"--protocol=");
651 if ( qstrnicmp( pos,
"OpenPGP", strlen(
"OpenPGP") ) == 0 ) {
653 pos += strlen(
"OpenPGP");
654 }
else if ( qstrnicmp( pos,
"CMS", strlen(
"CMS") ) == 0 ) {
656 pos += strlen(
"CMS");
660 }
else if ( qstrncmp( pos,
"-- ", strlen(
"-- ") ) == 0 ) {
662 while ( *pos ==
' ' || *pos ==
'\t' )
672 template <
typename T_memptr,
typename T_memptr2>
674 static int recipient_sender_handler( T_memptr mp, T_memptr2 info, assuan_context_t ctx,
char * line,
bool sender=
false ) {
676 static gpg_error_t recipient_sender_handler( T_memptr mp, T_memptr2 info, assuan_context_t ctx,
char * line,
bool sender=
false ) {
678 assert( assuan_get_pointer( ctx ) );
679 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx ) );
681 if ( !line || !*line )
682 return assuan_process_done( conn.ctx.get(), gpg_error( GPG_ERR_INV_ARG ) );
683 const char * begin = line;
684 const char *
const end = begin + qstrlen( line );
685 GpgME::Protocol proto = GpgME::UnknownProtocol;
686 const bool informative = parse_informative( begin, proto );
687 if ( !(conn.*mp).empty() && informative != (conn.*info) )
689 i18n(
"Cannot mix --info with non-info SENDER or RECIPIENT").toUtf8().constData() );
690 KMime::Types::Mailbox mb;
691 if ( !KMime::HeaderParsing::parseMailbox( begin, end, mb ) )
693 i18n(
"Argument is not a valid RFC-2822 mailbox").toUtf8().constData() );
696 i18n(
"Garbage after valid RFC-2822 mailbox detected").toUtf8().constData() );
697 (conn.*info) = informative;
698 (conn.*mp).push_back( mb );
700 const QString
email = mb.addrSpec().asString();
701 (void)assuan_write_line( conn.ctx.get(), qPrintable( QString().sprintf(
"# ok, parsed as \"%s\"", qPrintable( email ) ) ) );
702 if ( sender && !informative )
705 return assuan_process_done( ctx, 0 );
709 static int recipient_handler( assuan_context_t ctx,
char * line ) {
711 static gpg_error_t recipient_handler( assuan_context_t ctx,
char * line ) {
713 return recipient_sender_handler( &Private::recipients, &Private::informativeRecipients, ctx, line );
717 static int sender_handler( assuan_context_t ctx,
char * line ) {
719 static gpg_error_t sender_handler( assuan_context_t ctx,
char * line ) {
721 return recipient_sender_handler( &Private::senders, &Private::informativeSenders, ctx, line,
true );
724 QByteArray dumpOptions()
const {
726 for ( std::map<std::string,QVariant>::const_iterator it = options.begin(), end = options.end() ; it != end ; ++it )
727 result += it->first.c_str() + it->second.toString().toUtf8() +
'\n';
731 static QByteArray dumpStringList(
const QStringList & sl ) {
732 return sl.join( QLatin1String(
"\n" ) ).toUtf8();
735 template <
typename T_container>
736 static QByteArray dumpStringList(
const T_container & c ) {
738 std::copy( c.begin(), c.end(), std::back_inserter( sl ) );
739 return dumpStringList( sl );
742 template <
typename T_container>
743 static QByteArray dumpMailboxes(
const T_container & c ) {
745 std::transform( c.begin(), c.end(),
746 std::back_inserter( sl ),
747 boost::bind( &KMime::Types::Mailbox::prettyAddress, _1 ) );
748 return dumpStringList( sl );
751 QByteArray dumpSenders()
const {
752 return dumpMailboxes( senders );
755 QByteArray dumpRecipients()
const {
756 return dumpMailboxes( recipients );
759 QByteArray dumpMementos()
const {
762 char buf[2 + 2*
sizeof(
void*) + 2];
763 sprintf( buf,
"0x%p\n", (
void* )it->second.get() );
764 buf[
sizeof(buf)-1] =
'\0';
765 result += it->first + QByteArray::fromRawData( buf,
sizeof buf );
770 QByteArray dumpFiles()
const {
771 return dumpStringList( kdtools::copy<QStringList>( files ) );
778 informativeSenders =
false;
780 informativeRecipients =
false;
781 sessionTitle.clear();
785 std::for_each( inputs.begin(), inputs.end(),
788 std::for_each( outputs.begin(), outputs.end(),
791 std::for_each( messages.begin(), messages.end(),
794 bias = GpgME::UnknownProtocol;
800 bool cryptoCommandsEnabled : 1;
801 bool commandWaitingForCryptoCommandsEnabled : 1;
802 bool currentCommandIsNohup : 1;
803 bool informativeSenders;
804 bool informativeRecipients;
805 GpgME::Protocol bias;
806 QString sessionTitle;
807 unsigned int sessionId;
808 std::vector< shared_ptr<QSocketNotifier> > notifiers;
809 std::vector< shared_ptr<AssuanCommandFactory> > factories;
811 std::vector< shared_ptr<AssuanCommand> > nohupedCommands;
812 std::map<std::string,QVariant> options;
813 std::vector<KMime::Types::Mailbox> senders, recipients;
814 std::vector< shared_ptr<Input> > inputs, messages;
815 std::vector< shared_ptr<Output> > outputs;
816 std::vector<QString> files;
817 std::map< QByteArray, shared_ptr<AssuanCommand::Memento> > mementos;
820 void AssuanServerConnection::Private::cleanup() {
821 assert( nohupedCommands.empty() );
823 currentCommand.reset();
824 currentCommandIsNohup =
false;
825 commandWaitingForCryptoCommandsEnabled =
false;
836 cryptoCommandsEnabled( false ),
837 commandWaitingForCryptoCommandsEnabled( false ),
838 currentCommandIsNohup( false ),
839 informativeSenders( false ),
840 informativeRecipients( false ),
841 bias( GpgME::UnknownProtocol ),
843 factories( factories_ )
850 throw Exception( gpg_error( GPG_ERR_INV_ARG ),
"pre-assuan_init_socket_server_ext" );
853 assuan_context_t naked_ctx = 0;
854 if (
const gpg_error_t err = assuan_init_socket_server_ext( &naked_ctx, fd,
INIT_SOCKET_FLAGS ) )
857 assuan_context_t naked_ctx = 0;
858 if (
const gpg_error_t err = assuan_new( &naked_ctx ) )
859 throw Exception( err,
"assuan_new" );
860 ctx.reset( naked_ctx );
862 if (
const gpg_error_t err = assuan_init_socket_server( ctx.get(), fd,
INIT_SOCKET_FLAGS ) )
864 throw Exception( err,
"assuan_init_socket_server_ext" );
867 ctx.reset( naked_ctx ); naked_ctx = 0;
871 assuan_set_pointer( ctx.get(), this );
873 FILE*
const logFile = Log::instance()->logFile();
874 assuan_set_log_stream( ctx.get(), logFile ? logFile : stderr );
879 assert( numFDs != -1 );
881 if ( !numFDs || fds[0] != fd ) {
882 const shared_ptr<QSocketNotifier> sn(
new QSocketNotifier( (intptr_t)fd, QSocketNotifier::Read ), mem_fn( &QObject::deleteLater ) );
883 connect( sn.get(), SIGNAL(activated(
int)),
this, SLOT(slotReadActivity(
int)) );
884 notifiers.push_back( sn );
887 notifiers.reserve( notifiers.size() + numFDs );
888 for (
int i = 0 ; i < numFDs ; ++i ) {
889 const shared_ptr<QSocketNotifier> sn(
new QSocketNotifier( (intptr_t)fds[i], QSocketNotifier::Read ), mem_fn( &QObject::deleteLater ) );
890 connect( sn.get(), SIGNAL(activated(
int)),
this, SLOT(slotReadActivity(
int)) );
891 notifiers.push_back( sn );
897 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"INPUT", input_handler ) )
899 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"INPUT", input_handler,
"" ) )
901 throw Exception( err,
"register \"INPUT\" handler" );
903 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"MESSAGE", message_handler ) )
905 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"MESSAGE", message_handler,
"" ) )
907 throw Exception( err,
"register \"MESSAGE\" handler" );
909 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"OUTPUT", output_handler ) )
911 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"OUTPUT", output_handler,
"" ) )
913 throw Exception( err,
"register \"OUTPUT\" handler" );
915 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"FILE", file_handler ) )
917 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"FILE", file_handler,
"" ) )
919 throw Exception( err,
"register \"FILE\" handler" );
925 if (
const gpg_error_t err = assuan_register_command( ctx.get(), fac->name(), fac->_handler() ) )
927 if (
const gpg_error_t err = assuan_register_command( ctx.get(), fac->name(), fac->_handler(),
"" ) )
929 throw Exception( err,
std::string(
"register \"" ) + fac->name() +
"\" handler" );
932 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"GETINFO", getinfo_handler ) )
934 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"GETINFO", getinfo_handler,
"" ) )
936 throw Exception( err,
"register \"GETINFO\" handler" );
938 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"START_KEYMANAGER", start_keymanager_handler ) )
940 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"START_KEYMANAGER", start_keymanager_handler,
"" ) )
942 throw Exception( err,
"register \"START_KEYMANAGER\" handler" );
944 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"START_CONFDIALOG", start_confdialog_handler ) )
946 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"START_CONFDIALOG", start_confdialog_handler,
"" ) )
948 throw Exception( err,
"register \"START_CONFDIALOG\" handler" );
950 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"RECIPIENT", recipient_handler ) )
952 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"RECIPIENT", recipient_handler,
"" ) )
954 throw Exception( err,
"register \"RECIPIENT\" handler" );
956 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"SENDER", sender_handler ) )
958 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"SENDER", sender_handler,
"" ) )
960 throw Exception( err,
"register \"SENDER\" handler" );
962 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"SESSION", session_handler ) )
964 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"SESSION", session_handler,
"" ) )
966 throw Exception( err,
"register \"SESSION\" handler" );
968 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"CAPABILITIES", capabilities_handler ) )
970 if (
const gpg_error_t err = assuan_register_command( ctx.get(),
"CAPABILITIES", capabilities_handler,
"" ) )
972 throw Exception( err,
"register \"CAPABILITIES\" handler" );
974 assuan_set_hello_line( ctx.get(),
"GPG UI server (Kleopatra/" KLEOPATRA_VERSION_STRING
") ready to serve" );
979 if (
const gpg_error_t err = assuan_register_reset_notify( ctx.get(), reset_handler ) )
980 throw Exception( err,
"register reset notify" );
981 if (
const gpg_error_t err = assuan_register_option_handler( ctx.get(), option_handler ) )
982 throw Exception( err,
"register option handler" );
987 if (
const gpg_error_t err = assuan_accept( ctx.get() ) )
988 throw Exception( err,
"assuan_accept" );
991 AssuanServerConnection::Private::~Private() {
996 : QObject( p ),
d( new Private( fd, factories, this ) )
1004 if ( on == d->cryptoCommandsEnabled )
1006 d->cryptoCommandsEnabled = on;
1007 if ( d->commandWaitingForCryptoCommandsEnabled )
1008 QTimer::singleShot( 0, d.
get(), SLOT(startCommandBottomHalf()) );
1020 class InquiryHandler :
public QObject {
1024 #if defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT)
1025 explicit InquiryHandler(
const char * keyword_, QObject * p=0 )
1027 # if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT)
1036 # if defined(HAVE_ASSUAN2) || defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT)
1037 # ifndef HAVE_ASSUAN2
1038 static int handler(
void * cb_data,
int rc,
unsigned char * buffer,
size_t buflen )
1040 static gpg_error_t handler(
void * cb_data, gpg_error_t rc,
unsigned char * buffer,
size_t buflen )
1044 InquiryHandler * this_ =
static_cast<InquiryHandler*
>(cb_data);
1045 emit this_->signal( rc, QByteArray::fromRawData( reinterpret_cast<const char*>(buffer), buflen ), this_->keyword );
1046 std::free( buffer );
1051 static int handler(
void * cb_data,
int rc )
1054 InquiryHandler * this_ =
static_cast<InquiryHandler*
>(cb_data);
1055 emit this_->signal( rc, QByteArray::fromRawData( reinterpret_cast<const char*>(this_->buffer), this_->buflen ), this_->keyword );
1056 std::free( this_->buffer );
1063 #if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT)
1064 friend class ::Kleo::AssuanCommand;
1065 unsigned char * buffer;
1068 const char * keyword;
1069 #endif // defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT)
1072 void signal(
int rc,
const QByteArray & data,
const QByteArray & keyword );
1077 class AssuanCommand::Private {
1082 bias( GpgME::UnknownProtocol ),
1089 std::map<std::string,QVariant>
options;
1091 std::vector< shared_ptr<Output> >
outputs;
1092 std::vector<QString> files;
1095 GpgME::Protocol bias;
1098 QByteArray utf8ErrorKeepAlive;
1116 if (
const int err = doStart() )
1120 }
catch (
const Exception & e ) {
1122 done( e.error_code(), e.message() );
1124 }
catch (
const GpgME::Exception & e ) {
1126 done( e.error(), QString::fromLocal8Bit( e.message().c_str() ) );
1128 }
catch (
const std::exception & e ) {
1130 done(
makeError( GPG_ERR_INTERNAL ), i18n(
"Caught unexpected exception: %1", QString::fromLocal8Bit( e.what() ) ) );
1134 done(
makeError( GPG_ERR_INTERNAL ), i18n(
"Caught unknown exception - please report this error to the developers." ) );
1150 return d->options.count( opt );
1154 const std::map<std::string,QVariant>::const_iterator it = d->options.find( opt );
1155 if ( it == d->options.end() )
1166 template <
typename U,
typename V>
1167 std::vector<U> keys(
const std::map<U,V> & map ) {
1168 std::vector<U> result;
1169 result.resize( map.size() );
1170 for (
typename std::map<U,V>::const_iterator it = map.begin(), end = map.end() ; it != end ; ++it )
1171 result.push_back( it->first );
1176 const std::map< QByteArray, shared_ptr<AssuanCommand::Memento> > & AssuanCommand::mementos()
const {
1178 assert( assuan_get_pointer( d->ctx.
get() ) );
1179 const AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( d->ctx.
get() ) );
1180 return conn.mementos;
1184 if (
const unsigned int id =
sessionId() )
1187 return mementos().count( tag );
1191 if (
const unsigned int id =
sessionId() ) {
1194 const std::map< QByteArray, shared_ptr<Memento> >::const_iterator it = sd->mementos.find( tag );
1195 if ( it != sd->mementos.end() )
1198 const std::map< QByteArray, shared_ptr<Memento> >::const_iterator it = mementos().find( tag );
1199 if ( it == mementos().end() )
1206 const QByteArray tag = QByteArray::number( reinterpret_cast<qulonglong>( mem.get() ), 36 );
1212 assert( assuan_get_pointer( d->ctx.
get() ) );
1213 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( d->ctx.
get() ) );
1215 if (
const unsigned int id =
sessionId() )
1218 conn.mementos[tag] = mem;
1224 assert( assuan_get_pointer( d->ctx.
get() ) );
1225 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( d->ctx.
get() ) );
1227 conn.mementos.erase( tag );
1228 if (
const unsigned int id =
sessionId() )
1245 return kdtools::copy<QStringList>( d->files );
1249 return d->files.size();
1259 if (
const int err = assuan_write_status( d->ctx.
get(), keyword, text.c_str() ) )
1260 throw Exception( err, i18n(
"Cannot send \"%1\" status", QString::fromLatin1( keyword ) ) );
1266 if (
const gpg_error_t err = assuan_send_data( d->ctx.
get(), data.constData(), data.size() ) )
1267 throw Exception( err, i18n(
"Cannot send data" ) );
1269 if (
const gpg_error_t err = assuan_send_data( d->ctx.
get(), 0, 0 ) )
1270 throw Exception( err, i18n(
"Cannot flush data" ) );
1281 #if defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT)
1282 std::auto_ptr<InquiryHandler> ih(
new InquiryHandler( keyword, receiver ) );
1283 receiver->connect( ih.get(), SIGNAL(
signal(
int,QByteArray,QByteArray)),
slot );
1284 if (
const gpg_error_t err = assuan_inquire_ext( d->ctx.
get(), keyword,
1285 # if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT)
1286 &ih->buffer, &ih->buflen,
1288 maxSize, InquiryHandler::handler, ih.get() ) )
1293 return makeError( GPG_ERR_NOT_SUPPORTED );
1294 #endif // defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT)
1298 if ( d->ctx && !d->done && !details.isEmpty() ) {
1299 kDebug() <<
"Error: " << details;
1300 d->utf8ErrorKeepAlive = details.toUtf8();
1302 assuan_set_error( d->ctx.
get(), err.encodedError(), d->utf8ErrorKeepAlive.constData() );
1309 kDebug() << err.asString() <<
": called with NULL ctx.";
1313 kDebug() << err.asString() <<
": called twice!";
1319 std::for_each( d->messages.begin(), d->messages.end(),
1321 std::for_each( d->inputs.begin(), d->inputs.end(),
1323 std::for_each( d->outputs.begin(), d->outputs.end(),
1325 d->messages.clear();
1331 assert( assuan_get_pointer( d->ctx.
get() ) );
1332 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( d->ctx.
get() ) );
1335 conn.nohupDone(
this );
1339 const gpg_error_t rc = assuan_process_done( d->ctx.
get(), err.encodedError() );
1340 if ( gpg_err_code( rc ) != GPG_ERR_NO_ERROR )
1341 qFatal(
"AssuanCommand::done: assuan_process_done returned error %d (%s)",
1342 static_cast<int>(rc), gpg_strerror(rc) );
1344 d->utf8ErrorKeepAlive.clear();
1346 conn.commandDone(
this );
1363 return d->sessionTitle;
1367 return d->sessionId;
1371 return d->informativeSenders;
1375 return d->informativeRecipients;
1379 return d->recipients;
1386 #ifndef HAVE_ASSUAN2
1391 assert( assuan_get_pointer( ctx ) );
1392 AssuanServerConnection::Private & conn = *
static_cast<AssuanServerConnection::Private*
>( assuan_get_pointer( ctx ) );
1396 const std::vector< shared_ptr<AssuanCommandFactory> >::const_iterator it
1400 kleo_assert( qstricmp( (*it)->name(), commandName ) == 0 );
1405 cmd->d->ctx = conn.ctx;
1406 cmd->d->options = conn.options;
1407 cmd->d->inputs.swap( conn.inputs );
kleo_assert( conn.inputs.empty() );
1408 cmd->d->messages.swap( conn.messages );
kleo_assert( conn.messages.empty() );
1409 cmd->d->outputs.swap( conn.outputs );
kleo_assert( conn.outputs.empty() );
1410 cmd->d->files.swap( conn.files );
kleo_assert( conn.files.empty() );
1411 cmd->d->senders.swap( conn.senders );
kleo_assert( conn.senders.empty() );
1412 cmd->d->recipients.swap( conn.recipients );
kleo_assert( conn.recipients.empty() );
1413 cmd->d->informativeRecipients = conn.informativeRecipients;
1414 cmd->d->informativeSenders = conn.informativeSenders;
1415 cmd->d->bias = conn.bias;
1416 cmd->d->sessionTitle = conn.sessionTitle;
1417 cmd->d->sessionId = conn.sessionId;
1419 const std::map<std::string,std::string> cmdline_options =
parse_commandline( line );
1420 for ( std::map<std::string,std::string>::const_iterator it = cmdline_options.begin(), end = cmdline_options.end() ; it != end ; ++it )
1421 cmd->d->options[it->first] = QString::fromUtf8( it->second.c_str() );
1424 if ( cmd->d->options.count(
"nohup" ) ) {
1425 if ( !cmd->d->options[
"nohup"].toString().isEmpty() )
1426 return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_ASS_PARAMETER ),
"--nohup takes no argument" );
1428 cmd->d->options.erase(
"nohup" );
1431 conn.currentCommand = cmd;
1432 conn.currentCommandIsNohup = nohup;
1434 QTimer::singleShot( 0, &conn, SLOT(startCommandBottomHalf()) );
1438 }
catch (
const Exception & e ) {
1440 }
catch (
const std::exception & e ) {
1443 return assuan_process_done_msg( conn.ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), i18n(
"Caught unknown exception") );
1447 int AssuanServerConnection::Private::startCommandBottomHalf() {
1449 commandWaitingForCryptoCommandsEnabled = currentCommand && !cryptoCommandsEnabled;
1451 if ( !cryptoCommandsEnabled )
1458 currentCommand.reset();
1460 const bool nohup = currentCommandIsNohup;
1461 currentCommandIsNohup =
false;
1465 if (
const int err = cmd->start() ) {
1466 if ( cmd->isDone() )
1469 return assuan_process_done( ctx.get(), err );
1472 if ( cmd->isDone() )
1476 cmd->setNohup(
true );
1477 nohupedCommands.push_back( cmd );
1478 return assuan_process_done_msg( ctx.get(), 0,
"Command put in the background to continue executing after connection end." );
1480 currentCommand = cmd;
1484 }
catch (
const Exception & e ) {
1486 }
catch (
const std::exception & e ) {
1489 return assuan_process_done_msg( ctx.get(), gpg_error( GPG_ERR_UNEXPECTED ), i18n(
"Caught unknown exception") );
1509 if ( !hasOption(
"mode" ) )
1510 throw Exception( makeError( GPG_ERR_MISSING_VALUE ), i18n(
"Required --mode option missing" ) );
1512 const QString modeString =
option(
"mode").toString().toLower();
1513 if ( modeString == QLatin1String(
"filemanager" ) )
1515 if ( modeString == QLatin1String(
"email" ) )
1517 throw Exception( makeError( GPG_ERR_INV_ARG ), i18n(
"invalid mode: \"%1\"", modeString ) );
1534 if ( !hasOption(
"protocol") )
1535 if (
d->bias != GpgME::UnknownProtocol )
1538 throw Exception( makeError( GPG_ERR_MISSING_VALUE ), i18n(
"Required --protocol option missing" ) );
1540 return GpgME::UnknownProtocol;
1543 throw Exception( makeError( GPG_ERR_INV_FLAG ), i18n(
"--protocol is not allowed here") );
1545 const QString protocolString =
option(
"protocol").toString().toLower();
1546 if ( protocolString == QLatin1String(
"openpgp" ) )
1548 if ( protocolString == QLatin1String(
"cms" ) )
1550 throw Exception( makeError( GPG_ERR_INV_ARG ), i18n(
"invalid protocol \"%1\"", protocolString ) );
1553 void AssuanCommand::doApplyWindowID(
QWidget * widget )
const {
1554 if ( !widget || !hasOption(
"window-id" ) )
1563 #include "assuanserverconnection.moc"
1564 #include "moc_assuanserverconnection.cpp"
static const int FOR_READING
static const unsigned int MAX_ACTIVE_FDS
shared_ptr< remove_pointer< assuan_context_t >::type > AssuanContextBase
static void apply_window_id(QWidget *widget, const QString &winIdStr)
~AssuanServerConnection()
bool hasMemento(const QByteArray &tag) const
virtual void finalize()=0
static std::map< std::string, std::string > parse_commandline(const char *line)
static std::string email(const UserID &uid)
static gpg_error_t assuan_process_done_msg(assuan_context_t ctx, gpg_error_t err, const char *err_msg)
void sendStatusEncoded(const char *keyword, const std::string &text)
boost::shared_ptr< Memento > memento(const QByteArray &tag) const
static std::map< std::string, std::string > upcase_option(const char *option, std::map< std::string, std::string > options)
void removeMemento(const QByteArray &tag)
static const unsigned int INIT_SOCKET_FLAGS
QStringList fileNames() const
const std::vector< KMime::Types::Mailbox > & senders() const
void sendData(const QByteArray &data, bool moreToCome=false)
static WId wid_from_string(const QString &winIdStr, bool *ok=0)
static boost::shared_ptr< SessionDataHandler > instance()
int inquire(const char *keyword, QObject *receiver, const char *slot, unsigned int maxSize=0)
const std::vector< boost::shared_ptr< Output > > & outputs() const
#define ASSUAN_INVALID_FD
QVariant option(const char *opt) const
QString sessionTitle() const
void enableCryptoCommands(bool enable=true)
void sendStatus(const char *keyword, const QString &text)
const std::vector< KMime::Types::Mailbox > & recipients() const
static int _handle(assuan_context_s *, char *, const char *)
virtual void setBinaryOpt(bool value)=0
static int makeError(int code)
int makeGnuPGError(int code)
unsigned int numFiles() const
#define kleo_assert(cond)
const std::vector< boost::shared_ptr< Input > > & inputs() const
Base class for GnuPG UI Server commands.
void done(const GpgME::Error &err=GpgME::Error())
bool hasOption(const char *opt) const
QByteArray registerMemento(const boost::shared_ptr< Memento > &mem)
GpgME::Protocol checkProtocol(Mode mode, int options=0) const
unsigned int sessionId() const
bool informativeSenders() const
bool informativeRecipients() const
const std::map< std::string, QVariant > & options() const
std::string hexdecode(const char *s)
const std::vector< boost::shared_ptr< Input > > & messages() const