| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822 |
- /*
- * 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.
- */
- #include "account.h"
- #include "accountfwd.h"
- #include "clientsideencryptionjobs.h"
- #include "cookiejar.h"
- #include "networkjobs.h"
- #include "configfile.h"
- #include "accessmanager.h"
- #include "creds/abstractcredentials.h"
- #include "capabilities.h"
- #include "theme.h"
- #include "pushnotifications.h"
- #include "version.h"
- #include <deletejob.h>
- #include "common/asserts.h"
- #include "clientsideencryption.h"
- #include "ocsuserstatusconnector.h"
- #include <QLoggingCategory>
- #include <QNetworkReply>
- #include <QNetworkAccessManager>
- #include <QSslSocket>
- #include <QNetworkCookieJar>
- #include <QNetworkProxy>
- #include <QFileInfo>
- #include <QDir>
- #include <QSslKey>
- #include <QAuthenticator>
- #include <QStandardPaths>
- #include <QJsonDocument>
- #include <QJsonObject>
- #include <QJsonArray>
- #include <QLoggingCategory>
- #include <QHttpMultiPart>
- #include <qsslconfiguration.h>
- #include <qt5keychain/keychain.h>
- #include "creds/abstractcredentials.h"
- using namespace QKeychain;
- namespace {
- constexpr int pushNotificationsReconnectInterval = 1000 * 60 * 2;
- constexpr int usernamePrefillServerVersionMinSupportedMajor = 24;
- constexpr int checksumRecalculateRequestServerVersionMinSupportedMajor = 24;
- }
- namespace OCC {
- Q_LOGGING_CATEGORY(lcAccount, "nextcloud.sync.account", QtInfoMsg)
- const char app_password[] = "_app-password";
- Account::Account(QObject *parent)
- : QObject(parent)
- , _capabilities(QVariantMap())
- {
- qRegisterMetaType<AccountPtr>("AccountPtr");
- qRegisterMetaType<Account *>("Account*");
- _pushNotificationsReconnectTimer.setInterval(pushNotificationsReconnectInterval);
- connect(&_pushNotificationsReconnectTimer, &QTimer::timeout, this, &Account::trySetupPushNotifications);
- }
- AccountPtr Account::create()
- {
- AccountPtr acc = AccountPtr(new Account);
- acc->setSharedThis(acc);
- return acc;
- }
- ClientSideEncryption* Account::e2e()
- {
- // Qt expects everything in the connect to be a pointer, so return a pointer.
- return &_e2e;
- }
- Account::~Account() = default;
- QString Account::davPath() const
- {
- return davPathBase() + QLatin1Char('/') + davUser() + QLatin1Char('/');
- }
- void Account::setSharedThis(AccountPtr sharedThis)
- {
- _sharedThis = sharedThis.toWeakRef();
- setupUserStatusConnector();
- }
- QString Account::davPathBase()
- {
- return QStringLiteral("/remote.php/dav/files");
- }
- AccountPtr Account::sharedFromThis()
- {
- return _sharedThis.toStrongRef();
- }
- QString Account::davUser() const
- {
- return _davUser.isEmpty() && _credentials ? _credentials->user() : _davUser;
- }
- void Account::setDavUser(const QString &newDavUser)
- {
- if (_davUser == newDavUser)
- return;
- _davUser = newDavUser;
- emit wantsAccountSaved(this);
- }
- #ifndef TOKEN_AUTH_ONLY
- QImage Account::avatar() const
- {
- return _avatarImg;
- }
- void Account::setAvatar(const QImage &img)
- {
- _avatarImg = img;
- emit accountChangedAvatar();
- }
- #endif
- QString Account::displayName() const
- {
- QString dn = QString("%1@%2").arg(credentials()->user(), _url.host());
- int port = url().port();
- if (port > 0 && port != 80 && port != 443) {
- dn.append(QLatin1Char(':'));
- dn.append(QString::number(port));
- }
- return dn;
- }
- QString Account::davDisplayName() const
- {
- return _displayName;
- }
- void Account::setDavDisplayName(const QString &newDisplayName)
- {
- _displayName = newDisplayName;
- emit accountChangedDisplayName();
- }
- QString Account::id() const
- {
- return _id;
- }
- AbstractCredentials *Account::credentials() const
- {
- return _credentials.data();
- }
- void Account::setCredentials(AbstractCredentials *cred)
- {
- // set active credential manager
- QNetworkCookieJar *jar = nullptr;
- QNetworkProxy proxy;
- if (_am) {
- jar = _am->cookieJar();
- jar->setParent(nullptr);
- // Remember proxy (issue #2108)
- proxy = _am->proxy();
- _am = QSharedPointer<QNetworkAccessManager>();
- }
- // The order for these two is important! Reading the credential's
- // settings accesses the account as well as account->_credentials,
- _credentials.reset(cred);
- cred->setAccount(this);
- // Note: This way the QNAM can outlive the Account and Credentials.
- // This is necessary to avoid issues with the QNAM being deleted while
- // processing slotHandleSslErrors().
- _am = QSharedPointer<QNetworkAccessManager>(_credentials->createQNAM(), &QObject::deleteLater);
- if (jar) {
- _am->setCookieJar(jar);
- }
- if (proxy.type() != QNetworkProxy::DefaultProxy) {
- _am->setProxy(proxy);
- }
- connect(_am.data(), SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)),
- SLOT(slotHandleSslErrors(QNetworkReply *, QList<QSslError>)));
- connect(_am.data(), &QNetworkAccessManager::proxyAuthenticationRequired,
- this, &Account::proxyAuthenticationRequired);
- connect(_credentials.data(), &AbstractCredentials::fetched,
- this, &Account::slotCredentialsFetched);
- connect(_credentials.data(), &AbstractCredentials::asked,
- this, &Account::slotCredentialsAsked);
- trySetupPushNotifications();
- }
- void Account::setPushNotificationsReconnectInterval(int interval)
- {
- _pushNotificationsReconnectTimer.setInterval(interval);
- }
- void Account::trySetupPushNotifications()
- {
- // Stop the timer to prevent parallel setup attempts
- _pushNotificationsReconnectTimer.stop();
- if (_capabilities.availablePushNotifications() != PushNotificationType::None) {
- qCInfo(lcAccount) << "Try to setup push notifications";
- if (!_pushNotifications) {
- _pushNotifications = new PushNotifications(this, this);
- connect(_pushNotifications, &PushNotifications::ready, this, [this]() {
- _pushNotificationsReconnectTimer.stop();
- emit pushNotificationsReady(this);
- });
- const auto disablePushNotifications = [this]() {
- qCInfo(lcAccount) << "Disable push notifications object because authentication failed or connection lost";
- if (!_pushNotifications) {
- return;
- }
- if (!_pushNotifications->isReady()) {
- emit pushNotificationsDisabled(this);
- }
- if (!_pushNotificationsReconnectTimer.isActive()) {
- _pushNotificationsReconnectTimer.start();
- }
- };
- connect(_pushNotifications, &PushNotifications::connectionLost, this, disablePushNotifications);
- connect(_pushNotifications, &PushNotifications::authenticationFailed, this, disablePushNotifications);
- }
- // If push notifications already running it is no problem to call setup again
- _pushNotifications->setup();
- }
- }
- QUrl Account::davUrl() const
- {
- return Utility::concatUrlPath(url(), davPath());
- }
- QUrl Account::deprecatedPrivateLinkUrl(const QByteArray &numericFileId) const
- {
- return Utility::concatUrlPath(_userVisibleUrl,
- QLatin1String("/index.php/f/") + QUrl::toPercentEncoding(QString::fromLatin1(numericFileId)));
- }
- /**
- * clear all cookies. (Session cookies or not)
- */
- void Account::clearCookieJar()
- {
- auto jar = qobject_cast<CookieJar *>(_am->cookieJar());
- ASSERT(jar);
- jar->setAllCookies(QList<QNetworkCookie>());
- emit wantsAccountSaved(this);
- }
- /*! This shares our official cookie jar (containing all the tasty
- authentication cookies) with another QNAM while making sure
- of not losing its ownership. */
- void Account::lendCookieJarTo(QNetworkAccessManager *guest)
- {
- auto jar = _am->cookieJar();
- auto oldParent = jar->parent();
- guest->setCookieJar(jar); // takes ownership of our precious cookie jar
- jar->setParent(oldParent); // takes it back
- }
- QString Account::cookieJarPath()
- {
- return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/cookies" + id() + ".db";
- }
- void Account::resetNetworkAccessManager()
- {
- if (!_credentials || !_am) {
- return;
- }
- qCDebug(lcAccount) << "Resetting QNAM";
- QNetworkCookieJar *jar = _am->cookieJar();
- QNetworkProxy proxy = _am->proxy();
- // Use a QSharedPointer to allow locking the life of the QNAM on the stack.
- // Make it call deleteLater to make sure that we can return to any QNAM stack frames safely.
- _am = QSharedPointer<QNetworkAccessManager>(_credentials->createQNAM(), &QObject::deleteLater);
- _am->setCookieJar(jar); // takes ownership of the old cookie jar
- _am->setProxy(proxy); // Remember proxy (issue #2108)
- connect(_am.data(), SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)),
- SLOT(slotHandleSslErrors(QNetworkReply *, QList<QSslError>)));
- connect(_am.data(), &QNetworkAccessManager::proxyAuthenticationRequired,
- this, &Account::proxyAuthenticationRequired);
- }
- QNetworkAccessManager *Account::networkAccessManager()
- {
- return _am.data();
- }
- QSharedPointer<QNetworkAccessManager> Account::sharedNetworkAccessManager()
- {
- return _am;
- }
- QNetworkReply *Account::sendRawRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
- {
- req.setUrl(url);
- req.setSslConfiguration(this->getOrCreateSslConfig());
- if (verb == "HEAD" && !data) {
- return _am->head(req);
- } else if (verb == "GET" && !data) {
- return _am->get(req);
- } else if (verb == "POST") {
- return _am->post(req, data);
- } else if (verb == "PUT") {
- return _am->put(req, data);
- } else if (verb == "DELETE" && !data) {
- return _am->deleteResource(req);
- }
- return _am->sendCustomRequest(req, verb, data);
- }
- QNetworkReply *Account::sendRawRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, const QByteArray &data)
- {
- req.setUrl(url);
- req.setSslConfiguration(this->getOrCreateSslConfig());
- if (verb == "HEAD" && data.isEmpty()) {
- return _am->head(req);
- } else if (verb == "GET" && data.isEmpty()) {
- return _am->get(req);
- } else if (verb == "POST") {
- return _am->post(req, data);
- } else if (verb == "PUT") {
- return _am->put(req, data);
- } else if (verb == "DELETE" && data.isEmpty()) {
- return _am->deleteResource(req);
- }
- return _am->sendCustomRequest(req, verb, data);
- }
- QNetworkReply *Account::sendRawRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QHttpMultiPart *data)
- {
- req.setUrl(url);
- req.setSslConfiguration(this->getOrCreateSslConfig());
- if (verb == "PUT") {
- return _am->put(req, data);
- } else if (verb == "POST") {
- return _am->post(req, data);
- }
- return _am->sendCustomRequest(req, verb, data);
- }
- SimpleNetworkJob *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
- {
- auto job = new SimpleNetworkJob(sharedFromThis());
- job->startRequest(verb, url, req, data);
- return job;
- }
- void Account::setSslConfiguration(const QSslConfiguration &config)
- {
- _sslConfiguration = config;
- }
- QSslConfiguration Account::getOrCreateSslConfig()
- {
- if (!_sslConfiguration.isNull()) {
- // Will be set by CheckServerJob::finished()
- // We need to use a central shared config to get SSL session tickets
- return _sslConfiguration;
- }
- // if setting the client certificate fails, you will probably get an error similar to this:
- // "An internal error number 1060 happened. SSL handshake failed, client certificate was requested: SSL error: sslv3 alert handshake failure"
- QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
- // Try hard to re-use session for different requests
- sslConfig.setSslOption(QSsl::SslOptionDisableSessionTickets, false);
- sslConfig.setSslOption(QSsl::SslOptionDisableSessionSharing, false);
- sslConfig.setSslOption(QSsl::SslOptionDisableSessionPersistence, false);
- sslConfig.setOcspStaplingEnabled(Theme::instance()->enableStaplingOCSP());
- return sslConfig;
- }
- void Account::setApprovedCerts(const QList<QSslCertificate> certs)
- {
- _approvedCerts = certs;
- QSslConfiguration::defaultConfiguration().addCaCertificates(certs);
- }
- void Account::addApprovedCerts(const QList<QSslCertificate> certs)
- {
- _approvedCerts += certs;
- }
- void Account::resetRejectedCertificates()
- {
- _rejectedCertificates.clear();
- }
- void Account::setSslErrorHandler(AbstractSslErrorHandler *handler)
- {
- _sslErrorHandler.reset(handler);
- }
- void Account::setUrl(const QUrl &url)
- {
- _url = url;
- _userVisibleUrl = url;
- }
- void Account::setUserVisibleHost(const QString &host)
- {
- _userVisibleUrl.setHost(host);
- }
- QVariant Account::credentialSetting(const QString &key) const
- {
- if (_credentials) {
- QString prefix = _credentials->authType();
- QVariant value = _settingsMap.value(prefix + "_" + key);
- if (value.isNull()) {
- value = _settingsMap.value(key);
- }
- return value;
- }
- return QVariant();
- }
- void Account::setCredentialSetting(const QString &key, const QVariant &value)
- {
- if (_credentials) {
- QString prefix = _credentials->authType();
- _settingsMap.insert(prefix + "_" + key, value);
- }
- }
- void Account::slotHandleSslErrors(QNetworkReply *reply, QList<QSslError> errors)
- {
- NetworkJobTimeoutPauser pauser(reply);
- QString out;
- QDebug(&out) << "SSL-Errors happened for url " << reply->url().toString();
- foreach (const QSslError &error, errors) {
- QDebug(&out) << "\tError in " << error.certificate() << ":"
- << error.errorString() << "(" << error.error() << ")"
- << "\n";
- }
- qCInfo(lcAccount()) << "ssl errors" << out;
- qCInfo(lcAccount()) << reply->sslConfiguration().peerCertificateChain();
- bool allPreviouslyRejected = true;
- foreach (const QSslError &error, errors) {
- if (!_rejectedCertificates.contains(error.certificate())) {
- allPreviouslyRejected = false;
- }
- }
- // If all certs have previously been rejected by the user, don't ask again.
- if (allPreviouslyRejected) {
- qCInfo(lcAccount) << out << "Certs not trusted by user decision, returning.";
- return;
- }
- QList<QSslCertificate> approvedCerts;
- if (_sslErrorHandler.isNull()) {
- qCWarning(lcAccount) << out << "called without valid SSL error handler for account" << url();
- return;
- }
- // SslDialogErrorHandler::handleErrors will run an event loop that might execute
- // the deleteLater() of the QNAM before we have the chance of unwinding our stack.
- // Keep a ref here on our stackframe to make sure that it doesn't get deleted before
- // handleErrors returns.
- QSharedPointer<QNetworkAccessManager> qnamLock = _am;
- QPointer<QObject> guard = reply;
- if (_sslErrorHandler->handleErrors(errors, reply->sslConfiguration(), &approvedCerts, sharedFromThis())) {
- if (!guard)
- return;
- if (!approvedCerts.isEmpty()) {
- QSslConfiguration::defaultConfiguration().addCaCertificates(approvedCerts);
- addApprovedCerts(approvedCerts);
- emit wantsAccountSaved(this);
- // all ssl certs are known and accepted. We can ignore the problems right away.
- qCInfo(lcAccount) << out << "Certs are known and trusted! This is not an actual error.";
- }
- // Warning: Do *not* use ignoreSslErrors() (without args) here:
- // it permanently ignores all SSL errors for this host, even
- // certificate changes.
- reply->ignoreSslErrors(errors);
- } else {
- if (!guard)
- return;
- // Mark all involved certificates as rejected, so we don't ask the user again.
- foreach (const QSslError &error, errors) {
- if (!_rejectedCertificates.contains(error.certificate())) {
- _rejectedCertificates.append(error.certificate());
- }
- }
- // Not calling ignoreSslErrors will make the SSL handshake fail.
- return;
- }
- }
- void Account::slotCredentialsFetched()
- {
- if (_davUser.isEmpty()) {
- qCDebug(lcAccount) << "User id not set. Fetch it.";
- const auto fetchUserNameJob = new JsonApiJob(sharedFromThis(), QStringLiteral("/ocs/v1.php/cloud/user"));
- connect(fetchUserNameJob, &JsonApiJob::jsonReceived, this, [this, fetchUserNameJob](const QJsonDocument &json, int statusCode) {
- fetchUserNameJob->deleteLater();
- if (statusCode != 100) {
- qCWarning(lcAccount) << "Could not fetch user id. Login will probably not work.";
- emit credentialsFetched(_credentials.data());
- return;
- }
- const auto objData = json.object().value("ocs").toObject().value("data").toObject();
- const auto userId = objData.value("id").toString("");
- setDavUser(userId);
- emit credentialsFetched(_credentials.data());
- });
- fetchUserNameJob->start();
- } else {
- qCDebug(lcAccount) << "User id already fetched.";
- emit credentialsFetched(_credentials.data());
- }
- }
- void Account::slotCredentialsAsked()
- {
- emit credentialsAsked(_credentials.data());
- }
- void Account::handleInvalidCredentials()
- {
- // Retrieving password will trigger remote wipe check job
- retrieveAppPassword();
- emit invalidCredentials();
- }
- void Account::clearQNAMCache()
- {
- _am->clearAccessCache();
- }
- const Capabilities &Account::capabilities() const
- {
- return _capabilities;
- }
- void Account::setCapabilities(const QVariantMap &caps)
- {
- _capabilities = Capabilities(caps);
- setupUserStatusConnector();
- trySetupPushNotifications();
- }
- void Account::setupUserStatusConnector()
- {
- _userStatusConnector = std::make_shared<OcsUserStatusConnector>(sharedFromThis());
- connect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this, [this](const UserStatus &) {
- emit userStatusChanged();
- });
- connect(_userStatusConnector.get(), &UserStatusConnector::messageCleared, this, [this] {
- emit userStatusChanged();
- });
- }
- QString Account::serverVersion() const
- {
- return _serverVersion;
- }
- int Account::serverVersionInt() const
- {
- // FIXME: Use Qt 5.5 QVersionNumber
- auto components = serverVersion().split('.');
- return makeServerVersion(components.value(0).toInt(),
- components.value(1).toInt(),
- components.value(2).toInt());
- }
- int Account::makeServerVersion(int majorVersion, int minorVersion, int patchVersion)
- {
- return (majorVersion << 16) + (minorVersion << 8) + patchVersion;
- }
- bool Account::serverVersionUnsupported() const
- {
- if (serverVersionInt() == 0) {
- // not detected yet, assume it is fine.
- return false;
- }
- return serverVersionInt() < makeServerVersion(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MAJOR,
- NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MINOR, NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_PATCH);
- }
- bool Account::isUsernamePrefillSupported() const
- {
- return serverVersionInt() >= makeServerVersion(usernamePrefillServerVersionMinSupportedMajor, 0, 0);
- }
- bool Account::isChecksumRecalculateRequestSupported() const
- {
- return serverVersionInt() >= makeServerVersion(checksumRecalculateRequestServerVersionMinSupportedMajor, 0, 0);
- }
- int Account::checksumRecalculateServerVersionMinSupportedMajor() const
- {
- return checksumRecalculateRequestServerVersionMinSupportedMajor;
- }
- void Account::setServerVersion(const QString &version)
- {
- if (version == _serverVersion) {
- return;
- }
- auto oldServerVersion = _serverVersion;
- _serverVersion = version;
- emit serverVersionChanged(this, oldServerVersion, version);
- }
- void Account::writeAppPasswordOnce(QString appPassword){
- if(_wroteAppPassword)
- return;
- // Fix: Password got written from Account Wizard, before finish.
- // Only write the app password for a connected account, else
- // there'll be a zombie keychain slot forever, never used again ;p
- //
- // Also don't write empty passwords (Log out -> Relaunch)
- if(id().isEmpty() || appPassword.isEmpty())
- return;
- const QString kck = AbstractCredentials::keychainKey(
- url().toString(),
- davUser() + app_password,
- id()
- );
- auto *job = new WritePasswordJob(Theme::instance()->appName());
- job->setInsecureFallback(false);
- job->setKey(kck);
- job->setBinaryData(appPassword.toLatin1());
- connect(job, &WritePasswordJob::finished, [this](Job *incoming) {
- auto *writeJob = static_cast<WritePasswordJob *>(incoming);
- if (writeJob->error() == NoError)
- qCInfo(lcAccount) << "appPassword stored in keychain";
- else
- qCWarning(lcAccount) << "Unable to store appPassword in keychain" << writeJob->errorString();
- // We don't try this again on error, to not raise CPU consumption
- _wroteAppPassword = true;
- });
- job->start();
- }
- void Account::retrieveAppPassword(){
- const QString kck = AbstractCredentials::keychainKey(
- url().toString(),
- credentials()->user() + app_password,
- id()
- );
- auto *job = new ReadPasswordJob(Theme::instance()->appName());
- job->setInsecureFallback(false);
- job->setKey(kck);
- connect(job, &ReadPasswordJob::finished, [this](Job *incoming) {
- auto *readJob = static_cast<ReadPasswordJob *>(incoming);
- QString pwd("");
- // Error or no valid public key error out
- if (readJob->error() == NoError &&
- readJob->binaryData().length() > 0) {
- pwd = readJob->binaryData();
- }
- emit appPasswordRetrieved(pwd);
- });
- job->start();
- }
- void Account::deleteAppPassword()
- {
- const QString kck = AbstractCredentials::keychainKey(
- url().toString(),
- credentials()->user() + app_password,
- id()
- );
- if (kck.isEmpty()) {
- qCDebug(lcAccount) << "appPassword is empty";
- return;
- }
- auto *job = new DeletePasswordJob(Theme::instance()->appName());
- job->setInsecureFallback(false);
- job->setKey(kck);
- connect(job, &DeletePasswordJob::finished, [this](Job *incoming) {
- auto *deleteJob = static_cast<DeletePasswordJob *>(incoming);
- if (deleteJob->error() == NoError)
- qCInfo(lcAccount) << "appPassword deleted from keychain";
- else
- qCWarning(lcAccount) << "Unable to delete appPassword from keychain" << deleteJob->errorString();
- // Allow storing a new app password on re-login
- _wroteAppPassword = false;
- });
- job->start();
- }
- void Account::deleteAppToken()
- {
- const auto deleteAppTokenJob = new DeleteJob(sharedFromThis(), QStringLiteral("/ocs/v2.php/core/apppassword"));
- connect(deleteAppTokenJob, &DeleteJob::finishedSignal, this, [this]() {
- if (const auto deleteJob = qobject_cast<DeleteJob *>(QObject::sender())) {
- const auto httpCode = deleteJob->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (httpCode != 200) {
- qCWarning(lcAccount) << "AppToken remove failed for user: " << displayName() << " with code: " << httpCode;
- } else {
- qCInfo(lcAccount) << "AppToken for user: " << displayName() << " has been removed.";
- }
- } else {
- Q_ASSERT(false);
- qCWarning(lcAccount) << "The sender is not a DeleteJob instance.";
- }
- });
- deleteAppTokenJob->start();
- }
- void Account::fetchDirectEditors(const QUrl &directEditingURL, const QString &directEditingETag)
- {
- if(directEditingURL.isEmpty() || directEditingETag.isEmpty())
- return;
- // Check for the directEditing capability
- if (!directEditingURL.isEmpty() &&
- (directEditingETag.isEmpty() || directEditingETag != _lastDirectEditingETag)) {
- // Fetch the available editors and their mime types
- auto *job = new JsonApiJob(sharedFromThis(), QLatin1String("ocs/v2.php/apps/files/api/v1/directEditing"));
- QObject::connect(job, &JsonApiJob::jsonReceived, this, &Account::slotDirectEditingRecieved);
- job->start();
- }
- }
- void Account::slotDirectEditingRecieved(const QJsonDocument &json)
- {
- auto data = json.object().value("ocs").toObject().value("data").toObject();
- auto editors = data.value("editors").toObject();
- foreach (auto editorKey, editors.keys()) {
- auto editor = editors.value(editorKey).toObject();
- const QString id = editor.value("id").toString();
- const QString name = editor.value("name").toString();
- if(!id.isEmpty() && !name.isEmpty()) {
- auto mimeTypes = editor.value("mimetypes").toArray();
- auto optionalMimeTypes = editor.value("optionalMimetypes").toArray();
- auto *directEditor = new DirectEditor(id, name);
- foreach(auto mimeType, mimeTypes) {
- directEditor->addMimetype(mimeType.toString().toLatin1());
- }
- foreach(auto optionalMimeType, optionalMimeTypes) {
- directEditor->addOptionalMimetype(optionalMimeType.toString().toLatin1());
- }
- _capabilities.addDirectEditor(directEditor);
- }
- }
- }
- PushNotifications *Account::pushNotifications() const
- {
- return _pushNotifications;
- }
- std::shared_ptr<UserStatusConnector> Account::userStatusConnector() const
- {
- return _userStatusConnector;
- }
- } // namespace OCC
|