/**
 * cryptplugwrapper.cpp
 *
 * Copyright (c) 2001 Karl-Heinz Zimmer, Klaraelvdalens Datakonsult AB
 *
 * This CRYPTPLUG wrapper implementation is based on cryptplug.h by
 * Karl-Heinz Zimmer which is based on 'The Aegypten Plugin API' as
 * specified by Matthias Kalle Dalheimer, Klaraelvdalens Datakonsult AB,
 * see file mua-integration.sgml located on Aegypten CVS:
 *          http://www.gnupg.org/aegypten/development.en.html
 *
 * purpose: Wrap up all Aegypten Plugin API functions in one C++ class
 *          for usage by KDE programs, e.g. KMail (or KMime, resp.)
 *
 *  This program 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; version 2 of the License.
 *
 *  This program 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.
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "cryptplugwrapper.h"
#include "cryptplug.h"

#include <backends/qgpgme/qgpgmekeylistjob.h>
#include <backends/qgpgme/qgpgmeencryptjob.h>
#include <backends/qgpgme/qgpgmedecryptjob.h>
#include <backends/qgpgme/qgpgmesignjob.h>
#include <backends/qgpgme/qgpgmeverifydetachedjob.h>
#include <backends/qgpgme/qgpgmeverifyopaquejob.h>
#include <backends/qgpgme/qgpgmekeygenerationjob.h>
#include <backends/qgpgme/qgpgmeimportjob.h>
#include <backends/qgpgme/qgpgmeexportjob.h>
#include <backends/qgpgme/qgpgmesecretkeyexportjob.h>
#include <backends/qgpgme/qgpgmedownloadjob.h>
#include <backends/qgpgme/qgpgmedeletejob.h>
#include <backends/qgpgme/qgpgmesignencryptjob.h>
#include <backends/qgpgme/qgpgmedecryptverifyjob.h>
#include <backends/qgpgme/qgpgmecryptoconfig.h>
#include <backends/qgpgme/qgpgmerefreshkeysjob.h>

// qgpgme
#include <qgpgme/dataprovider.h>

// gpgme++
#include <gpgmepp/data.h>
#include <gpgmepp/importresult.h>
#include <gpgmepp/keygenerationresult.h>

// kde
#include <kdebug.h>
#include <tdeapplication.h>
#include <tdelocale.h>
#include <tdeglobal.h>
#include <tdeconfig.h>

// other
#include <memory>

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>




/*
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                                                                    *
 *  This file's source comments - as well as those in interface file  *
 *  cryptplugwrapper.h - are optimized for processing by Doxygen.     *
 *                                                                    *
 *  To obtain best results please get an updated version of Doxygen,  *
 *  for sources and binaries goto http://www.doxygen.org/index.html   *
 *                                                                    *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
                                                                      *
                                                                      */



/*! \file cryptplugwrapper.cpp
    \brief C++ wrapper for the CRYPTPLUG library API.

    This CRYPTPLUG wrapper implementation is based on cryptplug.h by
    Karl-Heinz Zimmer which is based on 'The Aegypten Plugin API' as
    specified by Matthias Kalle Dalheimer, Klaraelvdalens Datakonsult AB,
    see file mua-integration.sgml located on Aegypten CVS:
             http://www.gnupg.org/aegypten/development.en.html

    purpose: Wrap up all Aegypten Plugin API functions in one C++ class
             for usage by KDE programs, e.g. KMail (or KMime, resp.)

    CRYPTPLUG is an independent cryptography plug-in API
    developed for Sphinx-enabeling KMail and Mutt.

    CRYPTPLUG was designed for the Aegypten project, but it may
    be used by 3rd party developers as well to design pluggable
    crypto backends for the above mentioned MUAs.

    \note All string parameters appearing in this API are to be
    interpreted as UTF-8 encoded.

    \see cryptplugwrapper.h
*/


// a little helper class for reordering of DN attributes
class DNBeautifier {
public:
  enum UnknownAttrsHandling { unknownAttrsHide,
                              unknownAttrsPrefix,
                              unknownAttrsPostfix,
                              unknownAttrsInfix };
  // infix: at the position of "_X_", if any, else Postfix

  DNBeautifier()
  {
    // the attrOrder is defaulted to an empty string automatically
    _unknownAttrsHandling = unknownAttrsInfix;
    _unknownAttrsHandlingChar = "INFIX";
  }
  DNBeautifier( TDEConfig* config,
                const TQString& cfgGroup,
                const TQString& cfgAttributeOrderEntry,
                const TQString& cfgUnknownAttrsEntry,
                const TQStringList& fallbackAttrOrder = TQStringList(),
                UnknownAttrsHandling fallbackUnknowAttrsHandling = unknownAttrsInfix )
  {
    _unknownAttrsHandling = unknownAttrsInfix;
    _unknownAttrsHandlingChar = "INFIX";
    if( config ){
      const TQString oldGroup( config->group() );
      config->setGroup( cfgGroup );                             // e.g. "General"
      _attrOrder =
        config->readListEntry( cfgAttributeOrderEntry );        // e.g. "DNAttributeOrder"
      _unknownAttrsHandlingChar =
        config->readEntry( cfgUnknownAttrsEntry ).upper().latin1(); // e.g. "DNUnknownAttributes"
      config->setGroup( oldGroup );
      if( _unknownAttrsHandlingChar == "HIDE" )
        _unknownAttrsHandling = unknownAttrsHide;
      else if( _unknownAttrsHandlingChar == "PREFIX" )
        _unknownAttrsHandling = unknownAttrsPrefix;
      else if( _unknownAttrsHandlingChar == "POSTFIX" )
        _unknownAttrsHandling = unknownAttrsPostfix;
      else if( _unknownAttrsHandlingChar == "INFIX" )
        _unknownAttrsHandling = unknownAttrsInfix;
      else
        _unknownAttrsHandlingChar = "INFIX";
    }
    if( _attrOrder.isEmpty() && ! fallbackAttrOrder.isEmpty() )
      _attrOrder = fallbackAttrOrder;

    if( _attrOrder.isEmpty() ){
      _attrOrderChar = 0;
    }else{
      _attrOrderChar = new char*[ _attrOrder.count()+1 ];
      int i=0;
      for( TQStringList::ConstIterator itOrder = _attrOrder.begin();
           itOrder != _attrOrder.end();
           ++itOrder ){
        _attrOrderChar[ i ] = (char*)malloc( ((*itOrder).length()+1)*sizeof(char) );
        strcpy( _attrOrderChar[ i ], (*itOrder).latin1() );
        ++i;
      }
      _attrOrderChar[ i ] = NULL;
    }
  }
  ~DNBeautifier()
  {
    int i=0;
    for( TQStringList::ConstIterator itOrder = _attrOrder.begin();
         itOrder != _attrOrder.end();
         ++itOrder ){
      free( _attrOrderChar[ i ] );
      ++i;
    }
    delete[] _attrOrderChar;
  }

  TQStringList attrOrder() const
  {
    return _attrOrder;
  }
  char** attrOrderChar()
  {
    return _attrOrderChar;
  }

  UnknownAttrsHandling unknownAttrsHandling() const
  {
    return _unknownAttrsHandling;
  }
  const char* unknownAttrsHandlingChar() const
  {
    return _unknownAttrsHandlingChar;
  }

  TQValueList< TQPair<TQString,TQString> > reorder( const TQValueList< TQPair<TQString,TQString> > & dn ) const
  {
    return reorder( dn, _attrOrder, _unknownAttrsHandling );
  }


  static TQValueList< TQPair<TQString,TQString> > reorder(
    const TQValueList< TQPair<TQString,TQString> > & dn,
    TQStringList attrOrder,
    UnknownAttrsHandling unknownAttrsHandling )
  {
    if( !attrOrder.isEmpty() ){
      TQPtrList<   TQPair<TQString,TQString> > unknownEntries;
      TQValueList< TQPair<TQString,TQString> > dnNew;

      TQPair<TQString,TQString>* unknownEntry;
      TQStringList::ConstIterator itOrder;
      TQValueList< TQPair<TQString,TQString> >::ConstIterator itDN;
      bool bFound;

      if( unknownAttrsHandling != unknownAttrsHide ){
        // find all unknown entries in their order of appearance
        for( itDN = dn.begin(); itDN != dn.end(); ++itDN ){
          bFound = false;
          for( itOrder = attrOrder.begin(); itOrder != attrOrder.end(); ++itOrder ){
            if( (*itOrder) == (*itDN).first ){
              bFound = true;
              break;
            }
          }
          if( !bFound )
            unknownEntries.append( &(*itDN) );
        }
      }

      // prepend the unknown attrs (if desired)
      if( unknownAttrsHandling == unknownAttrsPrefix ){
        for( unknownEntry = unknownEntries.first(); unknownEntry; unknownEntry = unknownEntries.next() ){
          dnNew << *unknownEntry;
        }
      }

      // process the known attrs in the desired order
      bool b_X_declared = false;
      for( itOrder = attrOrder.begin(); itOrder != attrOrder.end(); ++itOrder ){
        if( (*itOrder) == "_X_" ){
          b_X_declared = true;
          // insert the unknown attrs (if desired)
          if( unknownAttrsHandling == unknownAttrsInfix ){
            for( unknownEntry = unknownEntries.first(); unknownEntry; unknownEntry = unknownEntries.next() ){
              dnNew << *unknownEntry;
            }
          }
        }else{
          for( itDN = dn.begin(); itDN != dn.end(); ++itDN ){
            if( (*itOrder) == (*itDN).first ){
              dnNew << *itDN;
              //kdDebug(5150) << TQString((*itDN).first) <<" = " << TQString((*itDN).second) << endl;;
            }
          }
        }
      }

      // append the unknown attrs (if desired)
      if( unknownAttrsHandling == unknownAttrsPostfix ||
          ( unknownAttrsHandling == unknownAttrsInfix && ! b_X_declared ) ){
        for( unknownEntry = unknownEntries.first(); unknownEntry; unknownEntry = unknownEntries.next() ){
          dnNew << *unknownEntry;
        }
      }

      return dnNew;
    }
    return dn;
  }

private:
  TQStringList _attrOrder;
  char**      _attrOrderChar;
  UnknownAttrsHandling _unknownAttrsHandling;
  TQCString    _unknownAttrsHandlingChar;
};



/* special helper class to be used by signing/encrypting functions *******/



StructuringInfoWrapper::StructuringInfoWrapper( CryptPlugWrapper* wrapper )
  : _initDone( false ), _wrapper( wrapper )
{
    initMe();
}
StructuringInfoWrapper::~StructuringInfoWrapper()
{
    freeMe();
}
void StructuringInfoWrapper::reset()
{
    freeMe();
    initMe();
}
void StructuringInfoWrapper::initMe()
{
    if ( _wrapper && _wrapper->cryptPlug() ) {
      _wrapper->cryptPlug()->init_StructuringInfo( &data );
      _initDone = true;
    }
}
void StructuringInfoWrapper::freeMe()
{
    if( _wrapper && _wrapper->cryptPlug() && _initDone ) {
      _wrapper->cryptPlug()->free_StructuringInfo( &data );
      _initDone = false;
    }
}

class CryptPlugWrapper::Config {
public:
  Config( gpgme_protocol_t proto );
  ~Config();

  const char*             signatureKeyCertificate;
  SignatureAlgorithm      signatureAlgorithm;
  SignatureCompoundMode   signatureCompoundMode;
  SendCertificates        sendCertificates;
  bool                    saveSentSignatures;
  bool                    warnNoCertificate;
  bool                    signatureUseCRLs;
  EncryptionAlgorithm     encryptionAlgorithm;
  EncryptEmail            encryptEmail;
  bool                    saveMessagesEncrypted;
  bool                    encryptionUseCRLs;
  bool                    encryptionCRLExpiryNearWarning;
  int                     encryptionCRLNearExpiryInterval;
  CertificateSource       certificateSource;
  bool                    warnSendUnsigned;
  bool                    signatureCertificateExpiryNearWarning;
  int                     signatureCertificateExpiryNearInterval;
  bool                    cACertificateExpiryNearWarning;
  int                     cACertificateExpiryNearInterval;
  bool                    rootCertificateExpiryNearWarning;
  int                     rootCertificateExpiryNearInterval;
  bool                    warnSendUnencrypted;
  bool                    checkCertificatePath;
  bool                    receiverCertificateExpiryNearWarning;
  int                     receiverCertificateExpiryNearWarningInterval;
  bool                    certificateInChainExpiryNearWarning;
  int                     certificateInChainExpiryNearWarningInterval;
  bool                    receiverEmailAddressNotInCertificateWarning;
  const char* libVersion; /* a statically allocated string with the GPGME Version used */
};

static const int NEAR_EXPIRY = 14;

CryptPlugWrapper::Config::Config( gpgme_protocol_t proto )
{
  signatureAlgorithm                   = SignAlg_SHA1;
  if ( proto == GPGME_PROTOCOL_CMS )
    signatureCompoundMode              = SignatureCompoundMode_Opaque;
  else
    signatureCompoundMode              = SignatureCompoundMode_Detached;
  sendCertificates                     = SendCert_SendChainWithRoot;
  saveSentSignatures                   = true;
  warnNoCertificate                    = true;
  signatureUseCRLs                     = true;
  encryptionAlgorithm                  = EncryptAlg_RSA;
  encryptEmail                         = EncryptEmail_Ask;
  saveMessagesEncrypted                = true;
  encryptionUseCRLs                    = true;
  encryptionCRLExpiryNearWarning       = false;
  encryptionCRLNearExpiryInterval      = NEAR_EXPIRY;
  certificateSource                    = CertSrc_Server;
  warnSendUnsigned                             = true;
  signatureCertificateExpiryNearWarning        = true;
  signatureCertificateExpiryNearInterval       = NEAR_EXPIRY;
  cACertificateExpiryNearWarning               = true;
  cACertificateExpiryNearInterval              = NEAR_EXPIRY;
  rootCertificateExpiryNearWarning             = true;
  rootCertificateExpiryNearInterval            = NEAR_EXPIRY;
  warnSendUnencrypted                          = false;
  checkCertificatePath                         = true;
  receiverCertificateExpiryNearWarning         = true;
  receiverCertificateExpiryNearWarningInterval = NEAR_EXPIRY;
  certificateInChainExpiryNearWarning          = true;
  certificateInChainExpiryNearWarningInterval  = NEAR_EXPIRY;
  receiverEmailAddressNotInCertificateWarning  = true;
  libVersion = gpgme_check_version (NULL);
}

CryptPlugWrapper::Config::~Config() {
}

/* Some multi purpose functions ******************************************/

TQString CryptPlugWrapper::errorIdToText( int errId, bool & isPassphraseError ) {
  const GpgME::Error err( errId );
  isPassphraseError = err.isCanceled()
    || gpgme_err_code( errId ) == GPG_ERR_NO_SECKEY ; // FIXME: more?
  return TQString::fromLocal8Bit( err.asString() );
}

/* some special functions ************************************************/


CryptPlugWrapper::CryptPlugWrapper( const TQString& name,
                                    const TQString& libName,
                                    const TQString& update,
                                    bool           active )
  : Kleo::CryptoBackend::Protocol(),
    _name( name ),
    _libName( libName ),
    _updateURL( update ),
    _active(  active  ),
    _iniStatus( IniStatus_undef ),
    _cp( 0 ),
    _config( 0 ),
    _cryptoConfig( 0 )
{
  const bool ok = initialize( 0, 0 );
  assert( ok );
}


CryptPlugWrapper::~CryptPlugWrapper()
{
    deinitialize();
}


void CryptPlugWrapper::setActive( bool active )
{
    _active = active;
}


bool CryptPlugWrapper::active() const
{
    return _active;
}



bool CryptPlugWrapper::setLibName( const TQString& libName )
{
    bool bOk = ! _cp;           // Changing the lib name is only allowed
    if( bOk )                   // when either no initialization took
        _libName = libName;     // place or 'deinitialize()' has been
    return bOk;                 // called afterwards.
}

TQString CryptPlugWrapper::libName() const
{
    return _libName;
}

TQString CryptPlugWrapper::protocol() const
{
  if ( _libName.contains( "smime" ) )
    return "SMIME";
  if ( _libName.contains( "openpgp" ) )
    return "OpenPGP";
  return TQString();
}

void CryptPlugWrapper::setDisplayName( const TQString& name )
{
    _name = name;
}


TQString CryptPlugWrapper::displayName() const
{
    if ( !_name.isEmpty() )
      return _name;
    if ( _libName.contains( "smime" ) )
      return "gpgsm";
    if ( _libName.contains( "openpgp" ) )
      return "gpg";
    return i18n("(Unknown Protocol)");
}

bool CryptPlugWrapper::initialize( IniStatus* iniStatus, TQString* errorMsg )
{
    if ( _cp )
      return true;

    _iniStatus = IniStatus_undef;
    /* make sure we have a lib name */
    if ( _libName.isEmpty() ) {
      _iniStatus = IniStatus_NoLibName;
      kdDebug(5150) << "No library name was given.\n" << endl;
    } else {
      if ( _libName.contains( "smime" ) ) {
	_cp = new SMIMECryptPlug();
	_config = new Config( GPGME_PROTOCOL_CMS );
      } else if ( _libName.contains( "openpgp" ) ) {
	_cp = new OpenPGPCryptPlug();
	_config = new Config( GPGME_PROTOCOL_OpenPGP );
      } else {
	_cp = 0;
	_config = 0;
      }

      if ( !_cp ) {
	_iniStatus = IniStatus_LoadError;
	kdDebug(5150) << "Couldn't create '" << _libName.latin1() << "'" << endl;
      } else {
	/* now call the init function */
	if( !_cp->initialize() ) {
	  _iniStatus = IniStatus_InitError;
	  kdDebug(5150) << "Error while executing function 'initialize' on plugin " << _libName << endl;
	  _lastError = i18n("Error while initializing plugin \"%1\"").arg( _libName );
	  if ( errorMsg )
	    *errorMsg = _lastError;
	  delete _cp; _cp = 0;
	  delete _config; _config = 0;
	} else {
	  _iniStatus  = IniStatus_Ok;
	}
      }
    }
    if( iniStatus )
        *iniStatus = _iniStatus;
    return _iniStatus == IniStatus_Ok;
}



void CryptPlugWrapper::deinitialize()
{
    delete _cp; _cp = 0;
    delete _config; _config = 0;
    delete _cryptoConfig; _cryptoConfig = 0;
}


CryptPlugWrapper::IniStatus CryptPlugWrapper::iniStatus( TQString* errorMsg ) const
{
    if( errorMsg )
        *errorMsg = _lastError;
    return _iniStatus;
}


bool CryptPlugWrapper::hasFeature( Feature flag )
{
  return _cp && _cp->hasFeature( flag );
}


/* normal functions ******************************************************/

bool CryptPlugWrapper::checkMessageSignature( char** cleartext,
                                              const char* signaturetext,
                                              bool signatureIsBinary,
                                              int signatureLen,
                                              CryptPlug::SignatureMetaData* sigmeta )
{
  DNBeautifier dnBeautifier( kapp->config(),
                             "DN",
                             "AttributeOrder",
                             "UnknownAttributes" );
  return _cp && _cp->checkMessageSignature( cleartext,
                                            signaturetext,
                                            signatureIsBinary,
                                            signatureLen,
                                            sigmeta,
                                            dnBeautifier.attrOrderChar(),
                                            dnBeautifier.unknownAttrsHandlingChar() );
}


bool CryptPlugWrapper::decryptMessage( const char* ciphertext,
                                       bool        cipherIsBinary,
                                       int         cipherLen,
                                       char**      cleartext,
                                       const char* certificate,
                                       int* errId,
                                       char** errTxt )
{
  return _cp && _cp->decryptMessage( ciphertext, cipherIsBinary, cipherLen,
				     (const char**)cleartext, certificate, errId, errTxt );
}


bool CryptPlugWrapper::decryptAndCheckMessage(
                            const char*  ciphertext,
                            bool         cipherIsBinary,
                            int          cipherLen,
                            char**       cleartext,
                            const char*  certificate,
                            bool*        signatureFound,
                            CryptPlug::SignatureMetaData* sigmeta,
                            int*   errId,
                            char** errTxt )
{
  DNBeautifier dnBeautifier( kapp->config(),
                             "DN",
                             "AttributeOrder",
                             "UnknownAttributes" );
  return _cp && _cp->decryptAndCheckMessage( ciphertext,
                                             cipherIsBinary,
                                             cipherLen,
                                             (const char**)cleartext,
                                             certificate,
                                             signatureFound,
                                             sigmeta,
                                             errId,
                                             errTxt,
                                             dnBeautifier.attrOrderChar(),
                                             dnBeautifier.unknownAttrsHandlingChar() );
}




void CryptPlugWrapper::freeSignatureMetaData( CryptPlug::SignatureMetaData* sigmeta )
{
    if ( !sigmeta )
      return;
    free( sigmeta->status );
    for( int i = 0; i < sigmeta->extended_info_count; ++i ) {
        free( sigmeta->extended_info[i].creation_time );
        free( (void*)sigmeta->extended_info[i].status_text );
        free( (void*)sigmeta->extended_info[i].keyid );
        free( (void*)sigmeta->extended_info[i].fingerprint );
        free( (void*)sigmeta->extended_info[i].algo );
        free( (void*)sigmeta->extended_info[i].userid );
        free( (void*)sigmeta->extended_info[i].name );
        free( (void*)sigmeta->extended_info[i].comment );
        if( sigmeta->extended_info[i].emailCount ){
            for( int j=0; j < sigmeta->extended_info[i].emailCount; ++j)
                if( sigmeta->extended_info[i].emailList[j] )
                    free( (void*)sigmeta->extended_info[i].emailList[j] );
            free( (void*)sigmeta->extended_info[i].emailList );
        }
    }
    free( sigmeta->extended_info );
}

GpgME::ImportResult CryptPlugWrapper::importCertificate( const char* data, size_t length )
{
    if ( !_cp )
      return GpgME::ImportResult();


   return _cp->importCertificateFromMem( data, length );
}

Kleo::KeyListJob * CryptPlugWrapper::keyListJob( bool remote, bool includeSigs, bool validate ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  unsigned int mode = context->keyListMode();
  if ( remote ) {
    mode |= GpgME::Context::Extern;
    mode &= ~GpgME::Context::Local;
  } else {
    mode |= GpgME::Context::Local;
    mode &= ~GpgME::Context::Extern;
  }
  if ( includeSigs ) mode |= GpgME::Context::Signatures;
  if ( validate ) mode |= GpgME::Context::Validate;
  context->setKeyListMode( mode );
  return new Kleo::QGpgMEKeyListJob( context );
}

Kleo::EncryptJob * CryptPlugWrapper::encryptJob( bool armor, bool textmode ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setArmor( armor );
  context->setTextMode( textmode );
  return new Kleo::QGpgMEEncryptJob( context );
}

Kleo::DecryptJob * CryptPlugWrapper::decryptJob() const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  return new Kleo::QGpgMEDecryptJob( context );
}

Kleo::SignJob * CryptPlugWrapper::signJob( bool armor, bool textMode ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setArmor( armor );
  context->setTextMode( textMode );

  return new Kleo::QGpgMESignJob( context );
}

Kleo::VerifyDetachedJob * CryptPlugWrapper::verifyDetachedJob( bool textMode ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setTextMode( textMode );

  return new Kleo::QGpgMEVerifyDetachedJob( context );
}

Kleo::VerifyOpaqueJob * CryptPlugWrapper::verifyOpaqueJob( bool textMode ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setTextMode( textMode );

  return new Kleo::QGpgMEVerifyOpaqueJob( context );
}

Kleo::KeyGenerationJob * CryptPlugWrapper::keyGenerationJob() const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  return new Kleo::QGpgMEKeyGenerationJob( context );
}

Kleo::ImportJob * CryptPlugWrapper::importJob() const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  return new Kleo::QGpgMEImportJob( context );
}

Kleo::ExportJob * CryptPlugWrapper::publicKeyExportJob( bool armor ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setArmor( armor );
  return new Kleo::QGpgMEExportJob( context );
}

Kleo::ExportJob * CryptPlugWrapper::secretKeyExportJob( bool armor, const TQString& charset ) const {
  if ( !_cp || _cp->mProtocol != GpgME::Context::CMS ) // fixme: add support for gpg, too
    return 0;

  // this operation is not supported by gpgme, so we have to call gpgsm ourselves:
  return new Kleo::QGpgMESecretKeyExportJob( armor, charset );
}

Kleo::RefreshKeysJob * CryptPlugWrapper::refreshKeysJob() const {
  if ( !_cp || _cp->mProtocol != GpgME::Context::CMS ) // fixme: add support for gpg, too
    return 0;

  // this operation is not supported by gpgme, so we have to call gpgsm ourselves:
  return new Kleo::QGpgMERefreshKeysJob();
}

Kleo::DownloadJob * CryptPlugWrapper::downloadJob( bool armor ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setArmor( armor );
  // this is the hackish interface for downloading from keyserers currently:
  context->setKeyListMode( GpgME::Context::Extern );

  return new Kleo::QGpgMEDownloadJob( context );
}

Kleo::DeleteJob * CryptPlugWrapper::deleteJob() const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  return new Kleo::QGpgMEDeleteJob( context );
}

Kleo::SignEncryptJob * CryptPlugWrapper::signEncryptJob( bool armor, bool textMode ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setArmor( armor );
  context->setTextMode( textMode );

  return new Kleo::QGpgMESignEncryptJob( context );
}

Kleo::DecryptVerifyJob * CryptPlugWrapper::decryptVerifyJob( bool textMode ) const {
  if ( !_cp )
    return 0;

  GpgME::Context * context = GpgME::Context::createForProtocol( _cp->mProtocol );
  if ( !context )
    return 0;

  context->setTextMode( textMode );

  return new Kleo::QGpgMEDecryptVerifyJob( context );
}
