desktop/src/libsync/account.h

415 lines
13 KiB
C++

/*
* Copyright (C) by Daniel Molkentin <danimo@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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 SERVERCONNECTION_H
#define SERVERCONNECTION_H
#include <QByteArray>
#include <QUrl>
#include <QNetworkCookie>
#include <QNetworkRequest>
#include <QSslSocket>
#include <QSslCertificate>
#include <QSslConfiguration>
#include <QSslCipher>
#include <QSslError>
#include <QSharedPointer>
#ifndef TOKEN_AUTH_ONLY
#include <QPixmap>
#endif
#include "common/utility.h"
#include <memory>
#include "capabilities.h"
#include "clientsideencryption.h"
#include "syncfileitem.h"
class QSettings;
class QNetworkReply;
class QUrl;
class QNetworkAccessManager;
namespace QKeychain {
class Job;
class WritePasswordJob;
class ReadPasswordJob;
}
namespace OCC {
class AbstractCredentials;
class Account;
using AccountPtr = QSharedPointer<Account>;
class AccessManager;
class SimpleNetworkJob;
class PushNotifications;
class UserStatusConnector;
class SyncJournalDb;
/**
* @brief Reimplement this to handle SSL errors from libsync
* @ingroup libsync
*/
class AbstractSslErrorHandler
{
public:
virtual ~AbstractSslErrorHandler() = default;
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
*
* The Account has a name and url. It also has information about credentials,
* SSL errors and certificates.
*/
class OWNCLOUDSYNC_EXPORT Account : public QObject
{
Q_OBJECT
Q_PROPERTY(QString id MEMBER _id)
Q_PROPERTY(QString davUser MEMBER _davUser)
Q_PROPERTY(QString displayName MEMBER _displayName)
Q_PROPERTY(QUrl url MEMBER _url)
public:
static AccountPtr create();
~Account() override;
AccountPtr sharedFromThis();
AccountPtr sharedFromThis() const;
/**
* The user that can be used in dav url.
*
* This can very well be different frome the login user that's
* stored in credentials()->user().
*/
QString davUser() const;
void setDavUser(const QString &newDavUser);
QString davDisplayName() const;
void setDavDisplayName(const QString &newDisplayName);
#ifndef TOKEN_AUTH_ONLY
QImage avatar() const;
void setAvatar(const QImage &img);
#endif
/// The name of the account as shown in the toolbar
QString displayName() const;
QColor accentColor() const;
QColor headerColor() const;
QColor headerTextColor() const;
/// The internal id of the account.
QString id() const;
/** Server url of the account */
void setUrl(const QUrl &url);
QUrl url() const { return _url; }
/// Adjusts _userVisibleUrl once the host to use is discovered.
void setUserVisibleHost(const QString &host);
/**
* @brief The possibly themed dav path for the account. It has
* a trailing slash.
* @returns the (themeable) dav path for the account.
*/
QString davPath() const;
/** Returns webdav entry URL, based on url() */
QUrl davUrl() const;
/** Returns the legacy permalink url for a file.
*
* This uses the old way of manually building the url. New code should
* use the "privatelink" property accessible via PROPFIND.
*/
QUrl deprecatedPrivateLinkUrl(const QByteArray &numericFileId) const;
/** Holds the accounts credentials */
AbstractCredentials *credentials() const;
void setCredentials(AbstractCredentials *cred);
/** Create a network request on the account's QNAM.
*
* Network requests in AbstractNetworkJobs are created through
* this function. Other places should prefer to use jobs or
* sendRequest().
*/
QNetworkReply *sendRawRequest(const QByteArray &verb,
const QUrl &url,
QNetworkRequest req = QNetworkRequest(),
QIODevice *data = nullptr);
QNetworkReply *sendRawRequest(const QByteArray &verb,
const QUrl &url, QNetworkRequest req, const QByteArray &data);
QNetworkReply *sendRawRequest(const QByteArray &verb,
const QUrl &url, QNetworkRequest req, QHttpMultiPart *data);
/** Create and start network job for a simple one-off request.
*
* More complicated requests typically create their own job types.
*/
SimpleNetworkJob *sendRequest(const QByteArray &verb,
const QUrl &url,
QNetworkRequest req = QNetworkRequest(),
QIODevice *data = nullptr);
/** The ssl configuration during the first connection */
QSslConfiguration getOrCreateSslConfig();
QSslConfiguration sslConfiguration() const { return _sslConfiguration; }
void setSslConfiguration(const QSslConfiguration &config);
// Because of bugs in Qt, we use this to store info needed for the SSL Button
QSslCipher _sessionCipher;
QByteArray _sessionTicket;
QList<QSslCertificate> _peerCertificateChain;
/** The certificates of the account */
QList<QSslCertificate> approvedCerts() const { return _approvedCerts; }
void setApprovedCerts(const QList<QSslCertificate> certs);
void addApprovedCerts(const QList<QSslCertificate> certs);
// Usually when a user explicitly rejects a certificate we don't
// ask again. After this call, a dialog will again be shown when
// the next unknown certificate is encountered.
void resetRejectedCertificates();
// pluggable handler
void setSslErrorHandler(AbstractSslErrorHandler *handler);
// To be called by credentials only, for storing username and the like
QVariant credentialSetting(const QString &key) const;
void setCredentialSetting(const QString &key, const QVariant &value);
/** Assign a client certificate */
void setCertificate(const QByteArray certficate = QByteArray(), const QString privateKey = QString());
/** Access the server capabilities */
const Capabilities &capabilities() const;
void setCapabilities(const QVariantMap &caps);
/** Access the server version
*
* For servers >= 10.0.0, this can be the empty string until capabilities
* have been received.
*/
QString serverVersion() const;
/** Server version for easy comparison.
*
* Example: serverVersionInt() >= makeServerVersion(11, 2, 3)
*
* Will be 0 if the version is not available yet.
*/
int serverVersionInt() const;
static constexpr int makeServerVersion(const int majorVersion, const int minorVersion, const int patchVersion) {
return (majorVersion << 16) + (minorVersion << 8) + patchVersion;
};
void setServerVersion(const QString &version);
/** Whether the server is too old.
*
* Not supporting server versions is a gradual process. There's a hard
* compatibility limit (see ConnectionValidator) that forbids connecting
* to extremely old servers. And there's a weak "untested, not
* recommended, potentially dangerous" limit, that users might want
* to go beyond.
*
* This function returns true if the server is beyond the weak limit.
*/
bool serverVersionUnsupported() const;
bool isUsernamePrefillSupported() const;
bool isChecksumRecalculateRequestSupported() const;
int checksumRecalculateServerVersionMinSupportedMajor() const;
/** True when the server connection is using HTTP2 */
bool isHttp2Supported() { return _http2Supported; }
void setHttp2Supported(bool value) { _http2Supported = value; }
void clearCookieJar();
void lendCookieJarTo(QNetworkAccessManager *guest);
QString cookieJarPath();
void resetNetworkAccessManager();
QNetworkAccessManager *networkAccessManager();
QSharedPointer<QNetworkAccessManager> sharedNetworkAccessManager();
/// Called by network jobs on credential errors, emits invalidCredentials()
void handleInvalidCredentials();
ClientSideEncryption* e2e();
/// Used in RemoteWipe
void retrieveAppPassword();
void writeAppPasswordOnce(QString appPassword);
void deleteAppPassword();
void deleteAppToken();
/// Direct Editing
// Check for the directEditing capability
void fetchDirectEditors(const QUrl &directEditingURL, const QString &directEditingETag);
void setupUserStatusConnector();
void trySetupPushNotifications();
PushNotifications *pushNotifications() const;
void setPushNotificationsReconnectInterval(int interval);
std::shared_ptr<UserStatusConnector> userStatusConnector() const;
void setLockFileState(const QString &serverRelativePath,
SyncJournalDb * const journal,
const SyncFileItem::LockStatus lockStatus);
SyncFileItem::LockStatus fileLockStatus(SyncJournalDb * const journal,
const QString &folderRelativePath) const;
bool fileCanBeUnlocked(SyncJournalDb * const journal, const QString &folderRelativePath) const;
public slots:
/// Used when forgetting credentials
void clearQNAMCache();
void slotHandleSslErrors(QNetworkReply *, QList<QSslError>);
signals:
/// Emitted whenever there's network activity
void propagatorNetworkActivity();
/// Triggered by handleInvalidCredentials()
void invalidCredentials();
void credentialsFetched(AbstractCredentials *credentials);
void credentialsAsked(AbstractCredentials *credentials);
/// Forwards from QNetworkAccessManager::proxyAuthenticationRequired().
void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *);
// e.g. when the approved SSL certificates changed
void wantsAccountSaved(Account *acc);
void serverVersionChanged(Account *account, const QString &newVersion, const QString &oldVersion);
void accountChangedAvatar();
void accountChangedDisplayName();
/// Used in RemoteWipe
void appPasswordRetrieved(QString);
void pushNotificationsReady(Account *account);
void pushNotificationsDisabled(Account *account);
void userStatusChanged();
void serverUserStatusChanged();
void capabilitiesChanged();
void lockFileSuccess();
void lockFileError(const QString&);
protected Q_SLOTS:
void slotCredentialsFetched();
void slotCredentialsAsked();
void slotDirectEditingRecieved(const QJsonDocument &json);
private:
Account(QObject *parent = nullptr);
void setSharedThis(AccountPtr sharedThis);
static QString davPathBase();
QWeakPointer<Account> _sharedThis;
QString _id;
QString _davUser;
QString _displayName;
QTimer _pushNotificationsReconnectTimer;
#ifndef TOKEN_AUTH_ONLY
QImage _avatarImg;
#endif
QMap<QString, QVariant> _settingsMap;
QUrl _url;
/** If url to use for any user-visible urls.
*
* If the server configures overwritehost this can be different from
* the connection url in _url. We retrieve the visible host through
* the ocs/v1.php/config endpoint in ConnectionValidator.
*/
QUrl _userVisibleUrl;
QList<QSslCertificate> _approvedCerts;
QSslConfiguration _sslConfiguration;
Capabilities _capabilities;
QString _serverVersion;
QScopedPointer<AbstractSslErrorHandler> _sslErrorHandler;
QSharedPointer<QNetworkAccessManager> _am;
QScopedPointer<AbstractCredentials> _credentials;
bool _http2Supported = false;
/// Certificates that were explicitly rejected by the user
QList<QSslCertificate> _rejectedCertificates;
static QString _configFileName;
ClientSideEncryption _e2e;
/// Used in RemoteWipe
bool _wroteAppPassword = false;
friend class AccountManager;
// Direct Editing
QString _lastDirectEditingETag;
PushNotifications *_pushNotifications = nullptr;
std::shared_ptr<UserStatusConnector> _userStatusConnector;
/* IMPORTANT - remove later - FIXME MS@2019-12-07 -->
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY!
*
* Disabled as long as selecting another cert is not supported by the UI.
*
* Being able to specify a new certificate is important anyway: expiry etc.
*
* We introduce this dirty hack here, to allow deleting them upon Remote Wipe.
*/
public:
void setRemoteWipeRequested_HACK() { _isRemoteWipeRequested_HACK = true; }
bool isRemoteWipeRequested_HACK() { return _isRemoteWipeRequested_HACK; }
private:
bool _isRemoteWipeRequested_HACK = false;
// <-- FIXME MS@2019-12-07
};
}
Q_DECLARE_METATYPE(OCC::AccountPtr)
Q_DECLARE_METATYPE(OCC::Account *)
#endif //SERVERCONNECTION_H