1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-06 20:20:41 +02:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Leo Franchi
3999a98514 Only connect XMPP plugin when credentials are loaded 2012-06-10 11:28:28 +02:00
Leo Franchi
1210665e56 TWK-916: Don't allow null queries from HTTP Api 2012-06-10 10:38:23 +02:00
Leo Franchi
59cd8e5b80 Move QtKeychain to subdir 2012-06-10 09:08:38 +02:00
Leo Franchi
3f75db64eb Fix connection mismatch 2012-06-10 09:08:21 +02:00
Leo Franchi
6ac1545a09 Only install from attica when checking, even if we have checked + uninstalled 2012-06-10 09:08:01 +02:00
Leo Franchi
eb65f7ca39 Enable insecure fallback of QtKeychain 2012-06-10 09:07:40 +02:00
Leo Franchi
f9c58dda1e Add QtKeychain in thirdparty/ 2012-06-10 09:07:22 +02:00
Leo Franchi
bebac92763 Save credentials as well as committing them to storage 2012-06-09 11:27:25 +02:00
Leo Franchi
7130151949 Port accounts to async credentials loading
Merge branch 'master' into qtkeychain

Conflicts:
	src/libtomahawk/CMakeLists.txt
	src/libtomahawk/TomahawkSettings.cpp
	src/libtomahawk/accounts/Account.cpp
	src/libtomahawk/accounts/lastfm/LastFmConfig.cpp
2012-06-09 10:48:46 +02:00
Stefan Derkits
44aa299099 find qtkeychain library via CMake 2012-05-22 15:09:12 +02:00
Stefan Derkits
ed48f4a5f2 ported LastFm & Twitter config widgets to load credentials on show 2012-05-16 13:49:19 +02:00
Stefan Derkits
8febff6030 start migration code, load credentials on show (instead on object creation) in XmppConfigWidget 2012-05-14 19:55:58 +02:00
Stefan Derkits
e5933b775d fixed bugs in QtKeyChain integration 2012-05-14 17:18:34 +02:00
Stefan Derkits
a968f3e92b small changes in QtKeychain integration 2012-05-14 12:22:45 +02:00
Stefan Derkits
7fbcf653d3 Additional work on password storage via qtkeychain 2012-05-14 01:15:53 +02:00
Stefan Derkits
fc5bdae520 preperations & first steps towards implementations of password storage via QtKeychain (using async branch) 2012-05-07 00:16:44 +02:00
45 changed files with 2108 additions and 129 deletions

View File

@@ -159,6 +159,17 @@ SET( LIBPORTFWD_LIBRARY tomahawk_portfwd )
SET( LIBPORTFWD_LIBRARIES ${LIBPORTFWD_LIBRARY} )
ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/libportfwd )
### qtkeychain
SET( QTKEYCHAIN_INCLUDE_DIRS ${THIRDPARTY_DIR}/qtkeychain )
SET( QTKEYCHAIN_LIBRARY qtkeychain )
SET( QTKEYCHAIN_LIBRARIES ${QTKEYCHAIN_LIBRARY} )
ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/qtkeychain/qtkeychain )
if( NOT QTKEYCHAIN_INCLUDE_DIRS OR NOT QTKEYCHAIN_LIBRARIES )
macro_optional_find_package(QtKeychain)
macro_log_feature(QTKEYCHAIN_FOUND "QtKeychain" "Provides support for secure password storage" "https://github.com/frankosterfeld/qtkeychain" TRUE "" "")
endif()
# we need pthreads too
#macro_optional_find_package(Threads)
#macro_log_feature(THREADS_FOUND "Threads" "Threading Library" "" TRUE "" "Platform specific library for threading")

View File

@@ -53,6 +53,8 @@ TwitterAccount::TwitterAccount( const QString &accountId )
setAccountServiceName( "Twitter" );
setTypes( AccountTypes( StatusPushType | SipType ) );
connect( this, SIGNAL( credentialsLoaded( QVariantHash ) ), this, SLOT( onCredentialsLoaded( QVariantHash ) ) );
qDebug() << "Got cached peers:" << configuration() << configuration()[ "cachedpeers" ];
m_configWidget = QWeakPointer< TwitterConfigWidget >( new TwitterConfigWidget( this, 0 ) );
@@ -73,13 +75,41 @@ TwitterAccount::configDialogAuthedSignalSlot( bool authed )
{
tDebug() << Q_FUNC_INFO;
m_isAuthenticated = authed;
if ( !credentials()[ "username" ].toString().isEmpty() )
setAccountFriendlyName( QString( "@%1" ).arg( credentials()[ "username" ].toString() ) );
if ( !m_credentials[ "username" ].toString().isEmpty() )
setAccountFriendlyName( QString( "@%1" ).arg( m_credentials[ "username" ].toString() ) );
syncConfig();
emit configurationChanged();
}
void
TwitterAccount::onCredentialsLoaded( const QVariantHash &credentials )
{
// Credentials loaded
bool reload = false;
if ( !credentials[ "oauthtoken" ].toString().isEmpty() && !credentials[ "oauthtokensecret" ].toString().isEmpty() &&
( m_credentials[ "oauthtoken" ] != credentials[ "oauthtoken"] || m_credentials[ "oauthtokensecret" ] != credentials[ "oauthtokensecret" ] ) )
reload = true;
m_credentials = credentials;
if ( reload && enabled() )
{
qDebug() << "Twitter account got async load of credentials, authenticating now!";
authenticate();
}
}
void
TwitterAccount::setCredentials( const QVariantHash &credentials )
{
m_credentials = credentials;
saveCredentials( credentials );
}
Account::ConnectionState
TwitterAccount::connectionState() const
{
@@ -139,9 +169,9 @@ TwitterAccount::authenticateSlot()
return;
}
tDebug() << Q_FUNC_INFO << "credentials: " << credentials().keys();
tDebug() << Q_FUNC_INFO << "credentials: " << m_credentials.keys();
if ( credentials()[ "oauthtoken" ].toString().isEmpty() || credentials()[ "oauthtokensecret" ].toString().isEmpty() )
if ( m_credentials[ "oauthtoken" ].toString().isEmpty() || m_credentials[ "oauthtokensecret" ].toString().isEmpty() )
{
tDebug() << Q_FUNC_INFO << "TwitterSipPlugin has empty Twitter credentials; not connecting";
return;
@@ -191,8 +221,8 @@ TwitterAccount::refreshTwitterAuth()
if( m_twitterAuth.isNull() )
return false;
m_twitterAuth.data()->setOAuthToken( credentials()[ "oauthtoken" ].toString().toLatin1() );
m_twitterAuth.data()->setOAuthTokenSecret( credentials()[ "oauthtokensecret" ].toString().toLatin1() );
m_twitterAuth.data()->setOAuthToken( m_credentials[ "oauthtoken" ].toString().toLatin1() );
m_twitterAuth.data()->setOAuthTokenSecret( m_credentials[ "oauthtokensecret" ].toString().toLatin1() );
return true;
}

View File

@@ -79,11 +79,16 @@ public:
bool refreshTwitterAuth();
TomahawkOAuthTwitter* twitterAuth() const { return m_twitterAuth.data(); }
QVariantHash credentials() const { return m_credentials; }
void setCredentials( const QVariantHash& creds );
signals:
void nowAuthenticated( const QWeakPointer< TomahawkOAuthTwitter >&, const QTweetUser &user );
void nowDeauthenticated();
private slots:
void onCredentialsLoaded( const QVariantHash& credentials );
void authenticateSlot();
void configDialogAuthedSignalSlot( bool authed );
void connectAuthVerifyReply( const QTweetUser &user );
@@ -92,6 +97,7 @@ private:
QIcon m_icon;
bool m_isAuthenticated;
bool m_isAuthenticating;
QVariantHash m_credentials;
QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth;
QWeakPointer< TwitterConfigWidget > m_configWidget;
QWeakPointer< TwitterSipPlugin > m_twitterSipPlugin;

View File

@@ -58,6 +58,17 @@ TwitterConfigWidget::TwitterConfigWidget( TwitterAccount* account, QWidget *pare
m_ui->twitterTweetComboBox->setCurrentIndex( 0 );
m_ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) );
}
TwitterConfigWidget::~TwitterConfigWidget()
{
delete m_ui;
}
void
TwitterConfigWidget::loadConfig()
{
QVariantHash credentials = m_account->credentials();
if ( credentials[ "oauthtoken" ].toString().isEmpty() ||
@@ -79,13 +90,8 @@ TwitterConfigWidget::TwitterConfigWidget( TwitterAccount* account, QWidget *pare
emit twitterAuthed( true );
}
}
TwitterConfigWidget::~TwitterConfigWidget()
{
delete m_ui;
}
void
TwitterConfigWidget::authDeauthTwitter()
@@ -159,11 +165,7 @@ void
TwitterConfigWidget::deauthenticateTwitter()
{
qDebug() << Q_FUNC_INFO;
QVariantHash credentials = m_account->credentials();
credentials[ "oauthtoken" ] = QString();
credentials[ "oauthtokensecret" ] = QString();
credentials[ "username" ] = QString();
m_account->setCredentials( credentials );
m_account->setCredentials( QVariantHash() );
m_ui->twitterStatusLabel->setText(tr("Status: No saved credentials"));
m_ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) );
@@ -232,7 +234,7 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use
return;
}
TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( TomahawkUtils::nam(), this );
QVariantHash credentials = m_account->credentials();
const QVariantHash credentials = m_account->credentials();
twitAuth->setOAuthToken( credentials[ "oauthtoken" ].toString().toLatin1() );
twitAuth->setOAuthTokenSecret( credentials[ "oauthtokensecret" ].toString().toLatin1() );
if ( m_postGTtype != "Direct Message" )
@@ -291,6 +293,14 @@ TwitterConfigWidget::postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode
QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") );
}
void
TwitterConfigWidget::showEvent(QShowEvent* event)
{
loadConfig();
}
}
}
}

View File

@@ -50,11 +50,17 @@ class ACCOUNTDLLEXPORT TwitterConfigWidget : public QWidget
public:
explicit TwitterConfigWidget( TwitterAccount* account = 0, QWidget *parent = 0 );
virtual ~TwitterConfigWidget();
void loadConfig();
signals:
void twitterAuthed( bool authed );
void sizeHintChanged();
protected:
void showEvent( QShowEvent* event );
private slots:
void authDeauthTwitter();

View File

@@ -40,7 +40,10 @@ XmppAccountFactory::createAccount( const QString& accountId )
XmppAccount::XmppAccount( const QString &accountId )
: Account( accountId )
, m_credentialsLoading( true )
{
connect( this, SIGNAL( credentialsLoaded( QVariantHash ) ), this, SLOT( onCredentialsLoaded( QVariantHash ) ) );
setAccountServiceName( "Jabber (XMPP)" );
setTypes( SipType );
@@ -57,7 +60,7 @@ XmppAccount::~XmppAccount()
void
XmppAccount::authenticate()
{
if ( connectionState() != Account::Connected )
if ( connectionState() != Account::Connected && !m_credentialsLoading )
sipPlugin()->connectPlugin();
}
@@ -90,6 +93,25 @@ XmppAccount::saveConfig()
}
void
XmppAccount::onCredentialsLoaded( const QVariantHash& credentials )
{
m_credentials = credentials;
m_credentialsLoading = false;
if ( !m_xmppSipPlugin.isNull() )
m_xmppSipPlugin.data()->configurationChanged();
}
void
XmppAccount::setCredentials( const QVariantHash &credentials )
{
m_credentials = credentials;
saveCredentials( credentials );
}
InfoSystem::InfoPluginPtr
XmppAccount::infoPlugin()
{

View File

@@ -77,12 +77,21 @@ public:
QWidget* aclWidget() { return 0; }
void saveConfig();
QVariantHash credentials() const { return m_credentials; }
void setCredentials( const QVariantHash& credentials );
virtual Tomahawk::Accounts::Account::ConnectionState connectionState() const;
private slots:
void onCredentialsLoaded( const QVariantHash& credentials );
protected:
QWeakPointer< QWidget > m_configWidget; // so the google wrapper can change the config dialog a bit
QWeakPointer< XmppSipPlugin > m_xmppSipPlugin;
QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_xmppInfoPlugin;
QVariantHash m_credentials;
bool m_credentialsLoading;
};
};

View File

@@ -38,14 +38,9 @@ XmppConfigWidget::XmppConfigWidget( XmppAccount* account, QWidget *parent ) :
m_account( account )
{
m_ui->setupUi( this );
m_ui->xmppUsername->setText( account->credentials().contains( "username" ) ? account->credentials()[ "username" ].toString() : QString() );
m_ui->xmppPassword->setText( account->credentials().contains( "password" ) ? account->credentials()[ "password" ].toString() : QString() );
m_ui->xmppServer->setText( account->configuration().contains( "server" ) ? account->configuration()[ "server" ].toString() : QString() );
m_ui->xmppPort->setValue( account->configuration().contains( "port" ) ? account->configuration()[ "port" ].toInt() : 5222 );
m_ui->xmppPublishTracksCheckbox->setChecked( account->configuration().contains( "publishtracks" ) ? account->configuration()[ "publishtracks" ].toBool() : true);
m_ui->xmppEnforceSecureCheckbox->setChecked( account->configuration().contains( "enforcesecure" ) ? account->configuration()[ "enforcesecure" ].toBool() : false);
m_ui->jidExistsLabel->hide();
loadFromConfig();
connect( m_ui->xmppUsername, SIGNAL( textChanged( QString ) ), SLOT( onCheckJidExists( QString ) ) );
}
@@ -70,27 +65,52 @@ XmppConfigWidget::saveConfig()
configuration[ "enforcesecure"] = m_ui->xmppEnforceSecureCheckbox->isChecked();
m_account->setAccountFriendlyName( m_ui->xmppUsername->text() );
m_account->setCredentials( credentials );
m_account->setConfiguration( configuration);
m_account->sync();
m_account->setCredentials( credentials );
static_cast< XmppSipPlugin* >( m_account->sipPlugin() )->checkSettings();
}
void
XmppConfigWidget::showEvent(QShowEvent* event)
{
loadFromConfig();
}
void
XmppConfigWidget::loadFromConfig()
{
m_ui->xmppUsername->setText( m_account->credentials().contains( "username" ) ? m_account->credentials()[ "username" ].toString() : QString() );
m_ui->xmppPassword->setText( m_account->credentials().contains( "password" ) ? m_account->credentials()[ "password" ].toString() : QString() );
m_ui->xmppServer->setText( m_account->configuration().contains( "server" ) ? m_account->configuration()[ "server" ].toString() : QString() );
m_ui->xmppPort->setValue( m_account->configuration().contains( "port" ) ? m_account->configuration()[ "port" ].toInt() : 5222 );
m_ui->xmppPublishTracksCheckbox->setChecked( m_account->configuration().contains( "publishtracks" ) ? m_account->configuration()[ "publishtracks" ].toBool() : true);
m_ui->xmppEnforceSecureCheckbox->setChecked( m_account->configuration().contains( "enforcesecure" ) ? m_account->configuration()[ "enforcesecure" ].toBool() : false);
}
void
XmppConfigWidget::onCheckJidExists( QString jid )
{
QList< Tomahawk::Accounts::Account* > accounts = Tomahawk::Accounts::AccountManager::instance()->accounts( Tomahawk::Accounts::SipType );
const QList< Tomahawk::Accounts::Account* > accounts = Tomahawk::Accounts::AccountManager::instance()->accounts( Tomahawk::Accounts::SipType );
foreach( Tomahawk::Accounts::Account* account, accounts )
{
if ( account->accountId() == m_account->accountId() )
continue;
XmppAccount* xmppAccount = qobject_cast< XmppAccount* >( account );
if ( !xmppAccount )
continue;
QString savedUsername = account->credentials()[ "username" ].toString();
QStringList savedSplitUsername = account->credentials()[ "username" ].toString().split("@");
QString savedServer = account->configuration()[ "server" ].toString();
int savedPort = account->configuration()[ "port" ].toInt();
// Check if any other xmpp account already uses the given name/settings
QString savedUsername = xmppAccount->credentials()[ "username" ].toString();
QStringList savedSplitUsername = xmppAccount->credentials()[ "username" ].toString().split("@");
QString savedServer = xmppAccount->configuration()[ "server" ].toString();
int savedPort = xmppAccount->configuration()[ "port" ].toInt();
if ( ( savedUsername == jid || savedSplitUsername.contains( jid ) ) &&
savedServer == m_ui->xmppServer->text() &&

View File

@@ -48,10 +48,15 @@ public:
virtual ~XmppConfigWidget();
void saveConfig();
void loadFromConfig();
signals:
void dataError( bool exists );
protected:
void showEvent( QShowEvent* event );
private slots:
void onCheckJidExists( QString jid );

View File

@@ -43,7 +43,7 @@ GoogleWrapperFactory::icon() const
return QPixmap( ":/gmail-logo.png" );
}
GoogleWrapperSip::GoogleWrapperSip( Account* account )
GoogleWrapperSip::GoogleWrapperSip( GoogleWrapper *account )
: XmppSipPlugin( account )
{

View File

@@ -44,11 +44,13 @@ public:
virtual Account* createAccount( const QString& pluginId );
};
class GoogleWrapper;
class ACCOUNTDLLEXPORT GoogleWrapperSip : public XmppSipPlugin
{
Q_OBJECT
public:
GoogleWrapperSip( Tomahawk::Accounts::Account* account );
GoogleWrapperSip( Tomahawk::Accounts::GoogleWrapper* account );
virtual ~GoogleWrapperSip();
public slots:

View File

@@ -50,6 +50,8 @@
#include <accounts/AccountManager.h>
#include <TomahawkSettings.h>
#include "../XmppAccount.h"
#ifndef ENABLE_HEADLESS
#include <QtGui/QInputDialog>
#include <QtGui/QLineEdit>
@@ -90,8 +92,9 @@ JreenMessageHandler( QtMsgType type, const char *msg )
}
XmppSipPlugin::XmppSipPlugin( Account* account )
XmppSipPlugin::XmppSipPlugin( XmppAccount *account )
: SipPlugin( account )
, m_xmppAccount( account )
, m_state( Account::Disconnected )
#ifndef ENABLE_HEADLESS
, m_menu( 0 )
@@ -221,7 +224,7 @@ XmppSipPlugin::connectPlugin()
return;
}
if ( m_account->configuration().contains( "enforcesecure" ) && m_account->configuration().value( "enforcesecure" ).toBool() )
if ( m_xmppAccount->configuration().contains( "enforcesecure" ) && m_xmppAccount->configuration().value( "enforcesecure" ).toBool() )
{
tLog() << Q_FUNC_INFO << "Enforcing secure connection...";
m_client->setFeatureConfig( Jreen::Client::Encryption, Jreen::Client::Force );
@@ -510,7 +513,7 @@ XmppSipPlugin::showAddFriendDialog()
void
XmppSipPlugin::publishTune( const QUrl& url, const InfoSystem::InfoStringHash& trackInfo )
{
if ( m_account->configuration().value("publishtracks").toBool() == false )
if ( m_xmppAccount->configuration().value("publishtracks").toBool() == false )
{
tDebug() << Q_FUNC_INFO << m_client->jid().full() << "Not publishing now playing info (disabled in account config)";
return;
@@ -578,6 +581,7 @@ XmppSipPlugin::configurationChanged()
server = readServer();
port = readPort();
qDebug() << Q_FUNC_INFO << "Configuration changed in SIP plugin:" << username << password << server << port << m_account->accountFriendlyName();
if ( m_currentUsername != username )
{
m_currentUsername = username;
@@ -602,19 +606,21 @@ XmppSipPlugin::configurationChanged()
if ( !m_currentUsername.contains( '@' ) )
{
m_currentUsername += defaultSuffix();
QVariantHash credentials = m_account->credentials();
QVariantHash credentials = m_xmppAccount->credentials();
credentials[ "username" ] = m_currentUsername;
m_account->setCredentials( credentials );
m_account->sync();
m_xmppAccount->saveCredentials( credentials );
}
qDebug() << Q_FUNC_INFO << "reconnecting?" << reconnect << "enabled?" << m_account->enabled() << m_account->accountFriendlyName();
if ( reconnect )
setupClientHelper();
if ( reconnect && m_account->enabled() )
{
qDebug() << Q_FUNC_INFO << "Reconnecting jreen plugin...";
disconnectPlugin();
setupClientHelper();
qDebug() << Q_FUNC_INFO << "Updated settings";
connectPlugin();
}
@@ -1035,23 +1041,21 @@ XmppSipPlugin::readXmlConsoleEnabled()
QString
XmppSipPlugin::readUsername()
{
QVariantHash credentials = m_account->credentials();
return credentials.contains( "username" ) ? credentials[ "username" ].toString() : QString();
return m_xmppAccount->credentials().value( "username" ).toString();
}
QString
XmppSipPlugin::readPassword()
{
QVariantHash credentials = m_account->credentials();
return credentials.contains( "password" ) ? credentials[ "password" ].toString() : QString();
return m_xmppAccount->credentials().value( "password" ).toString();
}
int
XmppSipPlugin::readPort()
{
QVariantHash configuration = m_account->configuration();
QVariantHash configuration = m_xmppAccount->configuration();
return configuration.contains( "port" ) ? configuration[ "port" ].toInt() : 5222;
}
@@ -1059,7 +1063,7 @@ XmppSipPlugin::readPort()
QString
XmppSipPlugin::readServer()
{
QVariantHash configuration = m_account->configuration();
QVariantHash configuration = m_xmppAccount->configuration();
return configuration.contains( "server" ) ? configuration[ "server" ].toString() : QString();
}

View File

@@ -52,6 +52,13 @@
#include "../XmppInfoPlugin.h"
namespace Tomahawk {
namespace Accounts {
class XmppAccount;
}
}
class ACCOUNTDLLEXPORT XmppSipPlugin : public SipPlugin
{
Q_OBJECT
@@ -59,7 +66,7 @@ class ACCOUNTDLLEXPORT XmppSipPlugin : public SipPlugin
friend class Tomahawk::InfoSystem::XmppInfoPlugin;
public:
XmppSipPlugin( Tomahawk::Accounts::Account* account );
XmppSipPlugin( Tomahawk::Accounts::XmppAccount* account );
virtual ~XmppSipPlugin();
//FIXME: Make this more correct
@@ -131,6 +138,7 @@ private:
int m_currentPort;
QString m_currentResource;
Tomahawk::Accounts::XmppAccount* m_xmppAccount;
QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_infoPlugin;
Tomahawk::Accounts::Account::ConnectionState m_state;

View File

@@ -327,6 +327,8 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.
${LIBECHONEST_INCLUDE_DIR}/..
${CLUCENE_INCLUDE_DIRS}
${PHONON_INCLUDES}
${QTKEYCHAIN_INCLUDE_DIRS}
${QTKEYCHAIN_INCLUDE_DIRS}/..
${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src
playlist
@@ -357,14 +359,14 @@ ENDIF( UNIX AND NOT APPLE )
IF( WIN32 )
SET( OS_SPECIFIC_LINK_LIBRARIES
${OS_SPECIFIC_LINK_LIBRARIES}
# System
# System
"iphlpapi.a"
"ws2_32.dll"
"dnsapi.dll"
"dsound.dll"
"winmm.dll"
"advapi32.dll"
"shlwapi.dll"
"shlwapi.dll"
)
ENDIF( WIN32 )
@@ -388,7 +390,7 @@ IF( APPLE )
${COREAUDIO_LIBRARY}
${COREFOUNDATION_LIBRARY}
${FOUNDATION_LIBRARY}
${SCRIPTINGBRIDGE_LIBRARY}
${SCRIPTINGBRIDGE_LIBRARY}
/System/Library/Frameworks/AppKit.framework
/System/Library/Frameworks/Security.framework
@@ -437,6 +439,7 @@ TARGET_LINK_LIBRARIES( tomahawklib
${OS_SPECIFIC_LINK_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${LINK_LIBRARIES}
${QTKEYCHAIN_LIBRARIES}
)
INSTALL( TARGETS tomahawklib

View File

@@ -21,6 +21,7 @@
#include "TomahawkSettings.h"
#include <QDir>
#include <qtkeychain/keychain.h>
#include "Source.h"
#include "sip/SipHandler.h"
@@ -589,6 +590,37 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
createSpotifyAccount();
}
}
else if ( oldVersion == 12 )
{
const QStringList accounts = value( "accounts/allaccounts" ).toStringList();
//Move storage of Credentials from QSettings to QtKeychain
foreach ( const QString& account, accounts )
{
beginGroup( QString( "accounts/%1" ).arg( account ) );
const QVariantHash creds = value( "credentials" ).toHash();
if ( !creds.isEmpty() )
{
QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this );
j->setKey( account );
j->setAutoDelete( false );
QByteArray data;
QDataStream ds( &data, QIODevice::WriteOnly );
ds << creds;
j->setBinaryData( data );
// connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) );
j->start();
qDebug() << "Migrating account credentials for account:" << account;
}
remove( "credentials" );
endGroup();
}
}
}

View File

@@ -28,7 +28,7 @@
#include <QtNetwork/QNetworkProxy>
#include <QStringList>
#define TOMAHAWK_SETTINGS_VERSION 12
#define TOMAHAWK_SETTINGS_VERSION 13
/**
* Convenience wrapper around QSettings for tomahawk-specific config

View File

@@ -20,6 +20,9 @@
#include "Account.h"
#include "TomahawkSettings.h"
#include "utils/Logger.h"
#include <qtkeychain/keychain.h>
namespace Tomahawk
{
@@ -115,6 +118,50 @@ Account::onError( int errorCode, const QString& error )
}
void
Account::keychainJobFinished( QKeychain::Job* j )
{
if ( j->error() == QKeychain::NoError )
{
if ( QKeychain::ReadPasswordJob* readJob = qobject_cast< QKeychain::ReadPasswordJob* >( j ) )
{
tLog() << Q_FUNC_INFO << "readJob finished without errors";
QVariantHash credentials;
QDataStream dataStream( readJob->binaryData() );
dataStream >> credentials;
tLog() << Q_FUNC_INFO << readJob->key();
emit credentialsLoaded( credentials );
}
else if ( QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ) )
{
tLog() << Q_FUNC_INFO << "writeJob finished";
}
else if ( QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ) )
{
tLog() << Q_FUNC_INFO << "deleteJob finished";
}
}
else
{
tLog() << Q_FUNC_INFO << "Job finished with error: " << j->error() << " " << j->errorString();
}
j->deleteLater();
m_workingKeychainJobs.removeAll( j );
if ( !m_queuedKeychainJobs.isEmpty() )
{
QKeychain::Job* j = m_queuedKeychainJobs.dequeue();
j->start();
m_workingKeychainJobs << j;
}
}
void
Account::onConnectionStateChanged( Account::ConnectionState )
{
@@ -129,7 +176,6 @@ Account::syncConfig()
s->beginGroup( "accounts/" + m_accountId );
s->setValue( "accountfriendlyname", m_accountFriendlyName );
s->setValue( "enabled", m_enabled );
s->setValue( "credentials", m_credentials );
s->setValue( "configuration", m_configuration );
s->setValue( "acl", m_acl );
s->setValue( "types", m_types );
@@ -138,6 +184,63 @@ Account::syncConfig()
}
void
Account::syncType()
{
TomahawkSettings* s = TomahawkSettings::instance();
s->beginGroup( "accounts/" + m_accountId );
s->setValue( "types", m_types );
s->endGroup();
s->sync();
}
void
Account::saveCredentials( const QVariantHash &creds )
{
QByteArray data;
{
QDataStream ds( &data, QIODevice::WriteOnly );
ds << creds;
}
QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this );
j->setKey( m_accountId );
j->setAutoDelete( false );
j->setBinaryData( data );
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
j->setInsecureFallback( true );
#endif
connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) );
j->start();
m_workingKeychainJobs << j;
}
void
Account::loadCredentials() const
{
QKeychain::ReadPasswordJob* j = new QKeychain::ReadPasswordJob( QLatin1String( "tomahawkaccounts" ), const_cast<Account*>( this ) );
j->setKey( m_accountId );
j->setAutoDelete( false );
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
j->setInsecureFallback( true );
#endif
connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) );
if ( !m_workingKeychainJobs.isEmpty() )
{
m_queuedKeychainJobs.enqueue( j );
}
else
{
j->start();
m_workingKeychainJobs << j;
}
}
void
Account::loadFromConfig( const QString& accountId )
{
@@ -146,11 +249,12 @@ Account::loadFromConfig( const QString& accountId )
s->beginGroup( "accounts/" + m_accountId );
m_accountFriendlyName = s->value( "accountfriendlyname", QString() ).toString();
m_enabled = s->value( "enabled", false ).toBool();
m_credentials = s->value( "credentials", QVariantHash() ).toHash();
m_configuration = s->value( "configuration", QVariantHash() ).toHash();
m_acl = s->value( "acl", QVariantMap() ).toMap();
m_types = s->value( "types", QStringList() ).toStringList();
s->endGroup();
loadCredentials();
}
@@ -161,12 +265,23 @@ Account::removeFromConfig()
s->beginGroup( "accounts/" + m_accountId );
s->remove( "accountfriendlyname" );
s->remove( "enabled" );
s->remove( "credentials" );
//s->remove( "credentials" );
s->remove( "configuration" );
s->remove( "acl" );
s->remove( "types" );
s->endGroup();
s->remove( "accounts/" + m_accountId );
QKeychain::DeletePasswordJob* j = new QKeychain::DeletePasswordJob( QLatin1String( "tomahawk" ), this );
j->setKey( m_accountId );
j->setAutoDelete( false );
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
j->setInsecureFallback( true );
#endif
connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) );
j->start();
m_workingKeychainJobs << j;
}
@@ -183,7 +298,8 @@ Account::setTypes( AccountTypes types )
m_types << "ResolverType";
if ( types & StatusPushType )
m_types << "StatusPushType";
syncConfig();
//syncConfig();
syncType();
}

View File

@@ -20,12 +20,13 @@
#ifndef ACCOUNT_H
#define ACCOUNT_H
#include <QtCore/QObject>
#include <QtCore/QVariantMap>
#include <QtGui/QWidget>
#include <QtGui/QIcon>
#include <QtCore/QString>
#include <QtCore/QUuid>
#include <QObject>
#include <QVariantMap>
#include <QWidget>
#include <QIcon>
#include <QString>
#include <QUuid>
#include <QQueue>
#include <QMutex>
#include "Typedefs.h"
@@ -35,6 +36,11 @@
class SipPlugin;
namespace QKeychain
{
class Job;
}
namespace Tomahawk
{
@@ -79,6 +85,13 @@ public:
QVariantHash configuration() const { QMutexLocker locker( &m_mutex ); return m_configuration; }
// Starts the asynchronous loading of the credentials.
// Will emit credentialsChanged() when they are successfully loaded
void loadCredentials() const;
// Asynchronously save credentials to storage
void saveCredentials( const QVariantHash& credentials );
/**
* Configuration widgets can have a "dataError( bool )" signal to enable/disable the OK button in their wrapper dialogs.
*/
@@ -87,8 +100,6 @@ public:
virtual void saveConfig() {} // called when the widget has been edited. save values from config widget, call sync() to write to disk account generic settings
QVariantHash credentials() const { QMutexLocker locker( &m_mutex ); return m_credentials; }
QVariantMap acl() const { QMutexLocker locker( &m_mutex ); return m_acl; }
virtual QWidget* aclWidget() = 0;
@@ -112,12 +123,11 @@ public:
void setAccountFriendlyName( const QString &friendlyName ) { QMutexLocker locker( &m_mutex ); m_accountFriendlyName = friendlyName; }
void setEnabled( bool enabled ) { QMutexLocker locker( &m_mutex ); m_enabled = enabled; }
void setAccountId( const QString &accountId ) { QMutexLocker locker( &m_mutex ); m_accountId = accountId; }
void setCredentials( const QVariantHash &credentialHash ) { QMutexLocker locker( &m_mutex ); m_credentials = credentialHash; }
void setConfiguration( const QVariantHash &configuration ) { QMutexLocker locker( &m_mutex ); m_configuration = configuration; }
void setAcl( const QVariantMap &acl ) { QMutexLocker locker( &m_mutex ); m_acl = acl; }
void setTypes( AccountTypes types );
void sync() { QMutexLocker locker( &m_mutex ); syncConfig(); };
void sync() { QMutexLocker locker( &m_mutex ); syncConfig(); }
/**
* Removes all the settings held in the config file for this account instance
@@ -136,24 +146,32 @@ signals:
void configurationChanged();
void credentialsLoaded( const QVariantHash& credentials );
protected:
virtual void loadFromConfig( const QString &accountId );
virtual void syncConfig();
virtual void syncType();
private slots:
void onConnectionStateChanged( Tomahawk::Accounts::Account::ConnectionState );
void onError( int, const QString& );
void keychainJobFinished( QKeychain::Job* );
private:
QString m_accountServiceName;
QString m_accountFriendlyName;
QString m_cachedError;
bool m_enabled;
QString m_accountId;
QVariantHash m_credentials;
QVariantHash m_configuration;
QVariantMap m_acl;
QStringList m_types;
mutable QList< QKeychain::Job* > m_workingKeychainJobs;
mutable QQueue< QKeychain::Job* > m_queuedKeychainJobs;
mutable QMutex m_mutex;
};

View File

@@ -416,6 +416,9 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role
if ( role == CheckboxClickedRole )
{
Account* acct = 0;
Qt::CheckState checkState = static_cast< Qt::CheckState >( value.toInt() );
switch ( node->type )
{
case AccountModelNode::UniqueFactoryType:
@@ -453,9 +456,11 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role
state = AtticaManager::Uninstalled;
}
if ( state == AtticaManager::Installed )
// Don't install if we're unchecking. This happens if e.g. the user deletes his config file
// and opens tomahawk
if ( state == AtticaManager::Installed || checkState == Qt::Unchecked )
{
qDebug() << "Already installed with resolver, just enabling";
qDebug() << "Already installed with resolver, or unchecking, just enabling/disabling";
acct = node->atticaAccount;
break;
}
@@ -491,13 +496,10 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role
if ( node->type == AccountModelNode::FactoryType )
{
// Turn on or off all accounts for this factory
Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() );
// Turn on or off all accounts for this factory\
foreach ( Account* acct, node->accounts )
{
state == Qt::Checked ? AccountManager::instance()->enableAccount( acct )
checkState == Qt::Checked ? AccountManager::instance()->enableAccount( acct )
: AccountManager::instance()->disableAccount( acct );
}

View File

@@ -55,6 +55,8 @@ LastFmAccountFactory::icon() const
LastFmAccount::LastFmAccount( const QString& accountId )
: CustomAtticaAccount( accountId )
{
connect( this, SIGNAL( credentialsLoaded( QVariantHash ) ), this, SLOT( onCredentialsLoaded( QVariantHash ) ) );
setAccountFriendlyName( "Last.Fm" );
m_icon.load( RESPATH "images/lastfm-icon.png" );
@@ -152,7 +154,7 @@ LastFmAccount::configurationWidget()
Account::ConnectionState
LastFmAccount::connectionState() const
{
return (!m_resolver.isNull() && m_resolver.data()->running()) ? Account::Connected : Account::Disconnected;
return ( !m_resolver.isNull() && m_resolver.data()->running() ) ? Account::Connected : Account::Disconnected;
}
@@ -189,57 +191,66 @@ LastFmAccount::saveConfig()
setScrobble( m_configWidget.data()->scrobble() );
}
sync();
saveCredentials( m_credentials );
}
if ( m_infoPlugin )
QTimer::singleShot( 0, m_infoPlugin.data(), SLOT( settingsChanged() ) );
void
LastFmAccount::onCredentialsLoaded(const QVariantHash &credentials)
{
m_credentials = credentials;
if ( !m_infoPlugin.isNull() )
m_infoPlugin.data()->settingsChanged();
}
QString
LastFmAccount::password() const
{
return credentials().value( "password" ).toString();
return m_credentials.value( "password" ).toString();
}
void
LastFmAccount::setPassword( const QString& password )
LastFmAccount::setPassword( const QString& password, bool immediate )
{
QVariantHash creds = credentials();
creds[ "password" ] = password;
setCredentials( creds );
m_credentials[ "password" ] = password;
if ( immediate )
saveCredentials( m_credentials );
}
QString
LastFmAccount::sessionKey() const
{
return credentials().value( "sessionkey" ).toString();
return m_credentials.value( "sessionkey" ).toString();
}
void
LastFmAccount::setSessionKey( const QString& sessionkey )
LastFmAccount::setSessionKey( const QString& sessionkey, bool immediate )
{
QVariantHash creds = credentials();
creds[ "sessionkey" ] = sessionkey;
setCredentials( creds );
m_credentials[ "sessionkey" ] = sessionkey;
if ( immediate )
saveCredentials( m_credentials );
}
QString
LastFmAccount::username() const
{
return credentials().value( "username" ).toString();
return m_credentials.value( "username" ).toString();
}
void
LastFmAccount::setUsername( const QString& username )
LastFmAccount::setUsername( const QString& username, bool immediate )
{
QVariantHash creds = credentials();
creds[ "username" ] = username;
setCredentials( creds );
m_credentials[ "username" ] = username;
if ( immediate )
saveCredentials( m_credentials );
}

View File

@@ -84,11 +84,11 @@ public:
virtual void saveConfig();
QString username() const;
void setUsername( const QString& );
void setUsername( const QString&, bool immediate = false );
QString password() const;
void setPassword( const QString& );
void setPassword( const QString&, bool immediate = false );
QString sessionKey() const;
void setSessionKey( const QString& );
void setSessionKey( const QString&, bool immediate = false );
bool scrobble() const;
void setScrobble( bool scrobble );
@@ -100,12 +100,18 @@ private slots:
void resolverInstalled( const QString& resolverId );
void resolverChanged();
void onCredentialsLoaded( const QVariantHash& credentials );
private:
void hookupResolver();
QWeakPointer<Tomahawk::ExternalResolverGui> m_resolver;
QWeakPointer<Tomahawk::InfoSystem::LastFmInfoPlugin> m_infoPlugin;
QWeakPointer<LastFmConfig> m_configWidget;
QVariantHash m_credentials;
QPixmap m_icon;
};

View File

@@ -40,14 +40,10 @@ LastFmConfig::LastFmConfig( LastFmAccount* account )
m_ui = new Ui_LastFmConfig;
m_ui->setupUi( this );
m_ui->progressBar->hide();
connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), this, SLOT( testLogin() ) );
m_ui->username->setText( m_account->username() );
m_ui->password->setText( m_account->password() );
m_ui->enable->setChecked( m_account->scrobble() );
connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), SLOT( testLogin() ) );
connect( m_ui->importHistory, SIGNAL( clicked( bool ) ), SLOT( loadHistory() ) );
connect( m_ui->username, SIGNAL( textChanged( QString ) ), this, SLOT( enableButton() ) );
connect( m_ui->password, SIGNAL( textChanged( QString ) ), this, SLOT( enableButton() ) );
connect( m_ui->username, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
connect( m_ui->password, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
@@ -68,6 +64,15 @@ LastFmConfig::scrobble() const
}
void
LastFmConfig::loadFromConfig()
{
m_ui->username->setText( m_account->username() );
m_ui->password->setText( m_account->password() );
m_ui->enable->setChecked( m_account->scrobble() );
}
QString
LastFmConfig::username() const
{
@@ -232,3 +237,10 @@ LastFmConfig::onLastFmFinished()
}
}
}
void
LastFmConfig::showEvent(QShowEvent* event)
{
loadFromConfig();
}

View File

@@ -38,10 +38,15 @@ public:
QString username() const;
QString password() const;
bool scrobble() const;
void loadFromConfig();
public slots:
void testLogin();
void onLastFmFinished();
protected:
void showEvent( QShowEvent* event );
private slots:
void enableButton();

View File

@@ -853,7 +853,7 @@ LastFmInfoPlugin::settingsChanged()
m_scrobbler = 0;
}
m_account.data()->setSessionKey( QString() );
m_account.data()->setSessionKey( QString(), true );
createScrobbler();
}
}
@@ -876,13 +876,13 @@ LastFmInfoPlugin::onAuthenticated()
if ( lfm.children( "error" ).size() > 0 )
{
tLog() << "Error from authenticating with Last.fm service:" << lfm.text();
m_account.data()->setSessionKey( QByteArray() );
m_account.data()->setSessionKey( QString(), true );
}
else
{
lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text();
m_account.data()->setSessionKey( lastfm::ws::SessionKey.toLatin1() );
m_account.data()->setSessionKey( lastfm::ws::SessionKey, true );
// qDebug() << "Got session key from last.fm";
if ( m_account.data()->scrobble() )

View File

@@ -100,6 +100,8 @@ SpotifyAccount::~SpotifyAccount()
void
SpotifyAccount::init()
{
connect( this, SIGNAL( credentialsLoaded( QVariantHash ) ), this, SLOT( onCredentialsLoaded( QVariantHash ) ) );
setAccountFriendlyName( "Spotify" );
setAccountServiceName( "spotify" );
@@ -182,6 +184,22 @@ SpotifyAccount::hookupResolver()
}
void
SpotifyAccount::onCredentialsLoaded( const QVariantHash &credentials )
{
m_credentials = credentials;
}
void
SpotifyAccount::setCredentials(const QVariantHash &creds)
{
m_credentials = creds;
saveCredentials( creds );
}
bool
SpotifyAccount::checkForResolver()
{
@@ -455,14 +473,12 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg
{
if ( msgType == "credentials" )
{
QVariantHash creds = credentials();
m_credentials[ "username" ] = msg.value( "username" );
m_credentials[ "password" ] = msg.value( "password" );
m_credentials[ "highQuality" ] = msg.value( "highQuality" );
saveCredentials( m_credentials );
creds[ "username" ] = msg.value( "username" );
creds[ "password" ] = msg.value( "password" );
creds[ "highQuality" ] = msg.value( "highQuality" );
setCredentials( creds );
qDebug() << "Set creds:" << creds.value( "username" ) << creds.value( "password" ) << msg.value( "username" ) << msg.value( "password" );
qDebug() << "Set creds:" << m_credentials.value( "username" ) << m_credentials.value( "password" ) << msg.value( "username" ) << msg.value( "password" );
QVariantHash config = configuration();
config[ "hasMigrated" ] = true;
@@ -613,12 +629,10 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg
}
else if ( msgType == "loginResponse" )
{
QVariantHash creds = credentials();
creds[ "username" ] = msg.value( "username" ).toString();
creds[ "password" ] = msg.value( "password" ).toString();
creds[ "highQuality" ] = msg.value( "highQuality" ).toString();
setCredentials( creds );
sync();
m_credentials[ "username" ] = msg.value( "username" ).toString();
m_credentials[ "password" ] = msg.value( "password" ).toString();
m_credentials[ "highQuality" ] = msg.value( "highQuality" ).toString();
saveCredentials( m_credentials );
const bool success = msg.value( "success" ).toBool();
@@ -725,15 +739,14 @@ SpotifyAccount::saveConfig()
if ( m_configWidget.isNull() )
return;
QVariantHash creds = credentials();
if ( creds.value( "username" ).toString() != m_configWidget.data()->username() ||
creds.value( "password" ).toString() != m_configWidget.data()->password() ||
creds.value( "highQuality" ).toBool() != m_configWidget.data()->highQuality() )
if ( m_credentials.value( "username" ).toString() != m_configWidget.data()->username() ||
m_credentials.value( "password" ).toString() != m_configWidget.data()->password() ||
m_credentials.value( "highQuality" ).toBool() != m_configWidget.data()->highQuality() )
{
creds[ "username" ] = m_configWidget.data()->username();
creds[ "password" ] = m_configWidget.data()->password();
creds[ "highQuality" ] = m_configWidget.data()->highQuality();
setCredentials( creds );
m_credentials[ "username" ] = m_configWidget.data()->username();
m_credentials[ "password" ] = m_configWidget.data()->password();
m_credentials[ "highQuality" ] = m_configWidget.data()->highQuality();
saveCredentials( m_credentials );
}

View File

@@ -102,12 +102,17 @@ public:
void setManualResolverPath( const QString& resolverPath );
QVariantHash credentials() const { return m_credentials; }
void setCredentials( const QVariantHash& creds );
public slots:
void aboutToShow( QAction* action, const Tomahawk::playlist_ptr& playlist );
void syncActionTriggered( bool );
void atticaLoaded(Attica::Content::List);
private slots:
void onCredentialsLoaded( const QVariantHash& credentials );
void resolverChanged();
void resolverInstalled( const QString& resolverId );
@@ -143,6 +148,8 @@ private:
QWeakPointer<QWidget> m_aboutWidget;
QWeakPointer<ScriptResolver> m_spotifyResolver;
QVariantHash m_credentials;
QMap<QString, QPair<QObject*, QString> > m_qidToSlotMap;
// List of synced spotify playlists in config UI

View File

@@ -34,6 +34,7 @@ SipPlugin::SipPlugin( Tomahawk::Accounts::Account *account, QObject* parent )
{
connect( this, SIGNAL( peerOnline( QString ) ), this, SLOT( onPeerOnline( QString ) ) );
connect( this, SIGNAL( peerOffline( QString ) ), this, SLOT( onPeerOffline( QString ) ) );
connect( m_account, SIGNAL( credentialsChanged( ) ), this, SLOT( configurationChanged( ) ) );
}

View File

@@ -261,7 +261,18 @@ Api_v1::resolve( QxtWebRequestEvent* event )
else
qid = uuid();
query_ptr qry = Query::get( QUrl::fromPercentEncoding( event->url.queryItemValue( "artist" ).toUtf8() ), QUrl::fromPercentEncoding( event->url.queryItemValue( "track" ).toUtf8() ), QUrl::fromPercentEncoding( event->url.queryItemValue( "album" ).toUtf8() ), qid, false );
const QString track = event->url.queryItemValue( "track" );
const QString artist = event->url.queryItemValue( "artist" );
const QString album = event->url.queryItemValue( "album" );
if ( track.isEmpty() && artist.isEmpty() )
{
qDebug() << "HTTP API asked to resolve empty track/artist!";
send404( event );
return;
}
query_ptr qry = Query::get( QUrl::fromPercentEncoding( artist.toUtf8() ), QUrl::fromPercentEncoding( track.toUtf8() ), QUrl::fromPercentEncoding( album.toUtf8() ), qid, false );
Pipeline::instance()->resolve( qry, true, true );
QVariantMap r;

View File

@@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 2.8)
project(qtkeychain)
###
set(QTKEYCHAIN_VERSION 0)
set(QTKEYCHAIN_SOVERSION 0)
###
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules")
include(GNUInstallDirs)
if(UNIX AND NOT APPLE)
find_package(Qt4 COMPONENTS QtCore QtDBus REQUIRED)
else()
find_package(Qt4 COMPONENTS QtCore REQUIRED)
endif()
include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
list(APPEND qtkeychain_LIBRARIES ${QT_QTCORE_LIBRARY})
set(qtkeychain_SOURCES
keychain.cpp
)
if(WIN32)
list(APPEND qtkeychain_SOURCES keychain_win.cpp)
list(APPEND qtkeychain_LIBRARIES crypt32)
#FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there
if(MINGW)
add_definitions( -O2 )
endif()
endif()
if(APPLE)
list(APPEND qtkeychain_SOURCES keychain_mac.cpp)
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY})
find_library(SECURITY_LIBRARY Security)
list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY})
endif()
if(UNIX AND NOT APPLE)
list(APPEND qtkeychain_SOURCES keychain_dbus.cpp)
qt4_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface)
list(APPEND qtkeychain_LIBRARIES ${QT_QTDBUS_LIBRARY})
endif()
QT4_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h)
if(NOT QTKEYCHAIN_STATIC)
add_library(qtkeychain SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES})
set_target_properties(qtkeychain PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_BUILD_QKEYCHAIN_LIB)
target_link_libraries(qtkeychain ${qtkeychain_LIBRARIES})
else()
add_library(qtkeychain STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES})
set_target_properties(qtkeychain PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_STATICLIB)
endif()
set_target_properties(qtkeychain PROPERTIES
VERSION ${QTKEYCHAIN_VERSION}
SOVERSION ${QTKEYCHAIN_SOVERSION}
)
#install(FILES keychain.h qkeychain_export.h
# DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qtkeychain/
#)
#install(TARGETS qtkeychain
# EXPORT QtKeychainLibraryDepends
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
#)
#add_executable( testclient testclient.cpp )
#target_link_libraries( testclient qtkeychain)
###
### CMake config file
###
export(TARGETS qtkeychain FILE "${PROJECT_BINARY_DIR}/QtKeychainLibraryDepends.cmake")
export(PACKAGE QtKeychain)
configure_file(QtKeychainBuildTreeSettings.cmake.in
"${PROJECT_BINARY_DIR}/QtKeychainBuildTreeSettings.cmake" @ONLY)
configure_file(QtKeychainConfig.cmake.in
"${PROJECT_BINARY_DIR}/QtKeychainConfig.cmake" @ONLY)
configure_file(QtKeychainConfigVersion.cmake.in
"${PROJECT_BINARY_DIR}/QtKeychainConfigVersion.cmake" @ONLY)
#install(EXPORT QtKeychainLibraryDepends
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain"
#)
#install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfig.cmake
# ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfigVersion.cmake
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain
#)

View File

@@ -0,0 +1,20 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,4 @@
set(QTKEYCHAIN_INCLUDE_DIRS
"@PROJECT_SOURCE_DIR@"
"@PROJECT_BINARY_DIR@"
)

View File

@@ -0,0 +1,20 @@
# - Config file for the FooBar package
# It defines the following variables
# FOOBAR_INCLUDE_DIRS - include directories for FooBar
# FOOBAR_LIBRARIES - libraries to link against
# FOOBAR_EXECUTABLE - the bar executable
# Compute paths
get_filename_component(QTKEYCHAIN_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
if(EXISTS "${QTKEYCHAIN_CMAKE_DIR}/CMakeCache.txt")
# In build tree
include("${QTKEYCHAIN_CMAKE_DIR}/QtKeychainBuildTreeSettings.cmake")
else()
set(QTKEYCHAIN_INCLUDE_DIRS "${QTKEYCHAIN_CMAKE_DIR}/../../")
endif()
# Our library dependencies (contains definitions for IMPORTED targets)
include("${QTKEYCHAIN_CMAKE_DIR}/QtKeychainLibraryDepends.cmake")
# These are IMPORTED targets created by FooBarLibraryDepends.cmake
set(QTKEYCHAIN_LIBRARIES qtkeychain)

View File

@@ -0,0 +1,11 @@
set(PACKAGE_VERSION "@QTKEYCHAIN_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@@ -0,0 +1 @@
ReadMe.txt

View File

@@ -0,0 +1,16 @@
QtKeychain
==========
QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform:
* **Mac OS X:** Passwords are stored in the OS X Keychain.
* **Linux/Unix:** If running, KWallet (via D-Bus) is used.
Support for the GNOME Keyring via freedesktop.org's
[Secret Storage D-Bus specification](http://freedesktop.org/wiki/Specifications/secret-storage-spec "Secret Storage specification") is planned but not yet implemented.
* **Windows:** Windows does not provide a service for secure storage. QtKeychain uses the Windows API function [CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings.
In unsupported environments QtKeychain will report an error. It will never store any data unencrypted.
**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details.

View File

@@ -0,0 +1,188 @@
# - Define GNU standard installation directories
# Provides install directory variables as defined for GNU software:
# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
# Inclusion of this module defines the following variables:
# CMAKE_INSTALL_<dir> - destination for files of a given type
# CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
# where <dir> is one of:
# BINDIR - user executables (bin)
# SBINDIR - system admin executables (sbin)
# LIBEXECDIR - program executables (libexec)
# SYSCONFDIR - read-only single-machine data (etc)
# SHAREDSTATEDIR - modifiable architecture-independent data (com)
# LOCALSTATEDIR - modifiable single-machine data (var)
# LIBDIR - object code libraries (lib or lib64 or lib/<multiarch-tuple> on Debian)
# INCLUDEDIR - C header files (include)
# OLDINCLUDEDIR - C header files for non-gcc (/usr/include)
# DATAROOTDIR - read-only architecture-independent data root (share)
# DATADIR - read-only architecture-independent data (DATAROOTDIR)
# INFODIR - info documentation (DATAROOTDIR/info)
# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale)
# MANDIR - man documentation (DATAROOTDIR/man)
# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME)
# Each CMAKE_INSTALL_<dir> value may be passed to the DESTINATION options of
# install() commands for the corresponding file type. If the includer does
# not define a value the above-shown default will be used and the value will
# appear in the cache for editing by the user.
# Each CMAKE_INSTALL_FULL_<dir> value contains an absolute path constructed
# from the corresponding destination by prepending (if necessary) the value
# of CMAKE_INSTALL_PREFIX.
#=============================================================================
# Copyright 2011 Nikita Krupen'ko <krnekit@gmail.com>
# Copyright 2011 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
# Installation directories
#
if(NOT DEFINED CMAKE_INSTALL_BINDIR)
set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SBINDIR)
set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR)
set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR)
set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR)
set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR)
set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(_LIBDIR_DEFAULT "lib")
# Override this default 'lib' with 'lib64' iff:
# - we are on Linux system but NOT cross-compiling
# - we are NOT on debian
# - we are on a 64 bits system
# reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf
# For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if
# CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu"
# See http://wiki.debian.org/Multiarch
if(CMAKE_SYSTEM_NAME MATCHES "Linux"
AND NOT CMAKE_CROSSCOMPILING)
if (EXISTS "/etc/debian_version") # is this a debian system ?
if(CMAKE_LIBRARY_ARCHITECTURE)
set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
endif()
else() # not debian, rely on CMAKE_SIZEOF_VOID_P:
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
message(AUTHOR_WARNING
"Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
"Please enable at least one language before including GNUInstallDirs.")
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_LIBDIR_DEFAULT "lib64")
endif()
endif()
endif()
endif()
set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})")
endif()
if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR)
set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR)
set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)")
endif()
#-----------------------------------------------------------------------------
# Values whose defaults are relative to DATAROOTDIR. Store empty values in
# the cache and store the defaults in local variables if the cache values are
# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes.
if(NOT CMAKE_INSTALL_DATADIR)
set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)")
set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}")
endif()
if(NOT CMAKE_INSTALL_INFODIR)
set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)")
set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info")
endif()
if(NOT CMAKE_INSTALL_LOCALEDIR)
set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)")
set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale")
endif()
if(NOT CMAKE_INSTALL_MANDIR)
set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)")
set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man")
endif()
if(NOT CMAKE_INSTALL_DOCDIR)
set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)")
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}")
endif()
#-----------------------------------------------------------------------------
mark_as_advanced(
CMAKE_INSTALL_BINDIR
CMAKE_INSTALL_SBINDIR
CMAKE_INSTALL_LIBEXECDIR
CMAKE_INSTALL_SYSCONFDIR
CMAKE_INSTALL_SHAREDSTATEDIR
CMAKE_INSTALL_LOCALSTATEDIR
CMAKE_INSTALL_LIBDIR
CMAKE_INSTALL_INCLUDEDIR
CMAKE_INSTALL_OLDINCLUDEDIR
CMAKE_INSTALL_DATAROOTDIR
CMAKE_INSTALL_DATADIR
CMAKE_INSTALL_INFODIR
CMAKE_INSTALL_LOCALEDIR
CMAKE_INSTALL_MANDIR
CMAKE_INSTALL_DOCDIR
)
# Result directories
#
foreach(dir
BINDIR
SBINDIR
LIBEXECDIR
SYSCONFDIR
SHAREDSTATEDIR
LOCALSTATEDIR
LIBDIR
INCLUDEDIR
OLDINCLUDEDIR
DATAROOTDIR
DATADIR
INFODIR
LOCALEDIR
MANDIR
DOCDIR
)
if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}})
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}")
else()
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}")
endif()
endforeach()

View File

@@ -0,0 +1,173 @@
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain.h"
#include "keychain_p.h"
using namespace QKeychain;
Job::Job( const QString& service, QObject *parent )
: QObject( parent )
, d ( new Private( service ) ) {
}
Job::~Job() {
delete d;
}
QString Job::service() const {
return d->service;
}
QSettings* Job::settings() const {
return d->settings;
}
void Job::setSettings( QSettings* settings ) {
d->settings = settings;
}
void Job::start() {
QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection );
}
bool Job::autoDelete() const {
return d->autoDelete;
}
void Job::setAutoDelete( bool autoDelete ) {
d->autoDelete = autoDelete;
}
bool Job::insecureFallback() const {
return d->insecureFallback;
}
void Job::setInsecureFallback( bool insecureFallback ) {
d->insecureFallback = insecureFallback;
}
void Job::emitFinished() {
emit finished( this );
if ( d->autoDelete )
deleteLater();
}
void Job::emitFinishedWithError( Error error, const QString& errorString ) {
d->error = error;
d->errorString = errorString;
emitFinished();
}
Error Job::error() const {
return d->error;
}
QString Job::errorString() const {
return d->errorString;
}
void Job::setError( Error error ) {
d->error = error;
}
void Job::setErrorString( const QString& errorString ) {
d->errorString = errorString;
}
ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new Private( this ) )
{}
ReadPasswordJob::~ReadPasswordJob() {
delete d;
}
QString ReadPasswordJob::textData() const {
return QString::fromUtf8( d->data );
}
QByteArray ReadPasswordJob::binaryData() const {
return d->data;
}
QString ReadPasswordJob::key() const {
return d->key;
}
void ReadPasswordJob::setKey( const QString& key ) {
d->key = key;
}
void ReadPasswordJob::doStart() {
d->doStart();
}
WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new Private( this ) ) {
}
WritePasswordJob::~WritePasswordJob() {
delete d;
}
QString WritePasswordJob::key() const {
return d->key;
}
void WritePasswordJob::setKey( const QString& key ) {
d->key = key;
}
void WritePasswordJob::setBinaryData( const QByteArray& data ) {
d->binaryData = data;
d->mode = Private::Binary;
}
void WritePasswordJob::setTextData( const QString& data ) {
d->textData = data;
d->mode = Private::Text;
}
void WritePasswordJob::doStart() {
d->doStart();
}
DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new Private( this ) ) {
}
DeletePasswordJob::~DeletePasswordJob() {
delete d;
}
void DeletePasswordJob::doStart() {
//Internally, to delete a password we just execute a write job with no data set (null byte array).
//In all current implementations, this deletes the entry so this is sufficient
WritePasswordJob* job = new WritePasswordJob( service(), this );
connect( job, SIGNAL(finished(QKeychain::Job*)), d, SLOT(jobFinished(QKeychain::Job*)) );
job->setKey( d->key );
job->start();
}
QString DeletePasswordJob::key() const {
return d->key;
}
void DeletePasswordJob::setKey( const QString& key ) {
d->key = key;
}
void DeletePasswordJob::Private::jobFinished( Job* job ) {
q->setError( job->error() );
q->setErrorString( job->errorString() );
q->emitFinished();
}

View File

@@ -0,0 +1,132 @@
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#ifndef KEYCHAIN_H
#define KEYCHAIN_H
#include "qkeychain_export.h"
#include <QtCore/QObject>
#include <QtCore/QString>
class QSettings;
namespace QKeychain {
/**
* Error codes
*/
enum Error {
NoError=0, /**< No error occurred, operation was successful */
EntryNotFound, /**< For the given key no data was found */
CouldNotDeleteEntry, /**< Could not delete existing secret data */
AccessDeniedByUser, /**< User denied access to keychain */
AccessDenied, /**< Access denied for other reasons */
NoBackendAvailable, /**< No platform-specific keychain service available */
NotImplemented, /**< Not implemented on platform */
OtherError /**< Something else went wrong (errorString() might provide details) */
};
class QKEYCHAIN_EXPORT Job : public QObject {
Q_OBJECT
public:
explicit Job( const QString& service, QObject* parent=0 );
~Job();
QSettings* settings() const;
void setSettings( QSettings* settings );
void start();
QString service() const;
Error error() const;
QString errorString() const;
bool autoDelete() const;
void setAutoDelete( bool autoDelete );
bool insecureFallback() const;
void setInsecureFallback( bool insecureFallback );
Q_SIGNALS:
void finished( QKeychain::Job* );
protected:
Q_INVOKABLE virtual void doStart() = 0;
void setError( Error error );
void setErrorString( const QString& errorString );
void emitFinished();
void emitFinishedWithError(Error, const QString& errorString);
private:
class Private;
Private* const d;
};
class QKEYCHAIN_EXPORT ReadPasswordJob : public Job {
Q_OBJECT
public:
explicit ReadPasswordJob( const QString& service, QObject* parent=0 );
~ReadPasswordJob();
QString key() const;
void setKey( const QString& key );
QByteArray binaryData() const;
QString textData() const;
protected:
void doStart();
private:
class Private;
Private* const d;
};
class QKEYCHAIN_EXPORT WritePasswordJob : public Job {
Q_OBJECT
public:
explicit WritePasswordJob( const QString& service, QObject* parent=0 );
~WritePasswordJob();
QString key() const;
void setKey( const QString& key );
void setBinaryData( const QByteArray& data );
void setTextData( const QString& data );
protected:
void doStart();
private:
class Private;
Private* const d;
};
class QKEYCHAIN_EXPORT DeletePasswordJob : public Job {
Q_OBJECT
public:
explicit DeletePasswordJob( const QString& service, QObject* parent=0 );
~DeletePasswordJob();
QString key() const;
void setKey( const QString& key );
protected:
void doStart();
private:
class Private;
Private* const d;
};
} // namespace QtKeychain
#endif

View File

@@ -0,0 +1,160 @@
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain_p.h"
#include <QSettings>
#include <auto_ptr.h>
using namespace QKeychain;
void ReadPasswordJob::Private::doStart() {
iface = new org::kde::KWallet( QLatin1String("org.kde.kwalletd"), QLatin1String("/modules/kwalletd"), QDBusConnection::sessionBus(), this );
const QDBusPendingReply<int> reply = iface->open( QLatin1String("kdewallet"), 0, q->service() );
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, this );
connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) );
}
void ReadPasswordJob::Private::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
const QDBusPendingReply<int> reply = *watcher;
if ( reply.isError() ) {
const QDBusError err = reply.error();
if ( q->insecureFallback() ) {
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
data = actual->value( key ).toByteArray();
q->emitFinished();
} else {
if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running
q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") );
else
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
}
walletHandle = reply.value();
if ( walletHandle < 0 ) {
q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
return;
}
const QDBusPendingReply<int> nextReply = iface->entryType( walletHandle, q->service(), key, q->service() );
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher*)) );
}
void ReadPasswordJob::Private::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
if ( watcher->isError() ) {
const QDBusError err = watcher->error();
q->emitFinishedWithError( OtherError, tr("Could not determine data type: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
const QDBusPendingReply<int> reply = *watcher;
dataType = reply.value() == 1/*Password*/ ? Text : Binary;
const QDBusPendingCall nextReply = dataType == Text
? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) )
: QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) );
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletReadFinished(QDBusPendingCallWatcher*)) );
}
void ReadPasswordJob::Private::kwalletReadFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
if ( watcher->isError() ) {
const QDBusError err = watcher->error();
q->emitFinishedWithError( OtherError, tr("Could not read password: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
if ( dataType == Binary ) {
QDBusPendingReply<QByteArray> reply = *watcher;
data = reply.value();
} else {
QDBusPendingReply<QString> reply = *watcher;
data = reply.value().toUtf8();
}
q->emitFinished();
}
void WritePasswordJob::Private::doStart() {
iface = new org::kde::KWallet( QLatin1String("org.kde.kwalletd"), QLatin1String("/modules/kwalletd"), QDBusConnection::sessionBus(), this );
const QDBusPendingReply<int> reply = iface->open( QLatin1String("kdewallet"), 0, q->service() );
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, this );
connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) );
}
void WritePasswordJob::Private::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
QDBusPendingReply<int> reply = *watcher;
if ( reply.isError() ) {
if ( q->insecureFallback() ) {
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
if ( mode == Delete ) {
actual->remove( key );
actual->sync();
q->emitFinished();
return;
}
const QByteArray data = mode == Binary ? binaryData : textData.toUtf8();
actual->setValue( key, data );
actual->sync();
q->emitFinished();
} else {
const QDBusError err = reply.error();
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
}
return;
}
const int handle = reply.value();
if ( handle < 0 ) {
q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
return;
}
QDBusPendingReply<int> nextReply;
if ( !textData.isEmpty() )
nextReply = iface->writePassword( handle, q->service(), key, textData, q->service() );
else if ( !binaryData.isEmpty() )
nextReply = iface->writeEntry( handle, q->service(), key, binaryData, q->service() );
else
nextReply = iface->removeEntry( handle, q->service(), key, q->service() );
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletWriteFinished(QDBusPendingCallWatcher*)) );
}
void WritePasswordJob::Private::kwalletWriteFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
QDBusPendingReply<int> reply = *watcher;
if ( reply.isError() ) {
const QDBusError err = reply.error();
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
q->emitFinished();
}

View File

@@ -0,0 +1,159 @@
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain_p.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include <QDebug>
using namespace QKeychain;
template <typename T>
struct Releaser {
explicit Releaser( const T& v ) : value( v ) {}
~Releaser() {
CFRelease( value );
}
const T value;
};
static QString strForStatus( OSStatus os ) {
const Releaser<CFStringRef> str( SecCopyErrorMessageString( os, 0 ) );
const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 );
if ( !buf )
return QString();
return QString::fromUtf8( buf, strlen( buf ) );
}
static OSStatus readPw( QByteArray* pw,
const QString& service,
const QString& account,
SecKeychainItemRef* ref ) {
Q_ASSERT( pw );
pw->clear();
const QByteArray serviceData = service.toUtf8();
const QByteArray accountData = account.toUtf8();
void* data = 0;
UInt32 len = 0;
const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain
serviceData.size(),
serviceData.constData(),
accountData.size(),
accountData.constData(),
&len,
&data,
ref );
if ( ret == noErr ) {
*pw = QByteArray( reinterpret_cast<const char*>( data ), len );
const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data );
if ( ret2 != noErr )
qWarning() << "Could not free item content: " << strForStatus( ret2 );
}
return ret;
}
void ReadPasswordJob::Private::doStart()
{
QString errorString;
Error error = NoError;
const OSStatus ret = readPw( &data, q->service(), q->key(), 0 );
switch ( ret ) {
case noErr:
break;
case errSecItemNotFound:
errorString = tr("Password not found");
error = EntryNotFound;
break;
default:
errorString = strForStatus( ret );
error = OtherError;
break;
}
q->emitFinishedWithError( error, errorString );
}
static QKeychain::Error deleteEntryImpl( const QString& service, const QString& account, QString* err ) {
SecKeychainItemRef ref;
QByteArray pw;
const OSStatus ret1 = readPw( &pw, service, account, &ref );
if ( ret1 == errSecItemNotFound )
return NoError; // No item stored, we're done
if ( ret1 != noErr ) {
*err = strForStatus( ret1 );
//TODO map error code, set errstr
return OtherError;
}
const Releaser<SecKeychainItemRef> releaser( ref );
const OSStatus ret2 = SecKeychainItemDelete( ref );
if ( ret2 == noErr )
return NoError;
//TODO map error code
*err = strForStatus( ret2 );
return CouldNotDeleteEntry;
}
static QKeychain::Error writeEntryImpl( const QString& service,
const QString& account,
const QByteArray& data,
QString* err ) {
Q_ASSERT( err );
err->clear();
const QByteArray serviceData = service.toUtf8();
const QByteArray accountData = account.toUtf8();
const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain
serviceData.size(),
serviceData.constData(),
accountData.size(),
accountData.constData(),
data.size(),
data.constData(),
NULL //item reference
);
if ( ret != noErr ) {
switch ( ret ) {
case errSecDuplicateItem:
{
Error derr = deleteEntryImpl( service, account, err );
if ( derr != NoError )
return CouldNotDeleteEntry;
else
return writeEntryImpl( service, account, data, err );
}
default:
*err = strForStatus( ret );
return OtherError;
}
}
return NoError;
}
void WritePasswordJob::Private::doStart()
{
QString errorString;
Error error = NoError;
if ( mode == Delete ) {
const Error derr = deleteEntryImpl( q->service(), key, &errorString );
if ( derr != NoError )
error = CouldNotDeleteEntry;
q->emitFinishedWithError( error, errorString );
return;
}
const QByteArray data = mode == Text ? textData.toUtf8() : binaryData;
error = writeEntryImpl( q->service(), key, data, &errorString );
q->emitFinishedWithError( error, errorString );
}

View File

@@ -0,0 +1,122 @@
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#ifndef KEYCHAIN_P_H
#define KEYCHAIN_P_H
#include <QCoreApplication>
#include <QObject>
#include <QPointer>
#include <QSettings>
#if defined(Q_OS_UNIX) && !defined(Q_WS_MAC)
#include <QDBusPendingCallWatcher>
#include "kwallet_interface.h"
#else
class QDBusPendingCallWatcher;
#endif
#include "keychain.h"
namespace QKeychain {
class Job::Private : public QObject {
Q_OBJECT
public:
Private( const QString& service_ )
: error( NoError )
, service( service_ )
, autoDelete( true )
, insecureFallback( false ) {}
QKeychain::Error error;
QString errorString;
QString service;
bool autoDelete;
bool insecureFallback;
QPointer<QSettings> settings;
};
class ReadPasswordJob::Private : public QObject {
Q_OBJECT
public:
explicit Private( ReadPasswordJob* qq ) : q( qq ), walletHandle( 0 ), dataType( Text ) {}
void doStart();
ReadPasswordJob* const q;
QByteArray data;
QString key;
int walletHandle;
enum DataType {
Binary,
Text
};
DataType dataType;
#if defined(Q_OS_UNIX) && !defined(Q_WS_MAC)
org::kde::KWallet* iface;
private Q_SLOTS:
void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher );
void kwalletReadFinished( QDBusPendingCallWatcher* watcher );
#else //moc's too dumb to respect above macros, so just define empty slot implementations
private Q_SLOTS:
void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {}
void kwalletReadFinished( QDBusPendingCallWatcher* ) {}
#endif
};
class WritePasswordJob::Private : public QObject {
Q_OBJECT
public:
explicit Private( WritePasswordJob* qq ) : q( qq ), mode( Delete ) {}
void doStart();
enum Mode {
Delete,
Text,
Binary
};
WritePasswordJob* const q;
Mode mode;
QString key;
QByteArray binaryData;
QString textData;
#if defined(Q_OS_UNIX) && !defined(Q_WS_MAC)
org::kde::KWallet* iface;
private Q_SLOTS:
void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
void kwalletWriteFinished( QDBusPendingCallWatcher* watcher );
#else
private Q_SLOTS:
void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
void kwalletWriteFinished( QDBusPendingCallWatcher* ) {}
#endif
};
class DeletePasswordJob::Private : public QObject {
Q_OBJECT
public:
explicit Private( DeletePasswordJob* qq ) : q( qq ) {}
void doStart();
DeletePasswordJob* const q;
QString key;
private Q_SLOTS:
void jobFinished( QKeychain::Job* );
};
}
#endif // KEYCHAIN_P_H

View File

@@ -0,0 +1,107 @@
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain_p.h"
#include <QSettings>
#include <windows.h>
#include <wincrypt.h>
#include <memory>
using namespace QKeychain;
void ReadPasswordJob::Private::doStart() {
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
QByteArray encrypted = actual->value( key ).toByteArray();
if ( encrypted.isNull() ) {
q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
return;
}
DATA_BLOB blob_in, blob_out;
blob_in.pbData = reinterpret_cast<BYTE*>( encrypted.data() );
blob_in.cbData = encrypted.size();
const BOOL ret = CryptUnprotectData( &blob_in,
NULL,
NULL,
NULL,
NULL,
0,
&blob_out );
if ( !ret ) {
q->emitFinishedWithError( OtherError, tr("Could not decrypt data") );
return;
}
data = QByteArray( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
SecureZeroMemory( blob_out.pbData, blob_out.cbData );
LocalFree( blob_out.pbData );
q->emitFinished();
}
void WritePasswordJob::Private::doStart() {
if ( mode == Delete ) {
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
actual->remove( key );
actual->sync();
if ( actual->status() != QSettings::NoError ) {
const QString err = actual->status() == QSettings::AccessError
? tr("Could not delete encrypted data from settings: access error")
: tr("Could not delete encrypted data from settings: format error");
q->emitFinishedWithError( OtherError, err );
} else {
q->emitFinished();
}
return;
}
QByteArray data = mode == Binary ? binaryData : textData.toUtf8();
DATA_BLOB blob_in, blob_out;
blob_in.pbData = reinterpret_cast<BYTE*>( data.data() );
blob_in.cbData = data.size();
const BOOL res = CryptProtectData( &blob_in,
L"QKeychain-encrypted data",
NULL,
NULL,
NULL,
0,
&blob_out );
if ( !res ) {
q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available?
return;
}
const QByteArray encrypted( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
LocalFree( blob_out.pbData );
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
actual->setValue( key, encrypted );
actual->sync();
if ( actual->status() != QSettings::NoError ) {
const QString errorString = actual->status() == QSettings::AccessError
? tr("Could not store encrypted data in settings: access error")
: tr("Could not store encrypted data in settings: format error");
q->emitFinishedWithError( OtherError, errorString );
return;
}
q->emitFinished();
}

View File

@@ -0,0 +1,276 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.KWallet">
<signal name="walletListDirty">
</signal>
<signal name="walletCreated">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletOpened">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletAsyncOpened">
<arg name="tId" type="i" direction="out"/>
<arg name="handle" type="i" direction="out"/>
</signal>
<signal name="walletDeleted">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletClosed">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletClosed">
<arg name="handle" type="i" direction="out"/>
</signal>
<signal name="allWalletsClosed">
</signal>
<signal name="folderListUpdated">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="folderUpdated">
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
</signal>
<signal name="applicationDisconnected">
<arg name="wallet" type="s" direction="out"/>
<arg name="application" type="s" direction="out"/>
</signal>
<method name="isEnabled">
<arg type="b" direction="out"/>
</method>
<method name="open">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="openPath">
<arg type="i" direction="out"/>
<arg name="path" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="openAsync">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
<arg name="handleSession" type="b" direction="in"/>
</method>
<method name="openPathAsync">
<arg type="i" direction="out"/>
<arg name="path" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
<arg name="handleSession" type="b" direction="in"/>
</method>
<method name="close">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="force" type="b" direction="in"/>
</method>
<method name="close">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="force" type="b" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="sync">
<arg name="handle" type="i" direction="in"/>
<arg name="appid" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="deleteWallet">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
</method>
<method name="isOpen">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
</method>
<method name="isOpen">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
</method>
<method name="users">
<arg type="as" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
</method>
<method name="changePassword">
<arg name="wallet" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="wallets">
<arg type="as" direction="out"/>
</method>
<method name="folderList">
<arg type="as" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="hasFolder">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="createFolder">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="removeFolder">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="entryList">
<arg type="as" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readEntry">
<arg type="ay" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readMap">
<arg type="ay" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readPassword">
<arg type="s" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readEntryList">
<arg type="a{sv}" direction="out"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readMapList">
<arg type="a{sv}" direction="out"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readPasswordList">
<arg type="a{sv}" direction="out"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="renameEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="oldName" type="s" direction="in"/>
<arg name="newName" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writeEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="ay" direction="in"/>
<arg name="entryType" type="i" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writeEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="ay" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writeMap">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="ay" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writePassword">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="hasEntry">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="entryType">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="removeEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="disconnectApplication">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="application" type="s" direction="in"/>
</method>
<method name="reconfigure">
</method>
<method name="folderDoesNotExist">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="folder" type="s" direction="in"/>
</method>
<method name="keyDoesNotExist">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
</method>
<method name="closeAllWallets">
</method>
<method name="networkWallet">
<arg type="s" direction="out"/>
</method>
<method name="localWallet">
<arg type="s" direction="out"/>
</method>
<method name="pamOpen">
<arg name="wallet" type="s" direction="in"/>
<arg name="passwordHash" type="ay" direction="in"/>
<arg name="sessionTimeout" type="i" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
</interface>
</node>

View File

@@ -0,0 +1,17 @@
#ifndef QKEYCHAIN_EXPORT_H
#define QKEYCHAIN_EXPORT_H
#include <qglobal.h>
# ifdef QKEYCHAIN_STATICLIB
# undef QKEYCHAIN_SHAREDLIB
# define QKEYCHAIN_EXPORT
# else
# ifdef QKEYCHAIN_BUILD_QKEYCHAIN_LIB
# define QKEYCHAIN_EXPORT Q_DECL_EXPORT
# else
# define QKEYCHAIN_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif

View File

@@ -0,0 +1,98 @@
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include <QCoreApplication>
#include <QStringList>
#include "keychain.h"
#include <iostream>
using namespace QKeychain;
static int printUsage() {
std::cerr << "testclient store <account> <password>" << std::endl;
std::cerr << "testclient restore <account>" << std::endl;
std::cerr << "testclient delete <account>" << std::endl;
return 1;
}
int main( int argc, char** argv ) {
QCoreApplication app( argc, argv );
const QStringList args = app.arguments();
if ( args.count() < 2 )
return printUsage();
QStringList::ConstIterator it = args.constBegin();
++it;
if ( *it == QLatin1String("store") ) {
if ( ++it == args.constEnd() )
return printUsage();
const QString acc = *it;
if ( ++it == args.constEnd() )
return printUsage();
const QString pass = *it;
if ( ++it != args.constEnd() )
return printUsage();
WritePasswordJob job( QLatin1String("qtkeychain-testclient") );
job.setAutoDelete( false );
job.setKey( acc );
job.setTextData( pass );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
if ( job.error() ) {
std::cerr << "Storing password failed: " << qPrintable(job.errorString()) << std::endl;
return 1;
}
std::cout << "Password stored successfully" << std::endl;
} else if ( *it == QLatin1String("restore") ) {
if ( ++it == args.constEnd() )
return printUsage();
const QString acc = *it;
if ( ++it != args.constEnd() )
return printUsage();
ReadPasswordJob job( QLatin1String("qtkeychain-testclient") );
job.setAutoDelete( false );
job.setKey( acc );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
const QString pw = job.textData();
if ( job.error() ) {
std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) << std::endl;
return 1;
}
std::cout << qPrintable(pw) << std::endl;
} else if ( *it == QLatin1String("delete") ) {
if ( ++it == args.constEnd() )
return printUsage();
const QString acc = *it;
if ( ++it != args.constEnd() )
return printUsage();
DeletePasswordJob job( QLatin1String("qtkeychain-testclient") );
job.setAutoDelete( false );
job.setKey( acc );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
if ( job.error() ) {
std::cerr << "Deleting password failed: " << qPrintable(job.errorString()) << std::endl;
return 1;
}
std::cout << "Password deleted successfully" << std::endl;
} else {
return printUsage();
}
}