/* * Copyright (C) by Daniel Molkentin * * 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 #include #include #include #include #include #include #include #include #include #ifndef TOKEN_AUTH_ONLY #include #endif #include "capabilities.h" #include "clientsideencryption.h" #include "clientstatusreporting.h" #include "common/utility.h" #include "syncfileitem.h" #include 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; 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, const QSslConfiguration &conf, QList *, 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(QString prettyName READ prettyName NOTIFY prettyNameChanged) Q_PROPERTY(QUrl url MEMBER _url) Q_PROPERTY(bool e2eEncryptionKeysGenerationAllowed MEMBER _e2eEncryptionKeysGenerationAllowed) Q_PROPERTY(bool askUserForMnemonic READ askUserForMnemonic WRITE setAskUserForMnemonic NOTIFY askUserForMnemonicChanged) public: static AccountPtr create(); ~Account() override; AccountPtr sharedFromThis(); [[nodiscard]] AccountPtr sharedFromThis() const; /** * The user that can be used in dav url. * * This can very well be different from the login user that's * stored in credentials()->user(). */ [[nodiscard]] QString davUser() const; void setDavUser(const QString &newDavUser); [[nodiscard]] QString davDisplayName() const; void setDavDisplayName(const QString &newDisplayName); #ifndef TOKEN_AUTH_ONLY [[nodiscard]] QImage avatar() const; void setAvatar(const QImage &img); #endif /// The name of the account as shown in the toolbar [[nodiscard]] QString displayName() const; /// User id in a form 'user@example.de, optionally port is added (if it is not 80 or 443) [[nodiscard]] QString userIdAtHostWithPort() const; /// The name of the account that is displayed as nicely as possible, /// e.g. the actual name of the user (John Doe). If this cannot be /// provided, defaults to davUser (e.g. johndoe) [[nodiscard]] QString prettyName() const; [[nodiscard]] QColor accentColor() const; [[nodiscard]] QColor headerColor() const; [[nodiscard]] QColor headerTextColor() const; /// The internal id of the account. [[nodiscard]] QString id() const; /** Server url of the account */ void setUrl(const QUrl &url); [[nodiscard]] 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. */ [[nodiscard]] QString davPath() const; /** * @brief The possibly themed dav path root for the account. It has * no trailing slash. * @returns the (themeable) dav path for the account. */ [[nodiscard]] QString davPathRoot() const; /** Returns webdav entry URL, based on url() */ [[nodiscard]] 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. */ [[nodiscard]] QUrl deprecatedPrivateLinkUrl(const QByteArray &numericFileId) const; /** Holds the accounts credentials */ [[nodiscard]] 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(); [[nodiscard]] 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 _peerCertificateChain; /** The certificates of the account */ [[nodiscard]] QList approvedCerts() const { return _approvedCerts; } void setApprovedCerts(const QList certs); void addApprovedCerts(const QList 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 [[nodiscard]] QVariant credentialSetting(const QString &key) const; void setCredentialSetting(const QString &key, const QVariant &value); /** Assign a client certificate */ void setCertificate(const QByteArray certificate = QByteArray(), const QString privateKey = QString()); /** Access the server capabilities */ [[nodiscard]] 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. */ [[nodiscard]] QString serverVersion() const; // check if the checksum validation of E2EE metadata is allowed to be skipped via config file, this will only work before client 3.9.0 [[nodiscard]] bool shouldSkipE2eeMetadataChecksumValidation() const; void resetShouldSkipE2eeMetadataChecksumValidation(); /** Server version for easy comparison. * * Example: serverVersionInt() >= makeServerVersion(11, 2, 3) * * Will be 0 if the version is not available yet. */ [[nodiscard]] int serverVersionInt() const; [[nodiscard]] bool serverHasMountRootProperty() 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. */ [[nodiscard]] bool serverVersionUnsupported() const; [[nodiscard]] bool secureFileDropSupported() const; [[nodiscard]] bool isUsernamePrefillSupported() const; [[nodiscard]] bool isChecksumRecalculateRequestSupported() const; [[nodiscard]] 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 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(); [[nodiscard]] PushNotifications *pushNotifications() const; void setPushNotificationsReconnectInterval(int interval); void trySetupClientStatusReporting(); void reportClientStatus(const ClientStatusReportingStatus status) const; [[nodiscard]] std::shared_ptr userStatusConnector() const; void setLockFileState(const QString &serverRelativePath, const QString &remoteSyncPathWithTrailingSlash, const QString &localSyncPath, SyncJournalDb * const journal, const SyncFileItem::LockStatus lockStatus, const SyncFileItem::LockOwnerType lockOwnerType); SyncFileItem::LockStatus fileLockStatus(SyncJournalDb * const journal, const QString &folderRelativePath) const; bool fileCanBeUnlocked(SyncJournalDb * const journal, const QString &folderRelativePath) const; void setTrustCertificates(bool trustCertificates); [[nodiscard]] bool trustCertificates() const; void setE2eEncryptionKeysGenerationAllowed(bool allowed); [[nodiscard]] bool e2eEncryptionKeysGenerationAllowed() const; [[nodiscard]] bool askUserForMnemonic() const; public slots: /// Used when forgetting credentials void clearQNAMCache(); void slotHandleSslErrors(QNetworkReply *, QList); void setAskUserForMnemonic(const bool ask); signals: /// Emitted whenever there's network activity void propagatorNetworkActivity(); /// Triggered by handleInvalidCredentials() void invalidCredentials(); void credentialsFetched(OCC::AbstractCredentials *credentials); void credentialsAsked(OCC::AbstractCredentials *credentials); /// Forwards from QNetworkAccessManager::proxyAuthenticationRequired(). void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *); // e.g. when the approved SSL certificates changed void wantsAccountSaved(OCC::Account *acc); void serverVersionChanged(OCC::Account *account, const QString &newVersion, const QString &oldVersion); void accountChangedAvatar(); void accountChangedDisplayName(); void prettyNameChanged(); void askUserForMnemonicChanged(); /// Used in RemoteWipe void appPasswordRetrieved(QString); void pushNotificationsReady(OCC::Account *account); void pushNotificationsDisabled(OCC::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 slots: void removeLockStatusChangeInprogress(const QString &serverRelativePath, const SyncFileItem::LockStatus lockStatus); private: Account(QObject *parent = nullptr); void setSharedThis(AccountPtr sharedThis); void updateServerColors(); [[nodiscard]] static QString davPathBase(); [[nodiscard]] QColor serverColor() const; bool _trustCertificates = false; bool _e2eEncryptionKeysGenerationAllowed = false; bool _e2eAskUserForMnemonic = false; QWeakPointer _sharedThis; QString _id; QString _davUser; QString _displayName; QTimer _pushNotificationsReconnectTimer; #ifndef TOKEN_AUTH_ONLY QImage _avatarImg; #endif QMap _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 _approvedCerts; QSslConfiguration _sslConfiguration; Capabilities _capabilities; QString _serverVersion; QColor _serverColor; QColor _serverTextColor = QColorConstants::White; bool _skipE2eeMetadataChecksumValidation = false; QScopedPointer _sslErrorHandler; QSharedPointer _am; QScopedPointer _credentials; bool _http2Supported = false; /// Certificates that were explicitly rejected by the user QList _rejectedCertificates; static QString _configFileName; ClientSideEncryption _e2e; /// Used in RemoteWipe bool _wroteAppPassword = false; friend class AccountManager; // Direct Editing QString _lastDirectEditingETag; PushNotifications *_pushNotifications = nullptr; std::unique_ptr _clientStatusReporting; std::shared_ptr _userStatusConnector; QHash> _lockStatusChangeInprogress; /* 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