Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libQuotient for openSUSE:Factory checked in at 2021-01-18 11:28:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libQuotient (Old) and /work/SRC/openSUSE:Factory/.libQuotient.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libQuotient" Mon Jan 18 11:28:48 2021 rev:2 rq:863702 version:0.6.4 Changes: -------- --- /work/SRC/openSUSE:Factory/libQuotient/libQuotient.changes 2021-01-15 19:45:47.753983199 +0100 +++ /work/SRC/openSUSE:Factory/.libQuotient.new.28504/libQuotient.changes 2021-01-18 11:32:34.716697847 +0100 @@ -1,0 +2,11 @@ +Sat Jan 16 15:40:44 UTC 2021 - Luca Beltrame <lbeltr...@kde.org> + +- Actually disable e2ee + +------------------------------------------------------------------- +Fri Jan 15 16:17:38 UTC 2021 - Dead Mozay <dead_mo...@opensuse.org> + +- Update to version 0.6.4: + * Several fixes around homeserver resolution. + +------------------------------------------------------------------- Old: ---- 0.6.3.tar.gz New: ---- 0.6.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libQuotient.spec ++++++ --- /var/tmp/diff_new_pack.VoDfV9/_old 2021-01-18 11:32:35.284698406 +0100 +++ /var/tmp/diff_new_pack.VoDfV9/_new 2021-01-18 11:32:35.288698410 +0100 @@ -17,9 +17,9 @@ %define soversion 0_6 -%bcond_without e2ee +%bcond_with e2ee Name: libQuotient -Version: 0.6.3 +Version: 0.6.4 Release: 0 Summary: Library for Qt Matrix Clients License: LGPL-2.1-only ++++++ 0.6.3.tar.gz -> 0.6.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/.clang-format new/libQuotient-0.6.4/.clang-format --- old/libQuotient-0.6.3/.clang-format 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/.clang-format 2021-01-15 16:53:46.000000000 +0100 @@ -92,7 +92,7 @@ #IncludeIsMainRegex: '(_test)?$' #IncludeIsMainSourceRegex: '' #IndentCaseLabels: false -IndentGotoLabels: false +#IndentGotoLabels: false # Uncomment once on ClangFormat 10 IndentPPDirectives: AfterHash #IndentWidth: 4 #IndentWrappedFunctionNames: false @@ -122,7 +122,7 @@ #SpaceBeforeInheritanceColon: true #SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyBlock: false +#SpaceInEmptyBlock: false # Uncomment once on ClangFormat 10 #SpaceInEmptyParentheses: false #SpacesBeforeTrailingComments: 1 #SpacesInAngles: false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/.github/workflows/ci.yml new/libQuotient-0.6.4/.github/workflows/ci.yml --- old/libQuotient-0.6.3/.github/workflows/ci.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/libQuotient-0.6.4/.github/workflows/ci.yml 2021-01-15 16:53:46.000000000 +0100 @@ -0,0 +1,100 @@ +name: CI + +on: + push: + pull_request: + types: [opened, reopened] + +defaults: + run: + shell: bash + +jobs: + CI: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + max-parallel: 1 + matrix: + os: [ubuntu-18.04, macos-10.15] + compiler: [ GCC, Clang ] + # Not using binary values here, to make the job captions more readable + update-api: [ '', 'update-api' ] + exclude: + - os: macos-10.15 + compiler: GCC + + steps: + - uses: actions/checkout@v2 + with: + submodules: false + + - name: Cache Qt + id: cache-qt + uses: actions/cache@v2 + with: + path: ${{ runner.workspace }}/Qt + key: ${{ runner.os }}-QtCache + + - name: Install Qt + uses: jurplel/install-qt-action@v2.11.1 + with: + version: '5.9.9' + cached: ${{ steps.cache-qt.outputs.cache-hit }} + + - name: Install Valgrind + if: contains(matrix.os, 'ubuntu') + run: | + sudo apt-get install valgrind + echo "VALGRIND=valgrind --tool=memcheck --leak-check=yes --gen-suppressions=all --suppressions=tests/.valgrind.supp" >>$GITHUB_ENV + + - name: Setup build environment + run: | + if [ "${{ matrix.compiler }}" == "GCC" ]; then + if [ -n "${{ matrix.update-api }}" ]; then VERSION_POSTFIX='-8'; fi + echo "CC=gcc$VERSION_POSTFIX" >>$GITHUB_ENV + echo "CXX=g++$VERSION_POSTFIX" >>$GITHUB_ENV + else + echo "CC=clang" >>$GITHUB_ENV + echo "CXX=clang++" >>$GITHUB_ENV + fi + echo "QUOTEST_ORIGIN=${{ runner.os }}/${{ matrix.compiler }}" >>$GITHUB_ENV + echo "DESTDIR=${{ runner.workspace }}" >>$GITHUB_ENV + echo "CMAKE_ARGS=-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_PREFIX_PATH=${{ runner.workspace }}/usr" >>$GITHUB_ENV + cmake -E make_directory ${{ runner.workspace }}/build + + - name: Pull API definitions and build GTAD + if: matrix.update-api + run: | + cd ${{ runner.workspace }} + git clone https://github.com/matrix-org/matrix-doc.git + git clone --recursive https://github.com/KitsuneRal/gtad.git + pushd gtad + cmake . $CMAKE_ARGS + cmake --build . + popd + echo "CMAKE_ARGS=$CMAKE_ARGS -DMATRIX_DOC_PATH=${{ runner.workspace }}/matrix-doc -DGTAD_PATH=${{ runner.workspace }}/gtad/gtad" >>$GITHUB_ENV + echo "QUOTEST_ORIGIN=$QUOTEST_ORIGIN with generated API files" >>$GITHUB_ENV + + - name: Configure libQuotient + run: cmake -S $GITHUB_WORKSPACE -B build $CMAKE_ARGS + + - name: Generate API files + if: matrix.update-api + run: cmake --build build --target update-api + + - name: Build and install libQuotient + run: cmake --build build --target install + + - name: Build tests + run: | + cmake tests -Bbuild-test $CMAKE_ARGS + cmake --build build-test --target all + + - name: Run tests + env: + TEST_USER: ${{ secrets.TEST_USER }} + TEST_PWD: ${{ secrets.TEST_PWD }} + run: | + [[ -z "$TEST_USER" ]] || $VALGRIND build-test/quotest "$TEST_USER" "$TEST_PWD" quotest-gha '#quotest:matrix.org' "$QUOTEST_ORIGIN" + timeout-minutes: 5 # quotest is supposed to finish within 3 minutes, actually diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/.lgtm.yml new/libQuotient-0.6.4/.lgtm.yml --- old/libQuotient-0.6.3/.lgtm.yml 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/.lgtm.yml 2021-01-15 16:53:46.000000000 +0100 @@ -1,3 +1,8 @@ +path_classifiers: + library: + - 3rdparty/* + test: + - exclude: tests/quotest.cpp # Let alerts from this come up too extraction: cpp: prepare: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/.travis.yml new/libQuotient-0.6.4/.travis.yml --- old/libQuotient-0.6.3/.travis.yml 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,116 +0,0 @@ -language: cpp -dist: bionic - -git: - depth: false - -addons: - apt: - packages: - - ninja-build - - qt5-default - - qtmultimedia5-dev - - valgrind - - g++-8 - -env: - global: - - DESTDIR="$TRAVIS_BUILD_DIR/install" - - CMAKE_ARGS="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_PREFIX_PATH=$DESTDIR/usr" - - VALGRIND="valgrind --tool=memcheck --leak-check=yes --gen-suppressions=all --suppressions=tests/.valgrind.supp $VALGRIND_OPTIONS" - -matrix: # TODO: consider parallel execution, this thing takes an hour to run - include: - - os: linux - compiler: gcc - - os: linux - compiler: clang - - os: osx - osx_image: xcode10.1 - env: [ 'E2EE=1', 'VALGRIND=' ] - addons: - homebrew: - update: true - packages: - - qt5 - before_cache: - - brew cleanup - cache: - directories: - - $HOME/Library/Caches/Homebrew - # Check a few more advanced configurations - - os: linux - compiler: gcc - env: [ E2EE=1, UPDATE_API=1 ] # Check UPDATE_API with one of fatter options - - os: linux - compiler: clang - env: [ E2EE=1 ] - - os: linux - compiler: gcc - env: [ E2EE=1 ] - -before_install: -- if [ -f "$(which ninja)" ]; then export CMAKE_ARGS="$CMAKE_ARGS -GNinja"; fi -- if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PATH=/usr/local/opt/qt/bin:$PATH; fi -# The recent GTAD uses std::filesystem that's not available in stock bionic -- | - if [ -n "$UPDATE_API" ]; then - export CC=gcc-8 CXX=g++-8 - export CMAKE_UPDATE_API_ARGS="-DMATRIX_DOC_PATH=../matrix-doc -DGTAD_PATH=../gtad/gtad" - fi -- | - if [ -n "$E2EE" ]; then - export CMAKE_E2EE_ARGS="-DQuotient_ENABLE_E2EE=ON" - export QMAKE_E2EE_ARGS='"DEFINES += Quotient_E2EE_ENABLED USE_INTREE_LIBQOLM" "INCLUDEPATH += olm/include" "LIBS += -Lolm/build"' - export LIB_PATH_E2EE=olm/build - fi -# RPM spec-style: swallow a command with default parameters into an alias -# and add/override parameters further in the code if/as necessary -- shopt -s expand_aliases -- alias _cmake_config='cmake $CMAKE_ARGS . -Bbuild' -- alias _cmake_build='cmake --build build' - -install: -- pushd .. # Go out of libQuotient source tree -- | - if [ -n "$E2EE" ]; then - git clone https://gitlab.matrix.org/matrix-org/olm.git - pushd olm - _cmake_config - _cmake_build --target install - popd - fi - -- | - if [ -n "$UPDATE_API" ]; then - git clone https://github.com/matrix-org/matrix-doc.git - git clone --recursive https://github.com/KitsuneRal/gtad.git - pushd gtad - cmake $CMAKE_ARGS . - cmake --build . - popd - fi -- popd # back to libQuotient source tree - -before_script: -- _cmake_config $CMAKE_UPDATE_API_ARGS $CMAKE_E2EE_ARGS -- if [ -n "$UPDATE_API" ]; then _cmake_build --target update-api; fi - -script: -- _cmake_build --target install -# Build quotest with the installed libQuotient -- cmake $CMAKE_ARGS tests -Bbuild-test -- cmake --build build-test --target all -# Build with qmake -- qmake -Wall quotest.pro "CONFIG += debug" "CONFIG -= app_bundle" "QMAKE_CC = $CC" "QMAKE_CXX = $CXX" -- $QMAKE_E2EE_ARGS -- make all -# Run the qmake-compiled quotest under valgrind -- if [ "$TEST_USER" != "" ]; then LD_LIBRARY_PATH="$LIB_PATH_E2EE" $VALGRIND ./quotest "$TEST_USER" "$TEST_PWD" quotest-travis '#quotest:matrix.org' "Travis CI job $TRAVIS_JOB_NUMBER"; fi - -notifications: - webhooks: - urls: - - "https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MGtpdHN1bmUlM0FtYXRyaXgub3JnLyUyMVBDelV0eHRPalV5U3hTZWxvZiUzQW1hdHJpeC5vcmc" - on_success: change # always|never|change - on_failure: always - on_start: never diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/CMakeLists.txt new/libQuotient-0.6.4/CMakeLists.txt --- old/libQuotient-0.6.3/CMakeLists.txt 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/CMakeLists.txt 2021-01-15 16:53:46.000000000 +0100 @@ -4,7 +4,7 @@ endif() set(API_VERSION "0.6") -project(Quotient VERSION "${API_VERSION}.3" LANGUAGES CXX) +project(Quotient VERSION "${API_VERSION}.4" LANGUAGES CXX) option(${PROJECT_NAME}_INSTALL_TESTS "install quotest (former qmc-example) application" ON) # https://github.com/quotient-im/libQuotient/issues/369 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/CONTRIBUTING.md new/libQuotient-0.6.4/CONTRIBUTING.md --- old/libQuotient-0.6.3/CONTRIBUTING.md 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/CONTRIBUTING.md 2021-01-15 16:53:46.000000000 +0100 @@ -326,9 +326,9 @@ definition files. If you're unhappy with something in there and want to improve the code, you have to understand the way these files are produced and setup some additional tooling. The shortest possible procedure resembling -the below text can be found in .travis.yml (our CI configuration actually -regenerates those files upon every build). As described below, there is also -a handy build target for CMake. +the below text can be found in .github/workflows/ci.yml (our CI configuration +actually regenerates those files upon every build). As described below, there +is also a handy build target for CMake. #### Why generate the code at all? Because otherwise we have to do monkey business of writing boilerplate code, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/lib/connection.cpp new/libQuotient-0.6.4/lib/connection.cpp --- old/libQuotient-0.6.3/lib/connection.cpp 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/lib/connection.cpp 2021-01-15 16:53:46.000000000 +0100 @@ -133,10 +133,27 @@ != "json"; bool lazyLoading = false; + /** \brief Check the homeserver and resolve it if needed, before connecting + * + * A single entry for functions that need to check whether the homeserver + * is valid before running. May execute connectFn either synchronously + * or asynchronously. In case of errors, emits resolveError() if + * the homeserver URL is not valid and cannot be resolved from userId, or + * the homeserver doesn't support the requested login flow. + * + * \param userId fully-qualified MXID to resolve HS from + * \param connectFn a function to execute once the HS URL is good + * \param flow optionally, a login flow that should be supported for + * connectFn to work; `none`, if there's no login flow + * requirements + * \sa resolveServer, resolveError + */ + void checkAndConnect(const QString &userId, + const std::function<void ()> &connectFn, + const std::optional<LoginFlows::LoginFlow> &flow = none); template <typename... LoginArgTs> void loginToServer(LoginArgTs&&... loginArgs); - void assumeIdentity(const QString& userId, const QString& accessToken, - const QString& deviceId); + void completeSetup(const QString& mxId); void removeRoom(const QString& roomId); void consumeRoomData(SyncDataList&& roomDataList, bool fromCache); @@ -264,12 +281,19 @@ return; } - auto domain = maybeBaseUrl.host(); - qCDebug(MAIN) << "Finding the server" << domain; + qCDebug(MAIN) << "Finding the server" << maybeBaseUrl.host(); - d->data->setBaseUrl(maybeBaseUrl); // Just enough to check .well-known file + const auto& oldBaseUrl = d->data->baseUrl(); + d->data->setBaseUrl(maybeBaseUrl); // Temporarily set it for this one call d->resolverJob = callApi<GetWellknownJob>(); - connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl] { + // Connect to finished() to make sure baseUrl is restored in any case + connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl, oldBaseUrl] { + // Revert baseUrl so that setHomeserver() below triggers signals + // in case the base URL actually changed + d->data->setBaseUrl(oldBaseUrl); + if (d->resolverJob->error() == BaseJob::Abandoned) + return; + if (d->resolverJob->error() != BaseJob::NotFoundError) { if (!d->resolverJob->status().good()) { qCWarning(MAIN) @@ -297,6 +321,7 @@ << "for base URL"; setHomeserver(maybeBaseUrl); } + Q_ASSERT(d->loginFlowsJob != nullptr); // Ensured by setHomeserver() connect(d->loginFlowsJob, &BaseJob::success, this, &Connection::resolved); connect(d->loginFlowsJob, &BaseJob::failure, this, [this] { @@ -324,10 +349,10 @@ const QString& initialDeviceName, const QString& deviceId) { - checkAndConnect(userId, [=] { + d->checkAndConnect(userId, [=] { d->loginToServer(LoginFlows::Password.type, makeUserIdentifier(userId), password, /*token*/ "", deviceId, initialDeviceName); - }); + }, LoginFlows::Password); } SsoSession* Connection::prepareForSso(const QString& initialDeviceName, @@ -340,17 +365,20 @@ const QString& initialDeviceName, const QString& deviceId) { + Q_ASSERT(d->data->baseUrl().isValid() && d->loginFlows.contains(LoginFlows::Token)); d->loginToServer(LoginFlows::Token.type, none /*user is encoded in loginToken*/, "" /*password*/, loginToken, deviceId, initialDeviceName); } -void Connection::assumeIdentity(const QString& userId, - const QString& accessToken, +void Connection::assumeIdentity(const QString& mxId, const QString& accessToken, const QString& deviceId) { - checkAndConnect(userId, - [=] { d->assumeIdentity(userId, accessToken, deviceId); }); + d->checkAndConnect(mxId, [this, mxId, accessToken, deviceId] { + d->data->setToken(accessToken.toLatin1()); + d->data->setDeviceId(deviceId); + d->completeSetup(mxId); + }); } void Connection::reloadCapabilities() @@ -390,8 +418,9 @@ auto loginJob = q->callApi<LoginJob>(std::forward<LoginArgTs>(loginArgs)...); connect(loginJob, &BaseJob::success, q, [this, loginJob] { - assumeIdentity(loginJob->userId(), loginJob->accessToken(), - loginJob->deviceId()); + data->setToken(loginJob->accessToken().toLatin1()); + data->setDeviceId(loginJob->deviceId()); + completeSetup(loginJob->userId()); #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED @@ -404,21 +433,18 @@ }); } -void Connection::Private::assumeIdentity(const QString& userId, - const QString& accessToken, - const QString& deviceId) +void Connection::Private::completeSetup(const QString& mxId) { - data->setUserId(userId); + data->setUserId(mxId); q->user(); // Creates a User object for the local user - data->setToken(accessToken.toLatin1()); - data->setDeviceId(deviceId); - q->setObjectName(userId % '/' % deviceId); + q->setObjectName(data->userId() % '/' % data->deviceId()); qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString() - << "by user" << userId << "from device" << deviceId; + << "by user" << data->userId() + << "from device" << data->deviceId(); #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED - AccountSettings accountSettings(userId); + AccountSettings accountSettings(data->userId()); encryptionManager.reset( new EncryptionManager(accountSettings.encryptionAccountPickle())); if (accountSettings.encryptionAccountPickle().isEmpty()) { @@ -431,22 +457,37 @@ q->reloadCapabilities(); } -void Connection::checkAndConnect(const QString& userId, - std::function<void()> connectFn) +void Connection::Private::checkAndConnect(const QString& userId, + const std::function<void()>& connectFn, + const std::optional<LoginFlows::LoginFlow>& flow) { - if (d->data->baseUrl().isValid()) { + if (data->baseUrl().isValid() && (!flow || loginFlows.contains(*flow))) { connectFn(); return; } - // Not good to go, try to fix the homeserver URL. + // Not good to go, try to ascertain the homeserver URL and flows if (userId.startsWith('@') && userId.indexOf(':') != -1) { - connectSingleShot(this, &Connection::homeserverChanged, this, connectFn); - // NB: doResolveServer can emit resolveError, so this is a part of - // checkAndConnect function contract. - resolveServer(userId); + q->resolveServer(userId); + if (flow) + connectSingleShot(q, &Connection::loginFlowsChanged, q, + [this, flow, connectFn] { + if (loginFlows.contains(*flow)) + connectFn(); + else + emit q->loginError( + tr("The homeserver at %1 does not support" + " the login flow '%2'") + .arg(data->baseUrl().toDisplayString()), + flow->type); + }); + else + connectSingleShot(q, &Connection::homeserverChanged, q, connectFn); } else - emit resolveError(tr("%1 is an invalid homeserver URL") - .arg(d->data->baseUrl().toString())); + emit q->resolveError(tr("Please provide the fully-qualified user id" + " (such as @user:example.org) so that the" + " homeserver could be resolved; the current" + " homeserver URL(%1) is not good") + .arg(data->baseUrl().toDisplayString())); } void Connection::logout() @@ -1493,10 +1534,8 @@ { if (isJobRunning(d->resolverJob)) d->resolverJob->abandon(); - d->resolverJob = nullptr; if (isJobRunning(d->loginFlowsJob)) d->loginFlowsJob->abandon(); - d->loginFlowsJob = nullptr; d->loginFlows.clear(); if (homeserver() != url) { @@ -1506,7 +1545,7 @@ // Whenever a homeserver is updated, retrieve available login flows from it d->loginFlowsJob = callApi<GetLoginFlowsJob>(BackgroundRequest); - connect(d->loginFlowsJob, &BaseJob::finished, this, [this] { + connect(d->loginFlowsJob, &BaseJob::result, this, [this] { if (d->loginFlowsJob->status().good()) d->loginFlows = d->loginFlowsJob->flows(); else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/lib/connection.h new/libQuotient-0.6.4/lib/connection.h --- old/libQuotient-0.6.3/lib/connection.h 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/lib/connection.h 2021-01-15 16:53:46.000000000 +0100 @@ -504,13 +504,35 @@ /** Determine and set the homeserver from MXID */ void resolveServer(const QString& mxid); + /** \brief Log in using a username and password pair + * + * Before logging in, this method checks if the homeserver is valid and + * supports the password login flow. If the homeserver is invalid but + * a full user MXID is provided, this method calls resolveServer() using + * this MXID. + * + * \sa resolveServer, resolveError, loginError + */ void loginWithPassword(const QString& userId, const QString& password, const QString& initialDeviceName, const QString& deviceId = {}); + /** \brief Log in using a login token + * + * One usual case for this method is the final stage of logging in via SSO. + * Unlike loginWithPassword() and assumeIdentity(), this method cannot + * resolve the server from the user name because the full user MXID is + * encoded in the login token. Callers should ensure the homeserver + * sanity in advance. + */ void loginWithToken(const QByteArray& loginToken, const QString& initialDeviceName, const QString& deviceId = {}); - void assumeIdentity(const QString& userId, const QString& accessToken, + /** \brief Use an existing access token to connect to the homeserver + * + * Similar to loginWithPassword(), this method checks that the homeserver + * URL is valid and tries to resolve it from the MXID in case it is not. + */ + void assumeIdentity(const QString& mxId, const QString& accessToken, const QString& deviceId); /*! \deprecated Use loginWithPassword instead */ void connectToServer(const QString& userId, const QString& password, @@ -662,9 +684,9 @@ * This was a signal resulting from a successful resolveServer(). * Since Connection now provides setHomeserver(), the HS URL * may change even without resolveServer() invocation. Use - * homeserverChanged() instead of resolved(). You can also use - * connectToServer and connectWithToken without the HS URL set in - * advance (i.e. without calling resolveServer), as they now trigger + * loginFLowsChanged() instead of resolved(). You can also use + * loginWith*() and assumeIdentity() without the HS URL set in + * advance (i.e. without calling resolveServer), as they trigger * server name resolution from MXID if the server URL is not valid. */ void resolved(); @@ -860,19 +882,6 @@ class Private; QScopedPointer<Private> d; - /** - * A single entry for functions that need to check whether the - * homeserver is valid before running. May either execute connectFn - * synchronously or asynchronously (if tryResolve is true and - * a DNS lookup is initiated); in case of errors, emits resolveError - * if the homeserver URL is not valid and cannot be resolved from - * userId. - * - * @param userId - fully-qualified MXID to resolve HS from - * @param connectFn - a function to execute once the HS URL is good - */ - void checkAndConnect(const QString& userId, std::function<void()> connectFn); - static room_factory_t _roomFactory; static user_factory_t _userFactory; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/lib/events/event.cpp new/libQuotient-0.6.4/lib/events/event.cpp --- old/libQuotient-0.6.3/lib/events/event.cpp 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/lib/events/event.cpp 2021-01-15 16:53:46.000000000 +0100 @@ -61,11 +61,14 @@ QByteArray Event::originalJson() const { return QJsonDocument(_json).toJson(); } +// On const below: this is to catch accidental attempts to change event JSON +// NOLINTNEXTLINE(readability-const-return-type) const QJsonObject Event::contentJson() const { return fullJson()[ContentKeyL].toObject(); } +// NOLINTNEXTLINE(readability-const-return-type) const QJsonObject Event::unsignedJson() const { return fullJson()[UnsignedKeyL].toObject(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/lib/jobs/basejob.cpp new/libQuotient-0.6.4/lib/jobs/basejob.cpp --- old/libQuotient-0.6.3/lib/jobs/basejob.cpp 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/lib/jobs/basejob.cpp 2021-01-15 16:53:46.000000000 +0100 @@ -24,6 +24,7 @@ #include <QtCore/QTimer> #include <QtCore/QStringBuilder> #include <QtCore/QMetaEnum> +#include <QtCore/QPointer> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkRequest> @@ -76,15 +77,6 @@ return dbg << ": " << message; } -struct NetworkReplyDeleter : public QScopedPointerDeleteLater { - static inline void cleanup(QNetworkReply* reply) - { - if (reply && reply->isRunning()) - reply->abort(); - QScopedPointerDeleteLater::cleanup(reply); - } -}; - template <typename... Ts> constexpr auto make_array(Ts&&... items) { @@ -112,6 +104,16 @@ retryTimer.setSingleShot(true); } + ~Private() + { + if (reply) { + if (reply->isRunning()) { + reply->abort(); + } + delete reply; + } + } + void sendRequest(); /*! \brief Parse the response byte array into JSON * @@ -140,7 +142,10 @@ QByteArrayList expectedKeys; - QScopedPointer<QNetworkReply, NetworkReplyDeleter> reply; + // When the QNetworkAccessManager is destroyed it destroys all pending replies. + // Using QPointer allows us to know when that happend. + QPointer<QNetworkReply> reply; + Status status = Unprepared; QByteArray rawResponse; /// Contains a null document in case of non-JSON body (for a successful @@ -194,6 +199,7 @@ setObjectName(name); connect(&d->timer, &QTimer::timeout, this, &BaseJob::timeout); connect(&d->retryTimer, &QTimer::timeout, this, [this] { + qCDebug(d->logCat) << "Retrying" << this; d->connection->submit(this); }); } @@ -315,16 +321,16 @@ switch (verb) { case HttpVerb::Get: - reply.reset(connection->nam()->get(req)); + reply = connection->nam()->get(req); break; case HttpVerb::Post: - reply.reset(connection->nam()->post(req, requestData.source())); + reply = connection->nam()->post(req, requestData.source()); break; case HttpVerb::Put: - reply.reset(connection->nam()->put(req, requestData.source())); + reply = connection->nam()->put(req, requestData.source()); break; case HttpVerb::Delete: - reply.reset(connection->nam()->sendCustomRequest(req, "DELETE", requestData.source())); + reply = connection->nam()->sendCustomRequest(req, "DELETE", requestData.source()); break; } } @@ -337,7 +343,7 @@ void BaseJob::initiate(ConnectionData* connData, bool inBackground) { - if (connData && connData->baseUrl().isValid()) { + if (Q_LIKELY(connData && connData->baseUrl().isValid())) { d->inBackground = inBackground; d->connection = connData; doPrepare(); @@ -350,7 +356,7 @@ setStatus(FileError, "Request data not ready"); } Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this - if (status().code == Unprepared) { + if (Q_LIKELY(status().code == Unprepared)) { d->connection->submit(this); return; } @@ -369,8 +375,11 @@ void BaseJob::sendRequest() { - if (status().code == Abandoned) + if (status().code == Abandoned) { + qCDebug(d->logCat) << "Won't proceed with the abandoned request:" + << d->dumpRequest(); return; + } Q_ASSERT(d->connection && status().code == Pending); qCDebug(d->logCat).noquote() << "Making" << d->dumpRequest(); d->needsToken |= d->connection->needsToken(objectName()); @@ -616,6 +625,7 @@ qCWarning(d->logCat).nospace() << this << ": retry #" << d->retriesTaken << " in " << retryIn.count() << " s"; + setStatus(Pending, "Pending retry"); d->retryTimer.start(retryIn); emit retryScheduled(d->retriesTaken, milliseconds(retryIn).count()); return; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/lib/room.cpp new/libQuotient-0.6.4/lib/room.cpp --- old/libQuotient-0.6.3/lib/room.cpp 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/lib/room.cpp 2021-01-15 16:53:46.000000000 +0100 @@ -2492,31 +2492,20 @@ , [this, oldStateEvent] (const RoomCanonicalAliasEvent& cae) { // clang-format on setObjectName(cae.alias().isEmpty() ? d->id : cae.alias()); - QString previousCanonicalAlias = - oldStateEvent - ? static_cast<const RoomCanonicalAliasEvent*>(oldStateEvent) - ->alias() - : QString(); - - auto previousAltAliases = - oldStateEvent - ? static_cast<const RoomCanonicalAliasEvent*>(oldStateEvent) - ->altAliases() - : QStringList(); - - if (!previousCanonicalAlias.isEmpty()) { - previousAltAliases.push_back(previousCanonicalAlias); + const auto* oldCae = + static_cast<const RoomCanonicalAliasEvent*>(oldStateEvent); + QStringList previousAltAliases {}; + if (oldCae) { + previousAltAliases = oldCae->altAliases(); + if (!oldCae->alias().isEmpty()) + previousAltAliases.push_back(oldCae->alias()); } - const auto previousAliases = std::move(previousAltAliases); - auto newAliases = cae.altAliases(); - - if (!cae.alias().isEmpty()) { + if (!cae.alias().isEmpty()) newAliases.push_front(cae.alias()); - } - connection()->updateRoomAliases(id(), previousAliases, newAliases); + connection()->updateRoomAliases(id(), previousAltAliases, newAliases); return AliasesChange; // clang-format off } @@ -2552,7 +2541,9 @@ if (u == localUser() && evt.isDirect()) connection()->addToDirectChats(this, user(evt.senderId())); break; - default: + case MembershipType::Knock: + case MembershipType::Ban: + case MembershipType::Leave: if (!d->membersLeft.contains(u)) d->membersLeft.append(u); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libQuotient-0.6.3/tests/quotest.cpp new/libQuotient-0.6.4/tests/quotest.cpp --- old/libQuotient-0.6.3/tests/quotest.cpp 2020-12-24 19:27:43.000000000 +0100 +++ new/libQuotient-0.6.4/tests/quotest.cpp 2021-01-15 16:53:46.000000000 +0100 @@ -91,6 +91,7 @@ void doTest(const QByteArray& testName); private slots: + TEST_DECL(findRoomByAlias) TEST_DECL(loadMembers) TEST_DECL(sendMessage) TEST_DECL(sendReaction) @@ -186,7 +187,7 @@ connect(c, &Connection::connected, this, &TestManager::setupAndRun); connect(c, &Connection::resolveError, this, - [this](const QString& error) { + [](const QString& error) { clog << "Failed to resolve the server: " << error.toStdString() << endl; QCoreApplication::exit(-2); @@ -268,7 +269,7 @@ << endl; connect(r, &Room::aboutToAddNewMessages, r, [r](RoomEventsRange timeline) { clog << timeline.size() << " new event(s) in room " - << r->canonicalAlias().toStdString() << endl; + << r->objectName().toStdString() << endl; }); } @@ -306,26 +307,25 @@ }); } +TEST_IMPL(findRoomByAlias) +{ + auto* roomByAlias = connection()->roomByAlias(targetRoom->canonicalAlias(), + JoinState::Join); + FINISH_TEST(roomByAlias == targetRoom); +} + TEST_IMPL(loadMembers) { - // Trying to load members from another (larger) room - const auto& testRoomAlias = QStringLiteral("#test:matrix.org"); - auto* r = connection()->roomByAlias(testRoomAlias, JoinState::Join); - if (!r) { - clog << testRoomAlias.toStdString() - << " is not found in the test user's rooms" << endl; - FAIL_TEST(); - } // It's not exactly correct because an arbitrary server might not support // lazy loading; but in the absence of capabilities framework we assume // it does. - if (r->memberNames().size() >= r->joinedCount()) { + if (targetRoom->users().size() >= targetRoom->joinedCount()) { clog << "Lazy loading doesn't seem to be enabled" << endl; FAIL_TEST(); } - r->setDisplayed(); - connect(r, &Room::allMembersLoaded, this, [this, thisTest, r] { - FINISH_TEST(r->memberNames().size() >= r->joinedCount()); + targetRoom->setDisplayed(); + connect(targetRoom, &Room::allMembersLoaded, this, [this, thisTest] { + FINISH_TEST(targetRoom->users().size() >= targetRoom->joinedCount()); }); return false; } @@ -831,7 +831,7 @@ // .then(this, &TestManager::finalize); // Qt-style or // .then([this] { finalize(); }); // STL-style auto* job = room->leaveRoom(); - connect(job, &BaseJob::finished, this, [this, job,plainReport] { + connect(job, &BaseJob::result, this, [this, job,plainReport] { Q_ASSERT(job->status().good()); finalize(); // Still flying, as the exit() connection in finalize() is queued