From b86d76f546758a558be5fda85a542f4109452346 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Tue, 13 May 2025 13:55:06 +0200 Subject: [PATCH] feat(debug): add sync type (classical or virtual) in user agent should enable easier debug by looking at web server access logs or server logs by just checking the user agent from desktop clients Signed-off-by: Matthieu Gallien --- src/common/utility.cpp | 21 +++++++++++---------- src/common/utility.h | 2 +- src/gui/creds/webflowcredentials.cpp | 1 + src/gui/systray.cpp | 1 + src/gui/updater/ocupdater.cpp | 5 +++++ src/gui/updater/ocupdater.h | 6 ++++-- src/gui/wizard/webview.cpp | 2 +- src/libsync/accessmanager.cpp | 13 ++++++++++++- src/libsync/accessmanager.h | 7 +++++++ src/libsync/account.cpp | 20 ++++++++++++++++++++ src/libsync/account.h | 4 ++++ src/libsync/syncengine.cpp | 13 ++++++++++++- 12 files changed, 79 insertions(+), 16 deletions(-) diff --git a/src/common/utility.cpp b/src/common/utility.cpp index 7df0d7d73b085..c1ada05e36868 100644 --- a/src/common/utility.cpp +++ b/src/common/utility.cpp @@ -151,16 +151,17 @@ static QLatin1String platform() #endif } -QByteArray Utility::userAgentString() -{ - return QStringLiteral("Mozilla/5.0 (%1) mirall/%2 (%3, %4-%5 ClientArchitecture: %6 OsArchitecture: %7)") - .arg(platform(), - QStringLiteral(MIRALL_VERSION_STRING), - qApp->applicationName(), - QSysInfo::productType(), - QSysInfo::kernelVersion(), - QSysInfo::buildCpuArchitecture(), - QSysInfo::currentCpuArchitecture()) +QByteArray Utility::userAgentString(const QString &synchronizationType) +{ + return QStringLiteral("Mozilla/5.0 (%1) mirall/%2 (%3, %4-%5 ClientArchitecture: %6 OsArchitecture: %7 SyncType: %8)") + .arg(platform(), + QStringLiteral(MIRALL_VERSION_STRING), + qApp->applicationName(), + QSysInfo::productType(), + QSysInfo::kernelVersion(), + QSysInfo::buildCpuArchitecture(), + QSysInfo::currentCpuArchitecture(), + synchronizationType) .toLatin1(); } diff --git a/src/common/utility.h b/src/common/utility.h index dc51338ea146a..4176553dc10d2 100644 --- a/src/common/utility.h +++ b/src/common/utility.h @@ -68,7 +68,7 @@ namespace Utility { OCSYNC_EXPORT bool writeRandomFile(const QString &fname, int size = -1); OCSYNC_EXPORT QString octetsToString(const qint64 octets); - OCSYNC_EXPORT QByteArray userAgentString(); + OCSYNC_EXPORT QByteArray userAgentString(const QString &synchronizationType); OCSYNC_EXPORT QByteArray friendlyUserAgentString(); /** * @brief Return whether launch on startup is enabled system wide. diff --git a/src/gui/creds/webflowcredentials.cpp b/src/gui/creds/webflowcredentials.cpp index 9f77b69ff5c88..b9bfeda5b57b3 100644 --- a/src/gui/creds/webflowcredentials.cpp +++ b/src/gui/creds/webflowcredentials.cpp @@ -46,6 +46,7 @@ class WebFlowCredentialsAccessManager : public AccessManager : AccessManager(parent) , _cred(cred) { + setSynchronizationType(QStringLiteral("login")); } protected: diff --git a/src/gui/systray.cpp b/src/gui/systray.cpp index 6ed0b78399682..e075f337fd73e 100644 --- a/src/gui/systray.cpp +++ b/src/gui/systray.cpp @@ -972,6 +972,7 @@ AccessManagerFactory::AccessManagerFactory() QNetworkAccessManager* AccessManagerFactory::create(QObject *parent) { const auto am = new AccessManager(parent); + am->setSynchronizationType(QStringLiteral("Systray")); const auto diskCache = new QNetworkDiskCache(am); diskCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); am->setCache(diskCache); diff --git a/src/gui/updater/ocupdater.cpp b/src/gui/updater/ocupdater.cpp index febe136816168..03455cd92f0c2 100644 --- a/src/gui/updater/ocupdater.cpp +++ b/src/gui/updater/ocupdater.cpp @@ -81,6 +81,7 @@ OCUpdater::OCUpdater(const QUrl &url) , _accessManager(new AccessManager(this)) , _timeoutWatchdog(new QTimer(this)) { + _accessManager->setSynchronizationType(QStringLiteral("updater")); } void OCUpdater::setUpdateUrl(const QUrl &url) @@ -248,6 +249,10 @@ bool OCUpdater::updateSucceeded() const return currentVersion >= targetVersionInt; } +QNetworkAccessManager *OCUpdater::qnam() const { + return _accessManager; +} + void OCUpdater::slotVersionInfoArrived() { qCDebug(lcUpdater) << "received a reply"; diff --git a/src/gui/updater/ocupdater.h b/src/gui/updater/ocupdater.h index 25c477b6864dd..c5498e5a6e360 100644 --- a/src/gui/updater/ocupdater.h +++ b/src/gui/updater/ocupdater.h @@ -73,6 +73,8 @@ private slots: QTimer _updateCheckTimer; /** Timer for the regular update check. */ }; +class AccessManager; + /** * @brief Class that uses an ownCloud proprietary XML format to fetch update information * @ingroup gui @@ -126,13 +128,13 @@ private slots: protected: virtual void versionInfoArrived(const UpdateInfo &info) = 0; [[nodiscard]] bool updateSucceeded() const; - [[nodiscard]] QNetworkAccessManager *qnam() const { return _accessManager; } + [[nodiscard]] QNetworkAccessManager *qnam() const; [[nodiscard]] UpdateInfo updateInfo() const { return _updateInfo; } private: QUrl _updateUrl; int _state = Unknown; - QNetworkAccessManager *_accessManager; + AccessManager *_accessManager; QTimer *_timeoutWatchdog; /** Timer to guard the timeout of an individual network request */ UpdateInfo _updateInfo; }; diff --git a/src/gui/wizard/webview.cpp b/src/gui/wizard/webview.cpp index 8e661a0b6a82d..3fdb170b63359 100644 --- a/src/gui/wizard/webview.cpp +++ b/src/gui/wizard/webview.cpp @@ -88,7 +88,7 @@ WebView::WebView(QWidget *parent) _interceptor = new WebViewPageUrlRequestInterceptor(this); _schemeHandler = new WebViewPageUrlSchemeHandler(this); - const QString userAgent(Utility::userAgentString()); + const QString userAgent(Utility::userAgentString(QStringLiteral("login"))); _profile->setHttpUserAgent(userAgent); QWebEngineProfile::defaultProfile()->setHttpUserAgent(userAgent); _profile->setUrlRequestInterceptor(_interceptor); diff --git a/src/libsync/accessmanager.cpp b/src/libsync/accessmanager.cpp index 6ad08940ad03f..67c9f8f833ae9 100644 --- a/src/libsync/accessmanager.cpp +++ b/src/libsync/accessmanager.cpp @@ -38,6 +38,16 @@ AccessManager::AccessManager(QObject *parent) }); } +const QString &AccessManager::synchronizationType() const +{ + return _synchronizationType; +} + +void AccessManager::setSynchronizationType(const QString &type) +{ + _synchronizationType = type; +} + QByteArray AccessManager::generateRequestId() { return QUuid::createUuid().toByteArray(QUuid::WithoutBraces); @@ -49,7 +59,8 @@ QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op, // Respect request specific user agent if any if (!newRequest.header(QNetworkRequest::UserAgentHeader).isValid()) { - newRequest.setHeader(QNetworkRequest::UserAgentHeader, Utility::userAgentString()); + Q_ASSERT(!_synchronizationType.isEmpty()); + newRequest.setHeader(QNetworkRequest::UserAgentHeader, Utility::userAgentString(_synchronizationType)); } // Some firewalls reject requests that have a "User-Agent" but no "Accept" header diff --git a/src/libsync/accessmanager.h b/src/libsync/accessmanager.h index 7b2a16e05f531..afd86addba2ee 100644 --- a/src/libsync/accessmanager.h +++ b/src/libsync/accessmanager.h @@ -28,8 +28,15 @@ class OWNCLOUDSYNC_EXPORT AccessManager : public QNetworkAccessManager AccessManager(QObject *parent = nullptr); + [[nodiscard]] const QString& synchronizationType() const; + + void setSynchronizationType(const QString &type); + protected: QNetworkReply *createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData = nullptr) override; + +private: + QString _synchronizationType; }; } // namespace OCC diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index fa620bb25fc1d..99ae6403de106 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -284,6 +284,11 @@ void Account::setCredentials(AbstractCredentials *cred) // processing slotHandleSslErrors(). _networkAccessManager = QSharedPointer(_credentials->createQNAM(), &QObject::deleteLater); + const auto accessManager = qobject_cast(_networkAccessManager.get()); + if (accessManager) { + accessManager->setSynchronizationType(QStringLiteral("account")); + } + if (jar) { _networkAccessManager->setCookieJar(jar); } @@ -414,6 +419,11 @@ void Account::resetNetworkAccessManager() // Make it call deleteLater to make sure that we can return to any QNAM stack frames safely. _networkAccessManager = QSharedPointer(_credentials->createQNAM(), &QObject::deleteLater); + const auto accessManager = qobject_cast(_networkAccessManager.get()); + if (accessManager) { + accessManager->setSynchronizationType(QStringLiteral("account")); + } + _networkAccessManager->setCookieJar(jar); // takes ownership of the old cookie jar _networkAccessManager->setProxy(proxy); // Remember proxy (issue #2108) @@ -1132,6 +1142,16 @@ void Account::setEncryptionCertificateFingerprint(const QByteArray &fingerprint) Q_EMIT wantsAccountSaved(sharedFromThis()); } +const QString &Account::synchronizationType() const +{ + return static_cast(_networkAccessManager.get())->synchronizationType(); +} + +void Account::setSynchronizationType(const QString &type) +{ + static_cast(_networkAccessManager.get())->setSynchronizationType(type); +} + void Account::setAskUserForMnemonic(const bool ask) { _e2eAskUserForMnemonic = ask; diff --git a/src/libsync/account.h b/src/libsync/account.h index eaa636b3ad0e3..144fce44ca73d 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -418,6 +418,10 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject [[nodiscard]] QByteArray encryptionCertificateFingerprint() const; void setEncryptionCertificateFingerprint(const QByteArray &fingerprint); + [[nodiscard]] const QString& synchronizationType() const; + + void setSynchronizationType(const QString &type); + public slots: /// Used when forgetting credentials void clearQNAMCache(); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 23dd4b9f26b80..d784952641efa 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -583,7 +583,18 @@ void SyncEngine::startSync() verStr.append(" SSL library ").append(QSslSocket::sslLibraryVersionString().toUtf8().data()); verStr.append(" on ").append(Utility::platformName()); - qCInfo(lcEngine) << verStr; + qCInfo(lcEngine) << verStr << "vfs mode: " << _syncOptions._vfs->mode(); + + switch (_syncOptions._vfs->mode()) + { + case Vfs::Off: + _account->setSynchronizationType(QStringLiteral("classical")); + case Vfs::WithSuffix: + case Vfs::WindowsCfApi: + case Vfs::XAttr: + _account->setSynchronizationType(QStringLiteral("virtual")); + break; + } // This creates the DB if it does not exist yet. if (!_journal->open()) {