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

kleopatra

command.cpp

Go to the documentation of this file.
00001 /* -*- mode: c++; c-basic-offset:4 -*-
00002     command.cpp
00003 
00004     This file is part of KleopatraClient, the Kleopatra interface library
00005     Copyright (c) 2008 Klarälvdalens Datakonsult AB
00006 
00007     KleopatraClient is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU Library General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     KleopatraClient is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020 */
00021 
00022 #include <config-kleopatra.h>
00023 
00024 #include "command.h"
00025 #include "command_p.h"
00026 
00027 #include <QtGlobal> // Q_OS_WIN
00028 
00029 #ifdef Q_OS_WIN // HACK: AllowSetForegroundWindow needs _WIN32_WINDOWS >= 0x0490 set
00030 # ifndef _WIN32_WINDOWS
00031 #  define _WIN32_WINDOWS 0x0500
00032 #  define _WIN32_WINNT 0x0500 // good enough for Vista too
00033 # endif
00034 # include <utils/gnupg-registry.h>
00035 # include <windows.h>
00036 #endif
00037 
00038 #include <QMutexLocker>
00039 #include <QFile>
00040 #include <QDebug>
00041 #include <QDir>
00042 
00043 #include <assuan.h>
00044 #include <gpg-error.h>
00045 
00046 #include <boost/shared_ptr.hpp>
00047 #include <boost/type_traits/remove_pointer.hpp>
00048 
00049 #include <algorithm>
00050 #include <string>
00051 
00052 using namespace KLEOPATRACLIENT_NAMESPACE;
00053 using namespace boost;
00054 
00055 // copied from kleopatra/utils/hex.cpp
00056 static std::string hexencode( const std::string & in ) {
00057     std::string result;
00058     result.reserve( 3 * in.size() );
00059 
00060     static const char hex[] = "0123456789ABCDEF";
00061 
00062     for ( std::string::const_iterator it = in.begin(), end = in.end() ; it != end ; ++it )
00063         switch ( const unsigned char ch = *it ) {
00064         default:
00065             if ( ch >= '!' && ch <= '~' || ch > 0xA0 ) {
00066                 result += ch;
00067                 break;
00068             }
00069             // else fall through
00070         case ' ':
00071             result += '+';
00072             break;
00073         case '"':
00074         case '#':
00075         case '$':
00076         case '%':
00077         case '\'':
00078         case '+':
00079         case '=':
00080             result += '%';
00081             result += hex[ (ch & 0xF0) >> 4 ];
00082             result += hex[ (ch & 0x0F)      ];
00083             break;
00084         }
00085     
00086     return result;
00087 }
00088 
00089 #ifdef UNUSED
00090 static std::string hexencode( const char * in ) {
00091     if ( !in )
00092         return std::string();
00093     return hexencode( std::string( in ) );
00094 }
00095 #endif
00096 
00097 static QByteArray hexencode( const QByteArray & in ) {
00098     if ( in.isNull() )
00099         return QByteArray();
00100     const std::string result = hexencode( std::string( in.constData() ) );
00101     return QByteArray( result.data(), result.size() );
00102 }
00103 // end copied from kleopatra/utils/hex.cpp
00104 
00105 Command::Command( QObject * p )
00106     : QObject( p ), d( new Private( this ) )
00107 {
00108     d->init();
00109 }
00110 
00111 Command::Command( Private * pp, QObject * p )
00112     : QObject( p ), d( pp )
00113 {
00114     d->init();
00115 }
00116 
00117 Command::~Command() {
00118     delete d; d = 0;
00119 }
00120 
00121 void Command::Private::init() {
00122     connect( this, SIGNAL(started()),  q, SIGNAL(started())  );
00123     connect( this, SIGNAL(finished()), q, SIGNAL(finished()) );
00124 }
00125 
00126 void Command::setParentWId( WId wid ) {
00127     const QMutexLocker locker( &d->mutex );
00128     d->inputs.parentWId = wid;
00129 }
00130 
00131 WId Command::parentWId() const {
00132     const QMutexLocker locker( &d->mutex );
00133     return d->inputs.parentWId;
00134 }
00135 
00136 
00137 void Command::setServerLocation( const QString & location ) {
00138     const QMutexLocker locker( &d->mutex );
00139     d->outputs.serverLocation = location;
00140 }
00141 
00142 QString Command::serverLocation() const {
00143     const QMutexLocker locker( &d->mutex );
00144     return d->outputs.serverLocation;
00145 }
00146 
00147 
00148 bool Command::waitForFinished() {
00149     return d->wait();
00150 }
00151 
00152 bool Command::waitForFinished( unsigned long ms ) {
00153     return d->wait( ms );
00154 }
00155 
00156 
00157 bool Command::error() const {
00158     const QMutexLocker locker( &d->mutex );
00159     return !d->outputs.errorString.isEmpty();
00160 }
00161 
00162 bool Command::wasCanceled() const {
00163     const QMutexLocker locker( &d->mutex );
00164     return d->outputs.canceled;
00165 }
00166 
00167 QString Command::errorString() const {
00168     const QMutexLocker locker( &d->mutex );
00169     return d->outputs.errorString;
00170 }
00171 
00172 
00173 qint64 Command::serverPid() const {
00174     const QMutexLocker locker( &d->mutex );
00175     return d->outputs.serverPid;
00176 }
00177 
00178 
00179 void Command::start() {
00180     d->start();
00181 }
00182 
00183 void Command::cancel() {
00184     qDebug( "Sorry, not implemented: KleopatraClient::Command::Cancel" );
00185 }
00186 
00187 
00188 void Command::setOptionValue( const char * name, const QVariant & value, bool critical ) {
00189     if ( !name || !*name )
00190         return;
00191     const QMutexLocker locker( &d->mutex );
00192     const Private::Option opt = {
00193         value,
00194         true,
00195         critical
00196     };
00197     d->inputs.options[name] = opt;
00198 }
00199 
00200 QVariant Command::optionValue( const char * name ) const {
00201     if ( !name || !*name )
00202         return QVariant();
00203     const QMutexLocker locker( &d->mutex );
00204 
00205     const std::map<std::string,Private::Option>::const_iterator it = d->inputs.options.find( name );
00206     if ( it == d->inputs.options.end() )
00207         return QVariant();
00208     else
00209         return it->second.value;
00210 }
00211 
00212 
00213 void Command::setOption( const char * name, bool critical ) {
00214     if ( !name || !*name )
00215         return;
00216     const QMutexLocker locker( &d->mutex );
00217 
00218     if ( isOptionSet( name ) )
00219         unsetOption( name );
00220 
00221     const Private::Option opt = {
00222         QVariant(),
00223         false,
00224         critical
00225     };
00226 
00227     d->inputs.options[name] = opt;
00228 }
00229 
00230 void Command::unsetOption( const char * name ) {
00231     if ( !name || !*name )
00232         return;
00233     const QMutexLocker locker( &d->mutex );
00234     d->inputs.options.erase( name );
00235 }
00236 
00237 bool Command::isOptionSet( const char * name ) const {
00238     if ( !name || !*name )
00239         return false;
00240     const QMutexLocker locker( &d->mutex );
00241     return d->inputs.options.count( name );
00242 }
00243 
00244 bool Command::isOptionCritical( const char * name ) const {
00245     if ( !name || !*name )
00246         return false;
00247     const QMutexLocker locker( &d->mutex );
00248     const std::map<std::string,Private::Option>::const_iterator it = d->inputs.options.find( name );
00249     return it != d->inputs.options.end() && it->second.isCritical;
00250 }
00251 
00252 void Command::setFilePaths( const QStringList & filePaths ) {
00253     const QMutexLocker locker( &d->mutex );
00254     d->inputs.filePaths = filePaths;
00255 }
00256 
00257 QStringList Command::filePaths() const {
00258     const QMutexLocker locker( &d->mutex );
00259     return d->inputs.filePaths;
00260 }
00261 
00262 QByteArray Command::receivedData() const {
00263     const QMutexLocker locker( &d->mutex );
00264     return d->outputs.data;
00265 }
00266 
00267 
00268 void Command::setCommand( const char * command ) {
00269     const QMutexLocker locker( &d->mutex );
00270     d->inputs.command = command;
00271 }
00272 
00273 QByteArray Command::command() const {
00274     const QMutexLocker locker( &d->mutex );
00275     return d->inputs.command;
00276 }
00277 
00278 //
00279 // here comes the ugly part
00280 //
00281 
00282 typedef shared_ptr< remove_pointer<assuan_context_t>::type > AssuanContextBase;
00283 namespace {
00284     struct AssuanClientContext : AssuanContextBase {
00285         AssuanClientContext() : AssuanContextBase() {}
00286         explicit AssuanClientContext( assuan_context_t ctx ) : AssuanContextBase( ctx, &assuan_disconnect ) {}
00287         void reset( assuan_context_t ctx=0 ) { AssuanContextBase::reset( ctx, &assuan_disconnect ); }
00288     };
00289 }
00290 
00291 static assuan_error_t
00292 my_assuan_transact( const AssuanClientContext & ctx,
00293                     const char *command,
00294                     int (*data_cb)( void *, const void *, size_t )=0,
00295                     void * data_cb_arg=0,
00296                     int (*inquire_cb)( void *, const char * )=0,
00297                     void * inquire_cb_arg=0,
00298                     int (*status_cb)( void *, const char * )=0,
00299                     void * status_cb_arg=0)
00300 {
00301     return assuan_transact( ctx.get(), command, data_cb, data_cb_arg, inquire_cb, inquire_cb_arg, status_cb, status_cb_arg );
00302 }
00303 
00304 static QString to_error_string( int err ) {
00305     char buffer[1024];
00306     gpg_strerror_r( static_cast<gpg_error_t>(err), 
00307                     buffer, sizeof buffer );
00308     buffer[sizeof buffer - 1] = '\0';
00309     return QString::fromLocal8Bit( buffer );
00310 }
00311 
00312 static QString gnupg_home_directory() {
00313 #ifdef Q_OS_WIN
00314     return QFile::decodeName( default_homedir() );
00315 #else
00316     const QByteArray gnupgHome = qgetenv( "GNUPGHOME" );
00317     if ( !gnupgHome.isEmpty() )
00318         return QFile::decodeName( gnupgHome );
00319     else
00320         return QDir::homePath() + QLatin1String( "/.gnupg" );
00321 #endif
00322 }
00323 
00324 static QString get_default_socket_name() {
00325     const QString homeDir = gnupg_home_directory();
00326     if ( homeDir.isEmpty() )
00327         return QString();
00328     return QDir( homeDir ).absoluteFilePath( QLatin1String( "S.uiserver" ) );
00329 }
00330 
00331 static QString default_socket_name() {
00332     static QString name = get_default_socket_name();
00333     return name;
00334 }
00335 
00336 static QString start_uiserver() {
00337     return Command::tr("start_uiserver: not yet implemented");
00338 }
00339 
00340 static int getinfo_pid_cb( void * opaque, const void * buffer, size_t length ) {
00341     qint64 & pid = *static_cast<qint64*>( opaque );
00342     pid = QByteArray( static_cast<const char*>( buffer ), length ).toLongLong();
00343     return 0;
00344 }
00345 
00346 static int command_data_cb( void * opaque, const void * buffer, size_t length ) {
00347     QByteArray & ba = *static_cast<QByteArray*>( opaque );
00348     ba.append( QByteArray( static_cast<const char*>(buffer), length ) );
00349     return 0;
00350 }
00351 
00352 static int send_option( const AssuanClientContext & ctx, const char * name, const QVariant & value ) {
00353     if ( value.isValid() )
00354         return my_assuan_transact( ctx, QString().sprintf( "OPTION %s=%s", name, value.toString().toUtf8().constData() ).toUtf8().constData() );
00355     else
00356         return my_assuan_transact( ctx, QString().sprintf( "OPTION %s", name ).toUtf8().constData() );
00357 }
00358 
00359 static int send_file( const AssuanClientContext & ctx, const QString & file ) {
00360     return my_assuan_transact( ctx, QString().sprintf( "FILE %s", hexencode( QFile::encodeName( file ) ).constData() ).toUtf8().constData() );
00361 }
00362 
00363 void Command::Private::run() {
00364 
00365     // Take a snapshot of the input data, and clear the output data:
00366     Inputs in;
00367     Outputs out;
00368     {
00369         const QMutexLocker locker( &mutex );
00370         in = inputs;
00371         outputs = out;
00372     }
00373 
00374     out.canceled = false;
00375 
00376     int err = 0;
00377 
00378     assuan_context_t naked_ctx = 0;
00379     AssuanClientContext ctx;
00380 
00381     if ( out.serverLocation.isEmpty() )
00382         out.serverLocation = default_socket_name();
00383 
00384     const QString socketName = out.serverLocation;
00385     if ( socketName.isEmpty() ) {
00386         out.errorString = tr("Invalid socket name!");
00387         goto leave;
00388     }
00389 
00390     err = assuan_socket_connect( &naked_ctx, QFile::encodeName( socketName ).constData(), -1 );
00391     if ( err ) {
00392         qDebug( "UI server not running, starting it" );
00393         
00394         const QString errorString = start_uiserver();
00395         if ( !errorString.isEmpty() ) {
00396             out.errorString = errorString;
00397             goto leave;
00398         }
00399 
00400         // give it a bit of time to start up and try a couple of times
00401         for ( int i = 0 ; err && i < 20 ; ++i ) {
00402             msleep( 500 );
00403             err = assuan_socket_connect( &naked_ctx, QFile::encodeName( socketName ).constData(), -1 );
00404         }
00405     }
00406 
00407     if ( err ) {
00408         out.errorString = tr( "Could not connect to Kleopatra UI server at %1: %2" )
00409             .arg( socketName, to_error_string( err ) );
00410         goto leave;
00411     }
00412 
00413     ctx.reset( naked_ctx );
00414     naked_ctx = 0;
00415 
00416     out.serverPid = -1;
00417     err = my_assuan_transact( ctx, "GETINFO pid", &getinfo_pid_cb, &out.serverPid );
00418     if ( err || out.serverPid <= 0 ) {
00419         out.errorString = tr( "Could not get the process-id of the Kleopatra UI server at %1: %2" )
00420             .arg( socketName, to_error_string( err ) );
00421         goto leave;
00422     }
00423 
00424     qDebug() << "Server PID =" << out.serverPid;
00425 
00426 #ifdef Q_OS_WIN
00427     if ( !AllowSetForegroundWindow( (pid_t)out.serverPid ) )
00428         qDebug() << "AllowSetForegroundWindow(" << out.serverPid << ") failed: " << GetLastError();
00429 #endif
00430 
00431     if ( in.command.isEmpty() )
00432         goto leave;
00433 
00434     if ( in.parentWId ) {
00435 #ifdef Q_OS_WIN32
00436         err = send_option( ctx, "window-id", QString().sprintf( "%lx", reinterpret_cast<unsigned long>( in.parentWId ) ) );
00437 #else
00438         err = send_option( ctx, "window-id", QString().sprintf( "%lx", static_cast<unsigned long>( in.parentWId ) ) );
00439 #endif
00440         if ( err )
00441             qDebug( "sending option window-id failed - ignoring" );
00442     }
00443 
00444     for ( std::map<std::string,Option>::const_iterator it = in.options.begin(), end = in.options.end() ; it != end ; ++it )
00445         if ( ( err = send_option( ctx, it->first.c_str(), it->second.hasValue ? it->second.value.toString() : QVariant() ) ) )
00446             if ( it->second.isCritical ) {
00447                 out.errorString = tr("Failed to send critical option %1: %2")
00448                     .arg( QString::fromLatin1( it->first.c_str() ), to_error_string( err ) );
00449                 goto leave;
00450             } else {
00451                 qDebug() << "Failed to send non-critical option" << it->first.c_str() << ":" << to_error_string( err );
00452             }
00453 
00454     Q_FOREACH( const QString & filePath, in.filePaths )
00455         if ( ( err = send_file( ctx, filePath ) ) ) {
00456             out.errorString = tr("Failed to send file path %1: %2")
00457                 .arg( filePath, to_error_string( err ) );
00458             goto leave;
00459         }
00460 
00461 #if 0
00462     setup I/O;
00463 #endif
00464 
00465     err = my_assuan_transact( ctx, in.command.constData(), &command_data_cb, &out.data );
00466     if ( err ) {
00467         if ( gpg_err_code( err ) == GPG_ERR_CANCELED )
00468             out.canceled = true;
00469         else
00470             out.errorString = tr( "Command (%1) failed: %2" )
00471                 .arg( QString::fromLatin1( in.command.constData() ) ).arg( to_error_string( err ) );
00472         goto leave;
00473     }
00474                                     
00475 
00476  leave:
00477     const QMutexLocker locker( &mutex );
00478     // copy outputs to where Command can see them:
00479     outputs = out;
00480 }
00481 
00482 #include "moc_command_p.cpp"
00483 #include "moc_command.cpp"

kleopatra

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

kdepim

Skip menu "kdepim"
  • akonadi
  •   clients
  •   kabc
  •   kcal
  •   kcm
  • akregator
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt
  • kdgantt1
  • kjots
  • kleopatra
  • kmail
  • kmobiletools
  • knode
  • knotes
  • kontact
  • kontactinterfaces
  • korganizer
  •   korgac
  • kpilot
  • ktimetracker
  •   doc
  • libkdepim
  • libkholidays
  • libkleo
  • libkpgp
  • maildir
Generated for kdepim by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal