[Pkg-owncloud-commits] [owncloud-client] 352/498: System proxy: Ask for credentials if needed.

Sandro Knauß hefee-guest at moszumanska.debian.org
Tue Aug 11 14:49:05 UTC 2015


This is an automated email from the git hooks/post-receive script.

hefee-guest pushed a commit to branch master
in repository owncloud-client.

commit 2124098f8419eb1d38711f8330deb98addd7af5b
Author: Christian Kamm <kamm at incasoftware.de>
Date:   Thu Jul 16 14:21:51 2015 +0200

    System proxy: Ask for credentials if needed.
    
    The proxyAuthenticationRequired() signal now goes to the
    ProxyAuthHandler class. That class will try to read the proxy settings
    from the keychain or ask the user about them.
    
    We won't ask the user for credentials for explicitly configured proxies.
    It also does not change how the credentials for explicitly configured
    proxies are stored. (see #261)
---
 src/gui/CMakeLists.txt             |   3 +
 src/gui/accountmanager.cpp         |  13 +-
 src/gui/accountmanager.h           |   6 +
 src/gui/networksettings.cpp        |  10 +-
 src/gui/networksettings.ui         |   9 --
 src/gui/owncloudsetupwizard.cpp    |   4 +-
 src/gui/proxyauthdialog.cpp        |  52 ++++++++
 src/gui/proxyauthdialog.h          |  52 ++++++++
 src/gui/proxyauthdialog.ui         | 115 ++++++++++++++++++
 src/gui/proxyauthhandler.cpp       | 236 +++++++++++++++++++++++++++++++++++++
 src/gui/proxyauthhandler.h         | 105 +++++++++++++++++
 src/libsync/abstractnetworkjob.cpp |   3 +-
 src/libsync/accessmanager.cpp      |  19 +--
 src/libsync/accessmanager.h        |   2 -
 src/libsync/account.cpp            |  10 +-
 src/libsync/account.h              |   6 +-
 16 files changed, 602 insertions(+), 43 deletions(-)

diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index f4c8561..a44a60a 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -26,6 +26,7 @@ set(client_UI
     sslerrordialog.ui
     owncloudsetuppage.ui
     addcertificatedialog.ui
+    proxyauthdialog.ui
     wizard/owncloudadvancedsetuppage.ui
     wizard/owncloudconnectionmethoddialog.ui
     wizard/owncloudhttpcredspage.ui
@@ -66,6 +67,8 @@ set(client_SRCS
     accountstate.cpp
     addcertificatedialog.cpp
     authenticationdialog.cpp
+    proxyauthhandler.cpp
+    proxyauthdialog.cpp
     creds/credentialsfactory.cpp
     creds/httpcredentialsgui.cpp
     creds/shibbolethcredentials.cpp
diff --git a/src/gui/accountmanager.cpp b/src/gui/accountmanager.cpp
index 71e65d3..6f7f8b4 100644
--- a/src/gui/accountmanager.cpp
+++ b/src/gui/accountmanager.cpp
@@ -13,6 +13,7 @@
 
 #include "accountmanager.h"
 #include "sslerrordialog.h"
+#include "proxyauthhandler.h"
 #include <theme.h>
 #include <creds/credentialsfactory.h>
 #include <creds/abstractcredentials.h>
@@ -168,7 +169,7 @@ void AccountManager::save(const AccountPtr& acc, QSettings& settings)
 
 AccountPtr AccountManager::load(QSettings& settings)
 {
-    auto acc = Account::create();
+    auto acc = createAccount();
 
     acc->setUrl(settings.value(QLatin1String(urlC)).toUrl());
 
@@ -186,7 +187,6 @@ AccountPtr AccountManager::load(QSettings& settings)
     // now the cert, it is in the general group
     settings.beginGroup(QLatin1String("General"));
     acc->setApprovedCerts(QSslCertificate::fromData(settings.value(caCertsKeyC).toByteArray()));
-    acc->setSslErrorHandler(new SslDialogErrorHandler);
     settings.endGroup();
 
     return acc;
@@ -219,6 +219,15 @@ void AccountManager::deleteAccount(AccountState* account)
     emit accountRemoved(account);
 }
 
+AccountPtr AccountManager::createAccount()
+{
+    AccountPtr acc = Account::create();
+    acc->setSslErrorHandler(new SslDialogErrorHandler);
+    connect(acc.data(), SIGNAL(proxyAuthenticationRequired(QNetworkProxy, QAuthenticator*)),
+            ProxyAuthHandler::instance(), SLOT(handleProxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+    return acc;
+}
+
 
 void AccountManager::shutdown()
 {
diff --git a/src/gui/accountmanager.h b/src/gui/accountmanager.h
index 9af0dbf..6644d84 100644
--- a/src/gui/accountmanager.h
+++ b/src/gui/accountmanager.h
@@ -64,6 +64,12 @@ public:
     void deleteAccount(AccountState *account);
 
 
+    /**
+     * Creates an account and sets up some basic handlers.
+     * Does *not* add the account to the account manager just yet.
+     */
+    static AccountPtr createAccount();
+
 private:
     void save(const AccountPtr& account, QSettings& settings);
     AccountPtr load(QSettings& settings);
diff --git a/src/gui/networksettings.cpp b/src/gui/networksettings.cpp
index 85dc1e0..ec67c7b 100644
--- a/src/gui/networksettings.cpp
+++ b/src/gui/networksettings.cpp
@@ -63,6 +63,7 @@ NetworkSettings::NetworkSettings(QWidget *parent) :
     connect(_ui->userLineEdit, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
     connect(_ui->passwordLineEdit, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
     connect(_ui->portSpinBox, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
+    connect(_ui->authRequiredcheckBox, SIGNAL(toggled(bool)), SLOT(saveProxySettings()));
 
     connect(_ui->uploadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
     connect(_ui->noUploadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
@@ -105,12 +106,9 @@ void NetworkSettings::loadProxySettings()
     if (port == 0)
         port = 8080;
     _ui->portSpinBox->setValue(port);
-    if (!cfgFile.proxyUser().isEmpty())
-    {
-        _ui->authRequiredcheckBox->setChecked(true);
-        _ui->userLineEdit->setText(cfgFile.proxyUser());
-        _ui->passwordLineEdit->setText(cfgFile.proxyPassword());
-    }
+    _ui->authRequiredcheckBox->setChecked(cfgFile.proxyNeedsAuth());
+    _ui->userLineEdit->setText(cfgFile.proxyUser());
+    _ui->passwordLineEdit->setText(cfgFile.proxyPassword());
 }
 
 void NetworkSettings::loadBWLimitSettings()
diff --git a/src/gui/networksettings.ui b/src/gui/networksettings.ui
index 56eb1ca..83c3854 100644
--- a/src/gui/networksettings.ui
+++ b/src/gui/networksettings.ui
@@ -103,9 +103,6 @@
            </item>
            <item>
             <widget class="QSpinBox" name="portSpinBox">
-             <property name="enabled">
-              <bool>false</bool>
-             </property>
              <property name="sizePolicy">
               <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
                <horstretch>0</horstretch>
@@ -155,9 +152,6 @@
             </property>
             <item>
              <widget class="QLineEdit" name="userLineEdit">
-              <property name="enabled">
-               <bool>false</bool>
-              </property>
               <property name="text">
                <string/>
               </property>
@@ -165,9 +159,6 @@
             </item>
             <item>
              <widget class="QLineEdit" name="passwordLineEdit">
-              <property name="enabled">
-               <bool>false</bool>
-              </property>
               <property name="text">
                <string/>
               </property>
diff --git a/src/gui/owncloudsetupwizard.cpp b/src/gui/owncloudsetupwizard.cpp
index a62dc83..b90cb79 100644
--- a/src/gui/owncloudsetupwizard.cpp
+++ b/src/gui/owncloudsetupwizard.cpp
@@ -78,10 +78,8 @@ void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *
 
 void OwncloudSetupWizard::startWizard()
 {
-    FolderMan *folderMan = FolderMan::instance();
-    AccountPtr account = Account::create();
+    AccountPtr account = AccountManager::createAccount();
     account->setCredentials(CredentialsFactory::create("dummy"));
-    account->setSslErrorHandler(new SslDialogErrorHandler);
     _ocWizard->setAccount(account);
     _ocWizard->setOCUrl(account->url().toString());
 
diff --git a/src/gui/proxyauthdialog.cpp b/src/gui/proxyauthdialog.cpp
new file mode 100644
index 0000000..e3dd922
--- /dev/null
+++ b/src/gui/proxyauthdialog.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 by Christian Kamm <kamm at incasoftware.de>
+ *
+ * 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 "proxyauthdialog.h"
+#include "ui_proxyauthdialog.h"
+
+namespace OCC {
+
+ProxyAuthDialog::ProxyAuthDialog(QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::ProxyAuthDialog)
+{
+    ui->setupUi(this);
+}
+
+ProxyAuthDialog::~ProxyAuthDialog()
+{
+    delete ui;
+}
+
+void ProxyAuthDialog::setProxyAddress(const QString &address)
+{
+    ui->proxyAddress->setText(address);
+}
+
+QString ProxyAuthDialog::username() const
+{
+    return ui->usernameEdit->text();
+}
+
+QString ProxyAuthDialog::password() const
+{
+    return ui->passwordEdit->text();
+}
+
+void ProxyAuthDialog::reset()
+{
+    ui->usernameEdit->clear();
+    ui->passwordEdit->clear();
+}
+
+} // namespace OCC
diff --git a/src/gui/proxyauthdialog.h b/src/gui/proxyauthdialog.h
new file mode 100644
index 0000000..0b24abb
--- /dev/null
+++ b/src/gui/proxyauthdialog.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 by Christian Kamm <kamm at incasoftware.de>
+ *
+ * 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 OCC_PROXYAUTHDIALOG_H
+#define OCC_PROXYAUTHDIALOG_H
+
+#include <QDialog>
+
+namespace OCC {
+
+namespace Ui {
+class ProxyAuthDialog;
+}
+
+/**
+ * @brief Ask for username and password for a given proxy.
+ *
+ * Used by ProxyAuthHandler.
+ */
+class ProxyAuthDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit ProxyAuthDialog(QWidget *parent = 0);
+    ~ProxyAuthDialog();
+
+    void setProxyAddress(const QString& address);
+
+    QString username() const;
+    QString password() const;
+
+    /// Resets the username and password.
+    void reset();
+
+private:
+    Ui::ProxyAuthDialog *ui;
+};
+
+
+} // namespace OCC
+#endif // OCC_PROXYAUTHDIALOG_H
diff --git a/src/gui/proxyauthdialog.ui b/src/gui/proxyauthdialog.ui
new file mode 100644
index 0000000..d71d392
--- /dev/null
+++ b/src/gui/proxyauthdialog.ui
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OCC::ProxyAuthDialog</class>
+ <widget class="QDialog" name="OCC::ProxyAuthDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>351</width>
+    <height>141</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Proxy authentication required</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Username:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QLineEdit" name="usernameEdit"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Proxy:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="0" colspan="2">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>The proxy server needs a username and password.</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Password:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QLineEdit" name="passwordEdit">
+     <property name="echoMode">
+      <enum>QLineEdit::Password</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QLabel" name="proxyAddress">
+     <property name="text">
+      <string>TextLabel</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>OCC::ProxyAuthDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>OCC::ProxyAuthDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/gui/proxyauthhandler.cpp b/src/gui/proxyauthhandler.cpp
new file mode 100644
index 0000000..30a82cc
--- /dev/null
+++ b/src/gui/proxyauthhandler.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2015 by Christian Kamm <kamm at incasoftware.de>
+ *
+ * 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 "proxyauthhandler.h"
+
+#include "proxyauthdialog.h"
+#include "theme.h"
+#include "configfile.h"
+
+#include <QApplication>
+
+#include <keychain.h>
+
+using namespace OCC;
+
+ProxyAuthHandler* ProxyAuthHandler::instance()
+{
+    static ProxyAuthHandler inst;
+    return &inst;
+}
+
+ProxyAuthHandler::ProxyAuthHandler()
+    : _blocked(false)
+    , _waitingForDialog(0)
+    , _waitingForKeychain(0)
+    , _keychainJobRunning(false)
+{
+    _dialog = new ProxyAuthDialog();
+
+    _configFile.reset(new ConfigFile);
+    _settings.reset(new QSettings(_configFile->configFile(), QSettings::IniFormat));
+    _settings->beginGroup(QLatin1String("Proxy"));
+    _settings->beginGroup(QLatin1String("Credentials"));
+}
+
+ProxyAuthHandler::~ProxyAuthHandler()
+{
+    delete _dialog;
+}
+
+void ProxyAuthHandler::handleProxyAuthenticationRequired(
+        const QNetworkProxy& proxy,
+        QAuthenticator* authenticator)
+{
+    if (!_dialog) {
+        return;
+    }
+
+    QString key = QString::fromLatin1("%1:%2").arg(
+            proxy.hostName(), QString::number(proxy.port()));
+
+    // If the proxy server has changed, forget what we know.
+    if (key != _proxy) {
+        _proxy = key;
+        _username.clear();
+        _password.clear();
+        _blocked = false;
+
+        // If the user explicitly configured the proxy in the
+        // network settings, don't ask about it.
+        if (_configFile->proxyType() == QNetworkProxy::HttpProxy
+                || _configFile->proxyType() == QNetworkProxy::Socks5Proxy) {
+            _blocked = true;
+        }
+    }
+
+    if (_blocked) {
+        return;
+    }
+
+    qDebug() << Q_FUNC_INFO << key << proxy.type() << authenticator->user();
+
+    // If we already had a username but auth still failed,
+    // invalidate the old credentials!
+    bool invalidated = false;
+    if (!authenticator->user().isEmpty() && !_waitingForDialog && !_waitingForKeychain) {
+        qDebug() << "invalidating old creds" << key;
+        _username.clear();
+        _password.clear();
+        invalidated = true;
+    }
+
+    if (_username.isEmpty() || _waitingForKeychain) {
+        if (invalidated || !getCredsFromKeychain()) {
+            if (getCredsFromDialog()) {
+                storeCredsInKeychain();
+            } else {
+                // dialog was cancelled, never ask for that proxy again
+                _blocked = true;
+                return;
+            }
+        }
+    }
+
+    qDebug() << "got creds for" << _proxy;
+    authenticator->setUser(_username);
+    authenticator->setPassword(_password);
+}
+
+void ProxyAuthHandler::slotKeychainJobDone()
+{
+    _keychainJobRunning = false;
+}
+
+bool ProxyAuthHandler::getCredsFromDialog()
+{
+    // Open the credentials dialog
+    if (!_waitingForDialog) {
+        _dialog->reset();
+        _dialog->setProxyAddress(_proxy);
+        _dialog->open();
+    }
+
+    // This function can be reentered while the dialog is open.
+    // If that's the case, continue processing the dialog until
+    // it's done.
+    ++_waitingForDialog;
+    while (_dialog && _dialog->isVisible()) {
+        QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers, 200);
+    }
+    --_waitingForDialog;
+
+    if (_dialog && _dialog->result() == QDialog::Accepted) {
+        qDebug() << "got creds for" << _proxy << "from dialog";
+        _username = _dialog->username();
+        _password = _dialog->password();
+        return true;
+    }
+    return false;
+}
+
+bool ProxyAuthHandler::getCredsFromKeychain()
+{
+    using namespace QKeychain;
+
+    if (_waitingForDialog) {
+        return false;
+    }
+
+    qDebug() << "trying to load" << _proxy;
+
+    if (!_waitingForKeychain) {
+        _username = _settings->value(keychainUsernameKey()).toString();
+        if (_username.isEmpty()) {
+            return false;
+        }
+
+        _readPasswordJob.reset(new ReadPasswordJob(Theme::instance()->appName()));
+        _readPasswordJob->setSettings(_settings.data());
+        _readPasswordJob->setInsecureFallback(false);
+        _readPasswordJob->setKey(keychainPasswordKey());
+        _readPasswordJob->setAutoDelete(false);
+        connect(_readPasswordJob.data(), SIGNAL(finished(QKeychain::Job*)),
+                SLOT(slotKeychainJobDone()));
+        _keychainJobRunning = true;
+        _readPasswordJob->start();
+    }
+
+    // While we wait for the password job to be done, this code may be reentered.
+    // This really needs the counter and the flag here, because otherwise we get
+    // bad behavior when we reenter this code after the flag has been switched
+    // but before the while loop has finished.
+    ++_waitingForKeychain;
+    _keychainJobRunning = true;
+    while (_keychainJobRunning) {
+        QApplication::processEvents(QEventLoop::AllEvents, 200);
+    }
+    --_waitingForKeychain;
+
+    if (_readPasswordJob->error() == NoError) {
+        qDebug() << "got creds for" << _proxy << "from keychain";
+        _password = _readPasswordJob->textData();
+        return true;
+    }
+
+    _username.clear();
+    if (_readPasswordJob->error() != EntryNotFound) {
+        qDebug() << "ReadPasswordJob failed with" << _readPasswordJob->errorString();
+    }
+    return false;
+}
+
+void ProxyAuthHandler::storeCredsInKeychain()
+{
+    using namespace QKeychain;
+
+    if (_waitingForKeychain) {
+        return;
+    }
+
+    qDebug() << "storing" << _proxy;
+
+    _settings->setValue(keychainUsernameKey(), _username);
+
+    WritePasswordJob* job = new WritePasswordJob(Theme::instance()->appName(), this);
+    job->setSettings(_settings.data());
+    job->setInsecureFallback(false);
+    job->setKey(keychainPasswordKey());
+    job->setTextData(_password);
+    job->setAutoDelete(false);
+    connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotKeychainJobDone()));
+    _keychainJobRunning = true;
+    job->start();
+
+    ++_waitingForKeychain;
+    _keychainJobRunning = true;
+    while (_keychainJobRunning) {
+        QApplication::processEvents(QEventLoop::AllEvents, 200);
+    }
+    --_waitingForKeychain;
+
+    job->deleteLater();
+    if (job->error() != NoError) {
+        qDebug() << "WritePasswordJob failed with" << job->errorString();
+    }
+}
+
+QString ProxyAuthHandler::keychainUsernameKey() const
+{
+    return QString::fromLatin1("%1/username").arg(_proxy);
+}
+
+QString ProxyAuthHandler::keychainPasswordKey() const
+{
+    return QString::fromLatin1("%1/password").arg(_proxy);
+}
diff --git a/src/gui/proxyauthhandler.h b/src/gui/proxyauthhandler.h
new file mode 100644
index 0000000..872ae09
--- /dev/null
+++ b/src/gui/proxyauthhandler.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 by Christian Kamm <kamm at incasoftware.de>
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "owncloudgui.h"
+#include <QObject>
+#include <QString>
+#include <QNetworkProxy>
+#include <QAuthenticator>
+#include <QPointer>
+#include <QScopedPointer>
+#include <QSettings>
+
+namespace QKeychain {
+class Job;
+class ReadPasswordJob;
+}
+
+namespace OCC {
+
+class ConfigFile;
+class ProxyAuthDialog;
+
+/**
+ * @brief Handle proxyAuthenticationRequired signals from our QNetworkAccessManagers.
+ *
+ * The main complication here is that the slot needs to return credential information
+ * synchronously - but running a dialog or getting password data from synchronous
+ * storage are asynchronous operations. This leads to reentrant calls that are
+ * fairly complicated to handle.
+ */
+class ProxyAuthHandler : public QObject
+{
+    Q_OBJECT
+
+public:
+    static ProxyAuthHandler* instance();
+
+    virtual ~ProxyAuthHandler();
+
+public slots:
+    /// Intended for QNetworkAccessManager::proxyAuthenticationRequired()
+    void handleProxyAuthenticationRequired(const QNetworkProxy& proxy,
+                                           QAuthenticator* authenticator);
+
+private slots:
+    void slotKeychainJobDone();
+
+private:
+    ProxyAuthHandler();
+
+    /// Runs the ProxyAuthDialog and returns true if new credentials were entered.
+    bool getCredsFromDialog();
+
+    /// Checks the keychain for credentials of the current proxy.
+    bool getCredsFromKeychain();
+
+    /// Stores the current credentials in the keychain.
+    void storeCredsInKeychain();
+
+    QString keychainUsernameKey() const;
+    QString keychainPasswordKey() const;
+
+    /// The hostname:port of the current proxy, used for detetcting switches
+    /// to a different proxy.
+    QString _proxy;
+
+    QString _username;
+    QString _password;
+
+    /// If the user cancels the credential dialog, blocked will be set to
+    /// true and we won't bother him again.
+    bool _blocked;
+
+    /// In several instances handleProxyAuthenticationRequired() can be called
+    /// while it is still running. These counters detect what we're currently
+    /// waiting for.
+    int _waitingForDialog;
+    int _waitingForKeychain;
+    bool _keychainJobRunning;
+
+    QPointer<ProxyAuthDialog> _dialog;
+
+    /// The QSettings instance to securely store username/password in the keychain.
+    QScopedPointer<QSettings> _settings;
+
+    /// Pointer to the most-recently-run ReadPasswordJob, needed due to reentrancy.
+    QScopedPointer<QKeychain::ReadPasswordJob> _readPasswordJob;
+
+    /// For checking the proxy config settings.
+    QScopedPointer<ConfigFile> _configFile;
+};
+
+} // namespace OCC
diff --git a/src/libsync/abstractnetworkjob.cpp b/src/libsync/abstractnetworkjob.cpp
index d085c6f..d4442aa 100644
--- a/src/libsync/abstractnetworkjob.cpp
+++ b/src/libsync/abstractnetworkjob.cpp
@@ -157,7 +157,8 @@ void AbstractNetworkJob::slotFinished()
     }
 
     if( _reply->error() != QNetworkReply::NoError ) {
-        qDebug() << Q_FUNC_INFO << _reply->error() << _reply->errorString();
+        qDebug() << Q_FUNC_INFO << _reply->error() << _reply->errorString()
+                 << _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
         if (_reply->error() == QNetworkReply::ProxyAuthenticationRequiredError) {
             qDebug() << Q_FUNC_INFO << _reply->rawHeader("Proxy-Authenticate");
         }
diff --git a/src/libsync/accessmanager.cpp b/src/libsync/accessmanager.cpp
index e380171..dcbb0ce 100644
--- a/src/libsync/accessmanager.cpp
+++ b/src/libsync/accessmanager.cpp
@@ -23,6 +23,10 @@
 #include "accessmanager.h"
 #include "utility.h"
 
+#include <QInputDialog>
+#include <QMutexLocker>
+#include <QApplication>
+
 namespace OCC
 {
 
@@ -36,9 +40,6 @@ AccessManager::AccessManager(QObject* parent)
     setProxy(proxy);
 #endif
     setCookieJar(new CookieJar);
-    connect(this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
-            this, SLOT(slotProxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
-
 }
 
 void AccessManager::setRawCookie(const QByteArray &rawCookie, const  QUrl &url)
@@ -72,16 +73,4 @@ QNetworkReply* AccessManager::createRequest(QNetworkAccessManager::Operation op,
     return QNetworkAccessManager::createRequest(op, newRequest, outgoingData);
 }
 
-void AccessManager::slotProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
-{
-    Q_UNUSED(authenticator);
-    qDebug() << Q_FUNC_INFO << proxy.type();
-    // We put in the password here and in ClientProxy in the proxy itself.
-    if (!proxy.user().isEmpty() || !proxy.password().isEmpty()) {
-        authenticator->setUser(proxy.user());
-        authenticator->setPassword(proxy.password());
-    }
-}
-
-
 } // namespace OCC
diff --git a/src/libsync/accessmanager.h b/src/libsync/accessmanager.h
index 675159e..00e0d22 100644
--- a/src/libsync/accessmanager.h
+++ b/src/libsync/accessmanager.h
@@ -38,8 +38,6 @@ public:
 
 protected:
     QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0) Q_DECL_OVERRIDE;
-protected slots:
-    void slotProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
 };
 
 } // namespace OCC
diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp
index 2b789e5..8840531 100644
--- a/src/libsync/account.cpp
+++ b/src/libsync/account.cpp
@@ -134,7 +134,9 @@ void Account::setCredentials(AbstractCredentials *cred)
         _am->setCookieJar(jar);
     }
     connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
-            SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
+            SLOT(slotHandleSslErrors(QNetworkReply*,QList<QSslError>)));
+    connect(_am, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+            SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
     connect(_credentials, SIGNAL(fetched()),
             SLOT(slotCredentialsFetched()));
 }
@@ -178,7 +180,9 @@ void Account::resetNetworkAccessManager()
     _am = _credentials->getQNAM();
     _am->setCookieJar(jar); // takes ownership of the old cookie jar
     connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
-            SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
+            SLOT(slotHandleSslErrors(QNetworkReply*,QList<QSslError>)));
+    connect(_am, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+            SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
 }
 
 QNetworkAccessManager *Account::networkAccessManager()
@@ -368,7 +372,7 @@ void Account::setCredentialSetting(const QString &key, const QVariant &value)
     }
 }
 
-void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> errors)
+void Account::slotHandleSslErrors(QNetworkReply *reply , QList<QSslError> errors)
 {
     NetworkJobTimeoutPauser pauser(reply);
     QString out;
diff --git a/src/libsync/account.h b/src/libsync/account.h
index e19795b..dc41aae 100644
--- a/src/libsync/account.h
+++ b/src/libsync/account.h
@@ -51,7 +51,6 @@ public:
     virtual bool handleErrors(QList<QSslError>, const QSslConfiguration &conf, QList<QSslCertificate>*, AccountPtr) = 0;
 };
 
-
 /**
  * @brief The Account class represents an account on an ownCloud Server
  * @ingroup libsync
@@ -157,8 +156,11 @@ signals:
     void invalidCredentials();
     void credentialsFetched(AbstractCredentials* credentials);
 
+    /// Forwards from QNetworkAccessManager::proxyAuthenticationRequired().
+    void proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*);
+
 protected Q_SLOTS:
-    void slotHandleErrors(QNetworkReply*,QList<QSslError>);
+    void slotHandleSslErrors(QNetworkReply*,QList<QSslError>);
     void slotCredentialsFetched();
 
 private:

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud-client.git



More information about the Pkg-owncloud-commits mailing list