Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrzesimir Nowak <krzesimir@endocode.com>2013-07-30 13:19:22 +0400
committerKrzesimir Nowak <krzesimir@endocode.com>2013-08-01 18:53:43 +0400
commit78b6f4df01bd7fb5620fe5af7d4619896c17249f (patch)
treee9212b75afb6f86e0914e27e66525d7d8bd2e48f /src/creds/http
parentb7e88aa2efbd004d58c98868abe64d7d6a8c6fa5 (diff)
Move the creds/ and wizard/ directories one level higher.
Diffstat (limited to 'src/creds/http')
-rw-r--r--src/creds/http/credentialstore.cpp338
-rw-r--r--src/creds/http/credentialstore.h132
-rw-r--r--src/creds/http/httpconfigfile.cpp81
-rw-r--r--src/creds/http/httpconfigfile.h40
4 files changed, 591 insertions, 0 deletions
diff --git a/src/creds/http/credentialstore.cpp b/src/creds/http/credentialstore.cpp
new file mode 100644
index 000000000..6f4533531
--- /dev/null
+++ b/src/creds/http/credentialstore.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <QtGui>
+#include <QInputDialog>
+
+#include "config.h"
+
+#include "creds/http/credentialstore.h"
+#include "creds/http/httpconfigfile.h"
+#include "mirall/theme.h"
+
+#ifdef WITH_QTKEYCHAIN
+#include <qtkeychain/keychain.h>
+using namespace QKeychain;
+#endif
+
+#define MAX_LOGIN_ATTEMPTS 3
+
+namespace Mirall {
+
+CredentialStore *CredentialStore::_instance=0;
+CredentialStore::CredState CredentialStore::_state = NotFetched;
+QString CredentialStore::_passwd = QString::null;
+QString CredentialStore::_user = QString::null;
+QString CredentialStore::_url = QString::null;
+QString CredentialStore::_errorMsg = QString::null;
+#ifdef WITH_QTKEYCHAIN
+CredentialStore::CredentialType CredentialStore::_type = KeyChain;
+#else
+CredentialStore::CredentialType CredentialStore::_type = Settings;
+#endif
+
+CredentialStore::CredentialStore(QObject *parent) :
+ QObject(parent)
+{
+}
+
+CredentialStore *CredentialStore::instance()
+{
+ if( !CredentialStore::_instance ) CredentialStore::_instance = new CredentialStore;
+ return CredentialStore::_instance;
+}
+
+QString CredentialStore::password() const
+{
+ return _passwd;
+}
+QString CredentialStore::user() const
+{
+ return _user;
+}
+
+CredentialStore::CredState CredentialStore::state()
+{
+ return _state;
+}
+
+void CredentialStore::fetchCredentials()
+{
+ HttpConfigFile cfgFile;
+
+ bool ok = false;
+ QString pwd;
+ _user = cfgFile.user();
+ _url = cfgFile.ownCloudUrl();
+
+ QString key = keyChainKey(_url);
+
+ if( key.isNull() ) {
+ qDebug() << "Can not fetch credentials, url is zero!";
+ _state = Error;
+ emit( fetchCredentialsFinished(false) );
+ return;
+ }
+
+ switch( _type ) {
+ case CredentialStore::Settings: {
+ /* Read from config file. */
+ _state = Fetching;
+ cfgFile.fixupOldPassword();
+ if( cfgFile.passwordExists() ) {
+ pwd = cfgFile.password();
+ ok = true;
+ } else {
+ ok = false;
+ _state = EntryNotFound;
+ }
+ break;
+ }
+ case CredentialStore::KeyChain: {
+ // If the credentials are here already, return.
+ if( _state == Ok || _state == AsyncWriting ) {
+ emit(fetchCredentialsFinished(true));
+ return;
+ }
+ // otherwise fetch asynchronious.
+#ifdef WITH_QTKEYCHAIN
+ _state = AsyncFetching;
+ if( !_user.isEmpty() ) {
+ ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
+ job->setKey( key );
+
+ connect( job, SIGNAL(finished(QKeychain::Job*)), this,
+ SLOT(slotKeyChainReadFinished(QKeychain::Job*)));
+ job->start();
+ }
+#else
+ qDebug() << "QtKeyChain: Not yet implemented!";
+ _state = Error;
+#endif
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ if( _state == Fetching ) { // ...but not AsyncFetching
+ if( ok ) {
+ _passwd = pwd;
+ _state = Ok;
+ }
+ if( !ok && _state == Fetching ) {
+ _state = Error;
+ }
+
+ emit( fetchCredentialsFinished(ok) );
+ } else {
+ // in case of AsyncFetching nothing happens here. The finished-Slot
+ // will emit the finish signal.
+ }
+}
+
+void CredentialStore::reset()
+{
+ _state = NotFetched;
+ _user = QString::null;
+ _passwd = QString::null;
+}
+
+QString CredentialStore::keyChainKey( const QString& url ) const
+{
+ QString u(url);
+ if( u.isEmpty() ) {
+ qDebug() << "Empty url in keyChain, error!";
+ return QString::null;
+ }
+ if( _user.isEmpty() ) {
+ qDebug() << "Error: User is emty!";
+ return QString::null;
+ }
+
+ if( !u.endsWith(QChar('/')) ) {
+ u.append(QChar('/'));
+ }
+
+ QString key = _user+QLatin1Char(':')+u;
+ return key;
+}
+
+void CredentialStore::slotKeyChainReadFinished(QKeychain::Job* job)
+{
+#ifdef WITH_QTKEYCHAIN
+ ReadPasswordJob *pwdJob = static_cast<ReadPasswordJob*>(job);
+ if( pwdJob ) {
+ switch( pwdJob->error() ) {
+ case QKeychain::NoError:
+ _passwd = pwdJob->textData();
+#ifdef Q_OS_LINUX
+ // Currently there is a bug in the keychain on linux that if no
+ // entry is there, an empty password comes back, but no error.
+ if( _passwd.isEmpty() ) {
+ _state = EntryNotFound;
+ _errorMsg = tr("No password entry found in keychain. Please reconfigure.");
+ } else
+#endif
+ _state = Ok;
+ break;
+ case QKeychain::EntryNotFound:
+ _state = EntryNotFound;
+ break;
+ case QKeychain::CouldNotDeleteEntry:
+ _state = Error;
+ break;
+ case QKeychain::AccessDenied:
+ _state = AccessDenied;
+ break;
+ case QKeychain::NoBackendAvailable:
+ _state = NoKeychainBackend;
+ break;
+ case QKeychain::NotImplemented:
+ _state = NoKeychainBackend;
+ break;
+ case QKeychain::OtherError:
+ default:
+ _state = Error;
+
+ }
+ /* In case there is no backend, tranparentely switch to Settings file. */
+ if( _state == NoKeychainBackend ) {
+ qDebug() << "No Storage Backend, falling back to Settings mode.";
+ _type = CredentialStore::Settings;
+ fetchCredentials(_user);
+ return;
+ }
+
+ if( _state == EntryNotFound ) {
+ // try to migrate.
+ }
+
+ if( _state != Ok ) {
+ qDebug() << "Error with keychain: " << pwdJob->errorString();
+ if(_errorMsg.isEmpty()) _errorMsg = pwdJob->errorString();
+ } else {
+ _errorMsg = QString::null;
+ }
+ } else {
+ _state = Error;
+ qDebug() << "Error: KeyChain Read Password Job failed!";
+ }
+ emit(fetchCredentialsFinished(_state == Ok));
+#else
+ (void) job;
+#endif
+}
+
+QString CredentialStore::errorMessage()
+{
+ return _errorMsg;
+}
+
+void CredentialStore::setCredentials( const QString& url, const QString& user,
+ const QString& pwd )
+{
+ _passwd = pwd;
+ _user = user;
+#ifdef WITH_QTKEYCHAIN
+ _type = KeyChain;
+#else
+ _type = Settings;
+#endif
+ _url = url;
+ _state = Ok;
+}
+
+void CredentialStore::saveCredentials( )
+{
+ HttpConfigFile cfgFile;
+ QString key = keyChainKey(_url);
+ if( key.isNull() ) {
+ qDebug() << "Error: Can not save credentials, URL is zero!";
+ return;
+ }
+#ifdef WITH_QTKEYCHAIN
+#endif
+
+ cfgFile.setUser(_user);
+ switch( _type ) {
+ case CredentialStore::KeyChain: {
+#ifdef WITH_QTKEYCHAIN
+ WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
+ // Set password in KeyChain
+ job->setKey( key );
+ job->setTextData(_passwd);
+
+ connect( job, SIGNAL(finished(QKeychain::Job*)), this,
+ SLOT(slotKeyChainWriteFinished(QKeychain::Job*)));
+ _state = AsyncWriting;
+ job->start();
+#endif
+ }
+ break;
+ case CredentialStore::Settings:
+ cfgFile.setPassword( _passwd );
+ reset();
+ break;
+ default:
+ // unsupported.
+ break;
+ }
+}
+
+void CredentialStore::slotKeyChainWriteFinished( QKeychain::Job *job )
+{
+#ifdef WITH_QTKEYCHAIN
+ WritePasswordJob *pwdJob = static_cast<WritePasswordJob*>(job);
+ if( pwdJob ) {
+ QKeychain::Error err = pwdJob->error();
+
+ if( err != QKeychain::NoError ) {
+ qDebug() << "Error with keychain: " << pwdJob->errorString();
+ if( err == NoBackendAvailable || err == NotImplemented ||
+ pwdJob->errorString().contains(QLatin1String("Could not open wallet"))) {
+ _state = NoKeychainBackend;
+ _type = Settings;
+ saveCredentials();
+ } else {
+ _state = Error;
+ }
+ } else {
+ qDebug() << "Successfully stored password for user " << _user;
+ // Try to remove password formerly stored in the config file.
+ HttpConfigFile cfgFile;
+ cfgFile.removePassword();
+ _state = NotFetched;
+ }
+ } else {
+ qDebug() << "Error: KeyChain Write Password Job failed!";
+ _state = Error;
+ }
+#else
+ (void) job;
+#endif
+}
+
+// Called if a user chooses to not store the password locally.
+void CredentialStore::deleteKeyChainCredential( const QString& key )
+{
+#ifdef WITH_QTKEYCHAIN
+ // Start the remove job, do not care so much about the result.
+ DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
+ job->setKey( key );
+ job->start();
+#endif
+}
+
+}
diff --git a/src/creds/http/credentialstore.h b/src/creds/http/credentialstore.h
new file mode 100644
index 000000000..2732e603c
--- /dev/null
+++ b/src/creds/http/credentialstore.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CREDENTIALSTORE_H
+#define CREDENTIALSTORE_H
+
+#include <QObject>
+#include <QInputDialog>
+
+namespace QKeychain {
+ class Job;
+}
+
+namespace Mirall {
+
+/*
+ * This object holds the credential information of the ownCloud connection. It
+ * is implemented as a singleton.
+ * At startup of the client, at first the fetchCredentials() method must be called
+ * which tries to get credentials from one of the supported backends. To determine
+ * which backend should be used, MirallConfigFile::credentialType() is called as
+ * the backend is configured in the config file.
+ *
+ * The fetchCredentials() call changes the internal state of the credential store
+ * to one of
+ * Ok: There are credentials. Note that it's unknown if they are correct!!
+ * Fetching: The fetching is not yet finished.
+ * EntryNotFound: No password entry found in the storage.
+ * Error: A general error happened.
+ * After fetching has finished, signal fetchCredentialsFinished(bool) is emitted.
+ * The result can be retrieved with state() and password() and user() methods.
+ */
+
+class CredentialStore : public QObject
+{
+ Q_OBJECT
+public:
+ enum CredState { NotFetched = 0,
+ Ok,
+ Fetching,
+ AsyncFetching,
+ EntryNotFound,
+ AccessDenied,
+ NoKeychainBackend,
+ Error,
+ AsyncWriting };
+
+ enum CredentialType {
+ Settings = 0,
+ KeyChain
+ };
+
+ QString password( ) const;
+ QString user( ) const;
+
+ /**
+ * @brief state
+ * @return the state of the Credentialstore.
+ */
+ CredState state();
+
+ /**
+ * @brief fetchCredentials - start to retrieve user credentials.
+ *
+ * This method must be called first to retrieve the credentials.
+ * At the end, this method emits the fetchKeyChainFinished() signal.
+ */
+ void fetchCredentials();
+
+ /**
+ * @brief instance - singleton pointer.
+ * @return the singleton pointer to access the object.
+ */
+ static CredentialStore *instance();
+
+ /**
+ * @brief setCredentials - sets the user credentials.
+ *
+ * This function is called from the setup wizard to set the credentials
+ * int this store. Note that it does not store the password.
+ * The function also sets the state to ok.
+ * @param url - the connection url
+ * @param user - the user name
+ */
+ void setCredentials( const QString& url, const QString& user, const QString& pwd);
+
+ void saveCredentials( );
+
+ QString errorMessage();
+
+ void reset();
+signals:
+ /**
+ * @brief fetchCredentialsFinished
+ *
+ * emitted as soon as the fetching of the credentials has finished.
+ * If the parameter is true, there is a password and user. This does
+ * however, not say if the credentials are valid log in data.
+ * If false, the user pressed cancel.
+ */
+ void fetchCredentialsFinished(bool);
+
+protected slots:
+ void slotKeyChainReadFinished( QKeychain::Job* );
+ void slotKeyChainWriteFinished( QKeychain::Job* );
+
+private:
+ explicit CredentialStore(QObject *parent = 0);
+ void deleteKeyChainCredential( const QString& );
+ QString keyChainKey( const QString& ) const;
+
+ static CredentialStore *_instance;
+ static CredState _state;
+ static QString _passwd;
+ static QString _user;
+ static QString _url;
+ static QString _errorMsg;
+ static CredentialType _type;
+};
+}
+
+#endif // CREDENTIALSTORE_H
diff --git a/src/creds/http/httpconfigfile.cpp b/src/creds/http/httpconfigfile.cpp
new file mode 100644
index 000000000..366b6c1ee
--- /dev/null
+++ b/src/creds/http/httpconfigfile.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "creds/http/httpconfigfile.h"
+
+namespace Mirall
+{
+
+namespace
+{
+
+const char userC[] = "user";
+const char passwdC[] = "passwd";
+const char oldPasswdC[] = "password";
+
+} // ns
+
+QString HttpConfigFile::user() const
+{
+ return retrieveData(QString(), QLatin1String(userC)).toString();
+}
+
+void HttpConfigFile::setUser(const QString& user)
+{
+ storeData(QString(), QLatin1String(userC), QVariant(user));
+}
+
+QString HttpConfigFile::password() const
+{
+ const QVariant passwd(retrieveData(QString(), QLatin1String(passwdC)));
+
+ if (passwd.isValid()) {
+ return QString::fromUtf8(QByteArray::fromBase64(passwd.toByteArray()));
+ }
+
+ return QString();
+}
+
+void HttpConfigFile::setPassword(const QString& password)
+{
+ QByteArray pwdba = password.toUtf8();
+ storeData( QString(), QLatin1String(passwdC), QVariant(pwdba.toBase64()) );
+ removeOldPassword();
+}
+
+bool HttpConfigFile::passwordExists() const
+{
+ dataExists(QString(), QLatin1String(passwdC));
+}
+
+void HttpConfigFile::removePassword()
+{
+ removeOldPassword();
+ removeData(QString(), QLatin1String(passwdC));
+}
+
+void HttpConfigFile::fixupOldPassword()
+{
+ const QString old(QString::fromLatin1(oldPasswdC));
+
+ if (dataExists(QString(), old)) {
+ setPassword(retrieveData(QString(), old).toString());
+ }
+};
+
+void HttpConfigFile::removeOldPassword()
+{
+ removeData(QString(), QLatin1String(oldPasswdC));
+}
+
+} // ns Mirall
diff --git a/src/creds/http/httpconfigfile.h b/src/creds/http/httpconfigfile.h
new file mode 100644
index 000000000..2e690054f
--- /dev/null
+++ b/src/creds/http/httpconfigfile.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef MIRALL_CREDS_HTTP_CONFIG_FILE_H
+#define MIRALL_CREDS_HTTP_CONFIG_FILE_H
+
+#include "mirall/mirallconfigfile.h"
+
+namespace Mirall
+{
+
+class HttpConfigFile : public MirallConfigFile
+{
+public:
+ QString user() const;
+ void setUser(const QString& user);
+
+ QString password() const;
+ void setPassword(const QString& password);
+ bool passwordExists() const;
+ void removePassword();
+ void fixupOldPassword();
+
+private:
+ void removeOldPassword();
+};
+
+} // ns Mirall
+
+#endif