1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2026-04-05 05:34:18 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Camila San
e0b32c19e4 Bump version to 2.6.0
Signed-off-by: Camila San <hello@camila.codes>
2019-09-27 17:01:24 +02:00
248 changed files with 56100 additions and 75137 deletions

View File

@@ -237,17 +237,13 @@ name: qt-5.12
steps: steps:
- name: build and test - name: build and test
image: nextcloudci/client-5.12:client-5.12-5 image: nextcloudci/client-5.12:client-5.12-2
commands: commands:
# Install QtKeyChain # Install QtKeyChain
- /bin/bash -c " - /bin/bash -c "
export CC=gcc-7 && export CC=gcc-7 &&
export CXX=g++-7 && export CXX=g++-7 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
cd /tmp && cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git && git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain && cd qtkeychain &&
@@ -261,11 +257,7 @@ steps:
- /bin/bash -c " - /bin/bash -c "
export CC=gcc-7 && export CC=gcc-7 &&
export CXX=g++-7 && export CXX=g++-7 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
mkdir build && mkdir build &&
cd build && cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ && cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
@@ -286,17 +278,13 @@ name: qt-5.12-clang
steps: steps:
- name: build and test - name: build and test
image: nextcloudci/client-5.12:client-5.12-5 image: nextcloudci/client-5.12:client-5.12-2
commands: commands:
# Install QtKeyChain # Install QtKeyChain
- /bin/bash -c " - /bin/bash -c "
export CC=clang-6.0 && export CC=clang-6.0 &&
export CXX=clang++-6.0 && export CXX=clang++-6.0 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
cd /tmp && cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git && git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain && cd qtkeychain &&
@@ -310,11 +298,7 @@ steps:
- /bin/bash -c " - /bin/bash -c "
export CC=clang-6.0 && export CC=clang-6.0 &&
export CXX=clang++-6.0 && export CXX=clang++-6.0 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
mkdir build && mkdir build &&
cd build && cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ && cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
@@ -335,10 +319,9 @@ name: AppImage
steps: steps:
- name: build - name: build
image: nextcloudci/client-5.12:client-5.12-5 image: nextcloudci/client-5.12:client-5.12-2
commands: commands:
- /bin/bash -c "./admin/linux/build-appimage.sh" - /bin/bash -c "./admin/linux/build-appimage.sh"
- /bin/bash -c "./admin/linux/upload-appimage.sh"
trigger: trigger:
branch: branch:
- master - master
@@ -361,9 +344,10 @@ steps:
from_secret: DEBIAN_SECRET_IV from_secret: DEBIAN_SECRET_IV
trigger: trigger:
branch: branch:
- stable-2.6 - master
event: event:
- tag - pull_request
- push
--- ---
kind: pipeline kind: pipeline
name: Documentation name: Documentation

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "src/3rdparty/qtmacgoodies"]
path = src/3rdparty/qtmacgoodies
url = https://github.com/camilasan/qtmacgoodies.git
[submodule "binary"] [submodule "binary"]
path = binary path = binary
url = git://github.com/owncloud/owncloud-client-binary.git url = git://github.com/owncloud/owncloud-client-binary.git

View File

@@ -198,7 +198,7 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[cs_CZ]=@APPLICATION_ICON_NAME@ Icon[cs_CZ]=@NAZEV_IKONY_APLIKACE@
Name[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop Name[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
Comment[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop Comment[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
GenericName[cs_CZ]=Synchronizace složek GenericName[cs_CZ]=Synchronizace složek

View File

@@ -1,204 +0,0 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
Icon[cy_GB]=@APPLICATION_ICON_NAME@
Name[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
Comment[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
GenericName[cy_GB]=Cydweddu Ffolder

View File

@@ -198,7 +198,7 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[de_DE]=@APPLICATION_ICON_NAME@ Icon[de]=@APPLICATION_ICON_NAME@
Name[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisierung Name[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
Comment[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisierung Comment[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
GenericName[de_DE]=Synchronisierungsordner GenericName[de]=Synchronisationsordner

View File

@@ -198,7 +198,4 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[el]=@APPLICATION_ICON_NAME@
Name[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
Comment[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
GenericName[el]=Συγχρονισμός φακέλου GenericName[el]=Συγχρονισμός φακέλου

View File

@@ -1,204 +0,0 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
Icon[es_AR]=@APPLICATION_ICON_NAME@
Name[es_AR]=@APPLICATION_NAME@ cliente de sincronización de escritorio
Comment[es_AR]=@APPLICATION_NAME@ cliente de sincronización de escritorio
GenericName[es_AR]=Sincronización de carpetas

View File

@@ -1,201 +0,0 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
Comment[fa]=@ APPLICATION_NAME @ مشتری هماهنگ سازی دسکتاپ

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[ro]=@APPLICATION_ICON_NAME@ Icon[ro]=@APPLICATION_ICON_NAME@
Name[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop Name[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
Comment[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop Comment[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
GenericName[ro]=Sincronizare director GenericName[ro]=Sincronizare dosare

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[sk_SK]=@APPLICATION_ICON_NAME@ Icon[sk_SK]=@APPLICATION_ICON_NAME@
Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
GenericName[sk_SK]=Synchronizácia priečinkov GenericName[sk_SK]=Synchnonizácia priečinkov

View File

@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[sl]=@APPLICATION_ICON_NAME@ Icon[sl]=@APPLICATION_ICON_NAME@
Name[sl]=@APPLICATION_NAME@ program za usklajevanje Name[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
Comment[sl]=@APPLICATION_NAME@ program za usklajevanje Comment[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
GenericName[sl]=Usklajevanje map GenericName[sl]=Usklajevanje map

View File

@@ -199,6 +199,4 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[sv]=@APPLICATION_ICON_NAME@ Icon[sv]=@APPLICATION_ICON_NAME@
Name[sv]=@APPLICATION_NAME@ desktopssynkklient
Comment[sv]=@APPLICATION_NAME@ desktopssynkroniseringsklient
GenericName[sv]=Mappsynkronisering GenericName[sv]=Mappsynkronisering

View File

@@ -1,204 +0,0 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
Icon[sw]=@APPLICATION_ICON_NAME@
Name[sw]=Teja ya @APPLICATION_NAME@ ya kufanana faili kwa seva na faili ziko hapa
Comment[sw]=Teja ya @APPLICATION_NAME@ ya kufanana faili kwa seva na faili ziko hapa
GenericName[sw]=Fanana Kabrasha

View File

@@ -198,7 +198,6 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[zh_TW]=@APPLICATION_ICON_NAME@
Name[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端 Name[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
Comment[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端 Comment[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
GenericName[zh_TW]=資料夾同步 GenericName[zh_TW]=資料夾同步

View File

@@ -203,16 +203,8 @@ if( WIN32 )
add_definitions( -D__USE_MINGW_ANSI_STDIO=1 ) add_definitions( -D__USE_MINGW_ANSI_STDIO=1 )
add_definitions( -DNOMINMAX ) add_definitions( -DNOMINMAX )
# Get APIs from from Vista onwards. # Get APIs from from Vista onwards.
add_definitions( -D_WIN32_WINNT=0x0601 ) add_definitions( -D_WIN32_WINNT=0x0600)
add_definitions( -DWINVER=0x0601 ) add_definitions( -DWINVER=0x0600)
if( MSVC )
# Use automatic overload for suitable CRT safe-functions
# See https://docs.microsoft.com/de-de/cpp/c-runtime-library/security-features-in-the-crt?view=vs-2019
add_definitions( -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 )
# Also: Disable compiler warnings because we don't use Windows CRT safe-functions explicitly and don't intend to
# as this is a pure cross-platform source the only alternative would be a ton of ifdefs with calls to the _s version
add_definitions( -D_CRT_SECURE_NO_WARNINGS )
endif( MSVC )
endif( WIN32 ) endif( WIN32 )
if (APPLE) if (APPLE)

View File

@@ -1,7 +1,7 @@
set( MIRALL_VERSION_MAJOR 2 ) set( MIRALL_VERSION_MAJOR 2 )
set( MIRALL_VERSION_MINOR 6 ) set( MIRALL_VERSION_MINOR 6 )
set( MIRALL_VERSION_PATCH 4 ) set( MIRALL_VERSION_PATCH 0 )
set( MIRALL_VERSION_YEAR 2020 ) set( MIRALL_VERSION_YEAR 2019 )
set( MIRALL_SOVERSION 0 ) set( MIRALL_SOVERSION 0 )
if ( NOT DEFINED MIRALL_VERSION_SUFFIX ) if ( NOT DEFINED MIRALL_VERSION_SUFFIX )

View File

@@ -14,7 +14,6 @@ RUN apt-get update -q && DEBIAN_FRONTEND=noninteractive apt-get install -q -y --
libsqlite3-dev \ libsqlite3-dev \
libssl-dev \ libssl-dev \
libcmocka-dev \ libcmocka-dev \
libcloudproviders-dev \
qt5-default \ qt5-default \
qttools5-dev-tools \ qttools5-dev-tools \
libqt5webkit5-dev \ libqt5webkit5-dev \

View File

@@ -6,7 +6,7 @@ mkdir /app
mkdir /build mkdir /build
#Set Qt-5.12 #Set Qt-5.12
export QT_BASE_DIR=/opt/qt5.12.5 export QT_BASE_DIR=/opt/qt512
export QTDIR=$QT_BASE_DIR export QTDIR=$QT_BASE_DIR
export PATH=$QT_BASE_DIR/bin:$PATH export PATH=$QT_BASE_DIR/bin:$PATH
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH
@@ -18,11 +18,11 @@ if [ $SUFFIX != "master" ]; then
SUFFIX="PR-$SUFFIX" SUFFIX="PR-$SUFFIX"
fi fi
#QtKeyChain master #QtKeyChain 0.9.1
cd /build cd /build
git clone https://github.com/frankosterfeld/qtkeychain.git git clone https://github.com/frankosterfeld/qtkeychain.git
cd qtkeychain cd qtkeychain
git checkout master git checkout v0.9.1
mkdir build mkdir build
cd build cd build
cmake -D CMAKE_INSTALL_PREFIX=/usr ../ cmake -D CMAKE_INSTALL_PREFIX=/usr ../
@@ -66,8 +66,7 @@ rm -rf ./usr/share/nemo-python/
mv ./etc/Nextcloud/sync-exclude.lst ./usr/bin/ mv ./etc/Nextcloud/sync-exclude.lst ./usr/bin/
rm -rf ./etc rm -rf ./etc
DESKTOP_FILE=/app/usr/share/applications/${LINUX_APPLICATION_ID}.desktop sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' usr/share/applications/nextcloud.desktop # Bug in desktop file?
sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' ${DESKTOP_FILE} # Bug in desktop file?
cp ./usr/share/icons/hicolor/512x512/apps/Nextcloud.png . # Workaround for linuxeployqt bug, FIXME cp ./usr/share/icons/hicolor/512x512/apps/Nextcloud.png . # Workaround for linuxeployqt bug, FIXME
@@ -88,12 +87,17 @@ chmod a+x linuxdeployqt*.AppImage
rm ./linuxdeployqt-continuous-x86_64.AppImage rm ./linuxdeployqt-continuous-x86_64.AppImage
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/app/usr/lib/ export LD_LIBRARY_PATH=/app/usr/lib/
./squashfs-root/AppRun ${DESKTOP_FILE} -bundle-non-qt-libs ./squashfs-root/AppRun /app/usr/share/applications/nextcloud.desktop -bundle-non-qt-libs
# Set origin # Set origin
./squashfs-root/usr/bin/patchelf --set-rpath '$ORIGIN/' /app/usr/lib/libnextcloudsync.so.0 ./squashfs-root/usr/bin/patchelf --set-rpath '$ORIGIN/' /app/usr/lib/libnextcloudsync.so.0
# Build AppImage # Build AppImage
./squashfs-root/AppRun ${DESKTOP_FILE} -appimage ./squashfs-root/AppRun /app/usr/share/applications/nextcloud.desktop -appimage
mv Nextcloud*.AppImage Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage mv Nextcloud*.AppImage Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage
curl --upload-file $(readlink -f ./Nextcloud*.AppImage) https://transfer.sh/Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage
echo
echo "Get the AppImage at the link above!"

View File

@@ -7,10 +7,9 @@ Build-Depends: cmake,
cdbs, cdbs,
dh-python, dh-python,
extra-cmake-modules (>= 5.16), extra-cmake-modules (>= 5.16),
kdelibs5-dev,
libkf5kio-dev, libkf5kio-dev,
libcmocka-dev, libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl, libhttp-dav-perl,
libinotify-dev [kfreebsd-any], libinotify-dev [kfreebsd-any],
libqt5svg5-dev, libqt5svg5-dev,

View File

@@ -1,48 +0,0 @@
nextcloud-client (2.3.3-1.0~oldstable1) oldstable; urgency=medium
* Debian build support for the forked client.
-- István Váradi <ivaradi@varadiistvan.hu> Mon, 6 Nov 2017 20:20:04 +0100
nextcloud-client (2.3.1-1.0) oldstable; urgency=medium
* New upstream version
-- István Váradi <ivaradi@varadiistvan.hu> Thu, 23 Mar 2017 19:07:36 +0100
nextcloud-client (2.3.0-1.0) oldstable; urgency=medium
* New upstream version
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 21 Mar 2017 19:34:13 +0100
nextcloud-client (2.2.4-1.4) oldstable; urgency=medium
* The locale-specific icon names are correct too
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 7 Feb 2017 19:55:40 +0100
nextcloud-client (2.2.4-1.3) oldstable; urgency=medium
* Caja syncstate plugin is built.
* The syncstate plugin has application-specific name
-- István Váradi <ivaradi@varadiistvan.hu> Fri, 27 Jan 2017 19:34:18 +0100
nextcloud-client (2.2.4-1.2) oldstable; urgency=medium
* Fixed appname in the Nemo syncstate extension.
-- István Váradi <ivaradi@varadiistvan.hu> Thu, 19 Jan 2017 16:46:50 +0100
nextcloud-client (2.2.4-1.1) oldstable; urgency=medium
* Added Nautilus and Nemo syncstate extensions.
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 17 Jan 2017 19:55:32 +0100
nextcloud-client (2.2.4-1.0) oldstable; urgency=medium
* Initial release.
-- István Váradi <ivaradi@varadiistvan.hu> Wed, 14 Dec 2016 20:07:46 +0100

View File

@@ -1,84 +0,0 @@
Source: nextcloud-client
Section: contrib/devel
Priority: optional
Maintainer: István Váradi <ivaradi@varadiistvan.hu>
Build-Depends: cmake,
debhelper,
cdbs,
dh-python,
extra-cmake-modules (>= 5.16),
kdelibs5-dev,
kio-dev,
libcmocka-dev,
libdbus-1-dev,
libhttp-dav-perl,
libinotify-dev [kfreebsd-any],
libqt5webkit5-dev,
libqt5svg5-dev,
libsqlite3-dev,
libssl-dev (>= 1.1.0),
zlib1g-dev,
optipng,
pkg-kde-tools,
python-sphinx | python3-sphinx,
python3-all,
qt5keychain-dev,
qtwebengine5-dev,
qtdeclarative5-dev,
qttools5-dev,
qttools5-dev-tools,
xvfb
Standards-Version: 3.9.8
Homepage: https://github.com/nextcloud/client_theming
#Vcs-Git: git://anonscm.debian.org/collab-maint/nextcloud-client.git
#Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/nextcloud-client.git
Package: nextcloud-client
Architecture: any
Depends: libnextcloudsync0 (=${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, nextcloud-client-l10n
Recommends: libgnome-keyring0
Description: Nextcloud desktop sync client
Use the desktop client to keep your files synchronized
between your Nextcloud server and your desktop. Select
one or more directories on your local machine and always
have access to your latest files wherever you are.
Package: libnextcloudsync0
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Nextcloud sync library
Used by the Nextcloud desktop client as the synchronization engine.
Package: libnextcloudsync-dev
Architecture: any
Section: contrib/libdevel
Depends: libnextcloudsync0 (=${binary:Version}), ${misc:Depends}
Description: Nextcloud sync library development files
The headers and development library for the Nextcloud sync library.
Package: nextcloud-client-l10n
Architecture: all
Depends: ${misc:Depends}
Description: Nextcloud client internatialization files
The translation files.
Package: nextcloud-client-nautilus
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nautilus, nautilus, ${misc:Depends}
Description: Nautilus plugin for Nextcloud
This package contains a Nautilus plugin to display
synchronization status icons for Nextcloud files.
Package: nextcloud-client-nemo
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nemo, nemo, ${misc:Depends}
Description: Nemo plugin for Nextcloud
This package contains a Nemo plugin to display
synchronization status icons for Nextcloud files.
Package: nextcloud-client-caja
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-caja, caja, ${misc:Depends}
Description: Caja plugin for Nextcloud
This package contains a Caja plugin to display
synchronization status icons for Nextcloud files.

View File

@@ -9,10 +9,7 @@ Build-Depends: cmake,
extra-cmake-modules (>= 5.16), extra-cmake-modules (>= 5.16),
kdelibs5-dev, kdelibs5-dev,
kio-dev, kio-dev,
libavcodec58,
libcmocka-dev, libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl, libhttp-dav-perl,
libinotify-dev [kfreebsd-any], libinotify-dev [kfreebsd-any],
libqt5webkit5-dev, libqt5webkit5-dev,

View File

@@ -1,4 +0,0 @@
usr/bin
usr/share/applications
usr/share/icons
debian/101-sync-inotify.conf etc/sysctl.d

View File

@@ -10,8 +10,6 @@ Build-Depends: cmake,
kdelibs5-dev, kdelibs5-dev,
libkf5kio-dev, libkf5kio-dev,
libcmocka-dev, libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl, libhttp-dav-perl,
libinotify-dev [kfreebsd-any], libinotify-dev [kfreebsd-any],
libqt5svg5-dev, libqt5svg5-dev,

View File

@@ -1,6 +1,4 @@
usr/bin usr/bin
usr/share/applications usr/share/applications
usr/share/cloud-providers/
usr/share/dbus-1/services/
usr/share/icons usr/share/icons
debian/101-sync-inotify.conf etc/sysctl.d debian/101-sync-inotify.conf etc/sysctl.d

View File

@@ -51,7 +51,7 @@ if ! wget http://ppa.launchpad.net/${repo}/ubuntu/pool/main/n/nextcloud-client/n
origsourceopt="-sa" origsourceopt="-sa"
fi fi
for distribution in xenial bionic disco eoan stable oldstable; do for distribution in xenial bionic disco eoan stable; do
rm -rf nextcloud-client_${basever} rm -rf nextcloud-client_${basever}
cp -a ${DRONE_WORKSPACE} nextcloud-client_${basever} cp -a ${DRONE_WORKSPACE} nextcloud-client_${basever}
@@ -98,46 +98,26 @@ if test "${pull_request}" = "master"; then
PPA=$PPA_BETA PPA=$PPA_BETA
OBS_PROJECT=$OBS_PROJECT_BETA OBS_PROJECT=$OBS_PROJECT_BETA
fi fi
OBS_SUBDIR="${OBS_PROJECT}/${OBS_PACKAGE}"
if test -f ~/.has_ppa_keys; then if test -f ~/.has_ppa_keys; then
for changes in nextcloud-client_*~+([a-z])1_source.changes; do for changes in nextcloud-client_*~+([a-z])1_source.changes; do
case "${changes}" in dput $PPA $changes > /dev/null
*oldstable1*)
;;
*)
dput $PPA $changes > /dev/null
;;
esac
done done
for distribution in stable oldstable; do mkdir osc
if test "${distribution}" = "oldstable"; then cd osc
pkgsuffix=".${distribution}" osc co ${OBS_PROJECT} ${OBS_PACKAGE}
pkgvertag="~${distribution}1" if test "$(ls ${OBS_SUBDIR})"; then
else osc delete ${OBS_SUBDIR}/*
pkgsuffix="" fi
pkgvertag="" cp ../nextcloud-client*.orig.tar.* ${OBS_SUBDIR}/
fi cp ../nextcloud-client_*[0-9.][0-9].dsc ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9].debian.tar* ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]_source.changes ${OBS_SUBDIR}/
osc add ${OBS_SUBDIR}/*
package="${OBS_PACKAGE}${pkgsuffix}" cd ${OBS_SUBDIR}
OBS_SUBDIR="${OBS_PROJECT}/${package}" osc commit -m "Travis update"
mkdir -p osc
pushd osc
osc co ${OBS_PROJECT} ${package}
if test "$(ls ${OBS_SUBDIR})"; then
osc delete ${OBS_SUBDIR}/*
fi
cp ../nextcloud-client*.orig.tar.* ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}.dsc ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}.debian.tar* ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}_source.changes ${OBS_SUBDIR}/
osc add ${OBS_SUBDIR}/*
cd ${OBS_SUBDIR}
osc commit -m "Travis update"
popd
done
fi fi
fi fi

View File

@@ -1,21 +0,0 @@
#! /bin/bash
set -xe
cd /build
# Upload AppImage
APPIMAGE=$(readlink -f ./Nextcloud*.AppImage)
BASENAME=$(basename ${APPIMAGE})
if curl --max-time 900 --upload-file ${APPIMAGE} https://transfer.sh/${BASENAME}
then
echo
echo "Get the AppImage at the link above!"
else
echo
echo "Upload failed, however this is an optional step."
fi
# Don't let the Drone build fail
exit 0

View File

@@ -49,7 +49,7 @@ fi
if [ ! -z "$identity" ]; then if [ ! -z "$identity" ]; then
echo "Will try to sign the installer" echo "Will try to sign the installer"
pushd $install_path pushd $install_path
productsign --timestamp --sign "$identity" "$installer_file" "$installer_file.new" productsign --sign "$identity" "$installer_file" "$installer_file.new"
mv "$installer_file".new "$installer_file" mv "$installer_file".new "$installer_file"
popd popd
else else

View File

@@ -32,12 +32,11 @@ FRAMEWORK_SEARCH_PATH=[
os.path.join(os.environ['HOME'], 'Library/Frameworks') os.path.join(os.environ['HOME'], 'Library/Frameworks')
] ]
LIBRARY_SEARCH_PATH=['/usr/local/lib', '/usr/local/Qt-5.12.5/lib', '.'] LIBRARY_SEARCH_PATH=['/usr/local/lib', '/usr/local/Qt-5.6.2/lib', '.']
QT_PLUGINS = [ QT_PLUGINS = [
'sqldrivers/libqsqlite.dylib', 'sqldrivers/libqsqlite.dylib',
'platforms/libqcocoa.dylib', 'platforms/libqcocoa.dylib',
'styles/libqmacstyle.dylib',
'imageformats/libqgif.dylib', 'imageformats/libqgif.dylib',
'imageformats/libqico.dylib', 'imageformats/libqico.dylib',
'imageformats/libqjpeg.dylib', 'imageformats/libqjpeg.dylib',
@@ -47,7 +46,7 @@ QT_PLUGINS = [
QT_PLUGINS_SEARCH_PATH=[ QT_PLUGINS_SEARCH_PATH=[
# os.path.join(os.environ['QTDIR'], 'plugins'), # os.path.join(os.environ['QTDIR'], 'plugins'),
# '/usr/local/Cellar/qt/5.2.1/plugins', # '/usr/local/Cellar/qt/5.2.1/plugins',
'/usr/local/Qt-5.12.5/plugins', '/usr/local/Qt-5.6.2/plugins',
] ]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

2
binary

Submodule binary updated: 09f12de312...3425fab2c6

View File

@@ -27,7 +27,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>@MIRALL_VERSION_STRING@</string> <string>@MIRALL_VERSION_STRING@</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>(C) 2014-2020 @APPLICATION_VENDOR@</string> <string>(C) 2014-2018 @APPLICATION_VENDOR@</string>
<key>NSSupportsAutomaticGraphicsSwitching</key> <key>NSSupportsAutomaticGraphicsSwitching</key>
<true/> <true/>
<key>SUShowReleaseNotes</key> <key>SUShowReleaseNotes</key>

View File

@@ -3,11 +3,7 @@
# For details see the accompanying COPYING* file. # For details see the accompanying COPYING* file.
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-long-long -Wno-gnu-zero-variadic-macro-arguments")
# Use this only for Clang
if (CMAKE_CXX_COMPILER MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-long-long -Wno-gnu-zero-variadic-macro-arguments")
endif()
# Fix sqlite compilation on macOS # Fix sqlite compilation on macOS
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers")

View File

@@ -87,7 +87,7 @@ To prevent automatic updates and disallow manual overrides:
1. Edit this Registry key: 1. Edit this Registry key:
``HKEY_LOCAL_MACHINE\Software\Policies\Nextcloud GmbH\Nextcloud`` ``HKEY_LOCAL_MACHINE\Software\Policies\Nextcloud\Nextcloud``
2. Add the key ``skipUpdateCheck`` (of type DWORD). 2. Add the key ``skipUpdateCheck`` (of type DWORD).

View File

@@ -27,7 +27,7 @@ download page.
System Requirements System Requirements
---------------------------------- ----------------------------------
- Windows 8.1+ - Windows 7+
- macOS 10.7+ (**64-bit only**) - macOS 10.7+ (**64-bit only**)
- CentOS 6 & 7 (64-bit only) - CentOS 6 & 7 (64-bit only)
- Debian 8.0 & 9.0 - Debian 8.0 & 9.0
@@ -36,7 +36,8 @@ System Requirements
- openSUSE Leap 42.2 & 42.3 - openSUSE Leap 42.2 & 42.3
.. note:: .. note::
For Linux distributions, we support, if technically feasible, the latest 2 versions per platform and the previous LTS. For Linux distributions, we support, if technically feasible, the latest 2 versions per platform and the previous `LTS`_.
>>>>>>> b2da03441... update supported linux platforms
Installation Wizard Installation Wizard
------------------- -------------------

View File

@@ -467,7 +467,6 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements;
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";

View File

@@ -35,11 +35,11 @@ public:
QString contextMenuTitle() const QString contextMenuTitle() const
{ {
return _strings.value("CONTEXT_MENU_TITLE", "Nextcloud"); return _strings.value("CONTEXT_MENU_TITLE", "ownCloud");
} }
QString shareActionTitle() const QString shareActionTitle() const
{ {
return _strings.value("SHARE_MENU_TITLE", "Share"); return _strings.value("SHARE_MENU_TITLE", "Share...");
} }
QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; } QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; }

View File

@@ -1,4 +1,4 @@
[D-BUS Service] [D-BUS Service]
Name=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@ Name=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@
Exec=@APPLICATION_EXECUTABLE@ --background Exec=@APPLICATION_EXECUTABLE@

View File

@@ -89,7 +89,7 @@ IFACEMETHODIMP OCContextMenu::Initialize(
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal)); HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
if (hDrop) { if (hDrop) {
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
for (UINT i = 0; i < nFiles; ++i) { for (int i = 0; i < nFiles; ++i) {
// Get the path of the file. // Get the path of the file.
wchar_t buffer[MAX_PATH]; wchar_t buffer[MAX_PATH];
@@ -182,38 +182,20 @@ IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{ {
std::wstring command; std::wstring command;
CMINVOKECOMMANDINFOEX *piciEx = nullptr;
if (pici->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
piciEx = (CMINVOKECOMMANDINFOEX*)pici;
// For the Unicode case, if the high-order word is not zero, the // For the Unicode case, if the high-order word is not zero, the
// command's verb string is in lpcmi->lpVerbW. // command's verb string is in lpcmi->lpVerbW.
if (piciEx if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
&& (piciEx->fMask & CMIC_MASK_UNICODE) {
&& HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW)) { command = ((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW;
} else {
command = piciEx->lpVerbW;
// Verify that we handle the verb
bool handled = false;
for (auto &item : m_info.menuItems) {
if (item.command == command) {
handled = true;
break;
}
}
if (!handled)
return E_FAIL;
} else if (IS_INTRESOURCE(pici->lpVerb)) {
// If the command cannot be identified through the verb string, then // If the command cannot be identified through the verb string, then
// check the identifier offset. // check the identifier offset.
auto offset = LOWORD(pici->lpVerb); auto offset = LOWORD(pici->lpVerb);
if (offset >= m_info.menuItems.size()) if (offset >= m_info.menuItems.size())
return E_FAIL; return E_FAIL;
command = m_info.menuItems[offset].command; command = m_info.menuItems[offset].command;
} else {
return E_FAIL;
} }
OCClientInterface::SendRequest(command.data(), m_selectedFiles); OCClientInterface::SendRequest(command.data(), m_selectedFiles);

View File

@@ -5,8 +5,6 @@
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
// Note: Here was a #define for windows target version #define WINVER 0x0501
// e.g. WINVER / _WIN32_WINNT, see https://devblogs.microsoft.com/oldnewthing/20070411-00/?p=27283 #define _WIN32_WINNT 0x0501
// Unnecessary because we define both in desktop/CMakeLists.txt
#include <SDKDDKVer.h> #include <SDKDDKVer.h>

View File

@@ -13,10 +13,8 @@
*/ */
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define WINVER 0x0501
// Note: Here was a #define for windows target version #define _WIN32_WINNT 0x0501
// e.g. WINVER / _WIN32_WINNT, see https://devblogs.microsoft.com/oldnewthing/20070411-00/?p=27283
// Unnecessary because we define both in desktop/CMakeLists.txt
#include "CommunicationSocket.h" #include "CommunicationSocket.h"
#include "RegistryUtil.h" #include "RegistryUtil.h"

View File

@@ -1,9 +1,7 @@
#pragma once #pragma once
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define WINVER 0x0501
// Note: Here was a #define for windows target version #define _WIN32_WINNT 0x0501
// e.g. WINVER / _WIN32_WINNT, see https://devblogs.microsoft.com/oldnewthing/20070411-00/?p=27283
// Unnecessary because we define both in desktop/CMakeLists.txt
#include <windows.h> #include <windows.h>

1
src/3rdparty/qtmacgoodies vendored Submodule

View File

@@ -29,17 +29,12 @@ if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2")
endif() endif()
# Calling Qt's qCWarning(category, ...) with no params for "..." is a GNU
# extension (C++11 §16.3/4 forbids them). Silence clang's warnings.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
endif() endif()
if(WIN32) if(WIN32)
# Enable DEP & ASLR # Enable DEP & ASLR
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /nxcompat /dynamicbase") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /nxcompat /dynamicbase") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")

View File

@@ -11,50 +11,43 @@
// For overloading macros by argument count // For overloading macros by argument count
// See stackoverflow.com/questions/16683146/can-macros-be-overloaded-by-number-of-arguments // See stackoverflow.com/questions/16683146/can-macros-be-overloaded-by-number-of-arguments
// Bugfix 08/09/2019: Broken arg expansion led to always collapsing to 1 arg (XXXX_1 overload result) #define OC_ASSERT_CAT(A, B) A##B
// See also: https://stackoverflow.com/questions/9183993/msvc-variadic-macro-expansion #define OC_ASSERT_SELECT(NAME, NUM) OC_ASSERT_CAT(NAME##_, NUM)
#define OC_ASSERT_GLUE(x, y) x y
#define OC_ASSERT_GET_COUNT(_1, _2, _3, COUNT, ...) COUNT #define OC_ASSERT_GET_COUNT(_1, _2, _3, COUNT, ...) COUNT
#define OC_ASSERT_EXPAND_ARGS(args) OC_ASSERT_GET_COUNT args #define OC_ASSERT_VA_SIZE(...) OC_ASSERT_GET_COUNT(__VA_ARGS__, 3, 2, 1, 0)
#define OC_ASSERT_VA_SIZE(...) OC_ASSERT_EXPAND_ARGS((__VA_ARGS__, 3, 2, 1, 0))
#define OC_ASSERT_SELECT2(NAME, COUNT) NAME##COUNT #define OC_ASSERT_OVERLOAD(NAME, ...) OC_ASSERT_SELECT(NAME, OC_ASSERT_VA_SIZE(__VA_ARGS__)) \
#define OC_ASSERT_SELECT1(NAME, COUNT) OC_ASSERT_SELECT2(NAME, COUNT) (__VA_ARGS__)
#define OC_ASSERT_SELECT(NAME, COUNT) OC_ASSERT_SELECT1(NAME, COUNT)
#define OC_ASSERT_OVERLOAD(NAME, ...) OC_ASSERT_GLUE(OC_ASSERT_SELECT(NAME, OC_ASSERT_VA_SIZE(__VA_ARGS__)), \
(__VA_ARGS__))
// Default assert: If the condition is false in debug builds, terminate. // Default assert: If the condition is false in debug builds, terminate.
// //
// Prints a message on failure, even in release builds. // Prints a message on failure, even in release builds.
#define ASSERT1(cond) \ #define ASSERT(...) OC_ASSERT_OVERLOAD(ASSERT, __VA_ARGS__)
#define ASSERT_1(cond) \
if (!(cond)) { \ if (!(cond)) { \
OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \ OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \
} else { \ } else { \
} }
#define ASSERT2(cond, message) \ #define ASSERT_2(cond, message) \
if (!(cond)) { \ if (!(cond)) { \
OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \ OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \
} else { \ } else { \
} }
#define ASSERT(...) OC_ASSERT_OVERLOAD(ASSERT, __VA_ARGS__)
// Enforce condition to be true, even in release builds. // Enforce condition to be true, even in release builds.
// //
// Prints 'message' and aborts execution if 'cond' is false. // Prints 'message' and aborts execution if 'cond' is false.
#define ENFORCE1(cond) \ #define ENFORCE(...) OC_ASSERT_OVERLOAD(ENFORCE, __VA_ARGS__)
#define ENFORCE_1(cond) \
if (!(cond)) { \ if (!(cond)) { \
qFatal("ENFORCE: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \ qFatal("ENFORCE: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \
} else { \ } else { \
} }
#define ENFORCE2(cond, message) \ #define ENFORCE_2(cond, message) \
if (!(cond)) { \ if (!(cond)) { \
qFatal("ENFORCE: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \ qFatal("ENFORCE: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \
} else { \ } else { \
} }
#define ENFORCE(...) OC_ASSERT_OVERLOAD(ENFORCE, __VA_ARGS__)
// An assert that is only present in debug builds: typically used for // An assert that is only present in debug builds: typically used for
// asserts that are too expensive for release mode. // asserts that are too expensive for release mode.

View File

@@ -207,10 +207,7 @@ static inline uint64_t c_jhash64(const uint8_t *k, uint64_t length, uint64_t int
/* handle the last 23 bytes */ /* handle the last 23 bytes */
c += length; c += length;
switch(len) { switch(len) {
// pragma only for GCC (and clang continues to pretend to be it by defining __GNUC__)
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif
case 23: c+=((uint64_t)k[22]<<56); case 23: c+=((uint64_t)k[22]<<56);
case 22: c+=((uint64_t)k[21]<<48); case 22: c+=((uint64_t)k[21]<<48);
case 21: c+=((uint64_t)k[20]<<40); case 21: c+=((uint64_t)k[20]<<40);

View File

@@ -281,8 +281,8 @@ int SqlQuery::prepare(const QByteArray &sql, bool allow_failure)
*/ */
static bool startsWithInsensitive(const QByteArray &a, const char *b) static bool startsWithInsensitive(const QByteArray &a, const char *b)
{ {
size_t len = strlen(b); int len = strlen(b);
return a.size() >= len && qstrnicmp(a.constData(), b, Utility::convertSizeToUint(len)) == 0; return a.size() >= len && qstrnicmp(a.constData(), b, len) == 0;
} }
bool SqlQuery::isSelect() bool SqlQuery::isSelect()

View File

@@ -103,7 +103,6 @@ public:
: _chunk(0) : _chunk(0)
, _transferid(0) , _transferid(0)
, _size(0) , _size(0)
, _modtime(0)
, _errorCount(0) , _errorCount(0)
, _valid(false) , _valid(false)
{ {

View File

@@ -396,26 +396,6 @@ void Utility::crash()
*a = 1; *a = 1;
} }
// Use this functions to retrieve uint/int (often required by Qt and WIN32) from size_t
// without compiler warnings about possible truncation
uint Utility::convertSizeToUint(size_t &convertVar)
{
if( convertVar > UINT_MAX ) {
//throw std::bad_cast();
convertVar = UINT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
return static_cast<uint>(convertVar);
}
uint Utility::convertSizeToInt(size_t &convertVar)
{
if( convertVar > INT_MAX ) {
//throw std::bad_cast();
convertVar = INT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
return static_cast<int>(convertVar);
}
// read the output of the owncloud --version command from the owncloud // read the output of the owncloud --version command from the owncloud
// version that is on disk. This works for most versions of the client, // version that is on disk. This works for most versions of the client,
// because clients that do not yet know the --version flag return the // because clients that do not yet know the --version flag return the

View File

@@ -55,12 +55,6 @@ namespace Utility {
OCSYNC_EXPORT QByteArray userAgentString(); OCSYNC_EXPORT QByteArray userAgentString();
OCSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName); OCSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName);
OCSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString &guiName, bool launch); OCSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString &guiName, bool launch);
OCSYNC_EXPORT uint convertSizeToUint(size_t &convertVar);
OCSYNC_EXPORT uint convertSizeToInt(size_t &convertVar);
#ifdef Q_OS_WIN
OCSYNC_EXPORT DWORD convertSizeToDWORD(size_t &convertVar);
#endif
/** /**
* Return the amount of free space available. * Return the amount of free space available.

View File

@@ -90,13 +90,32 @@ void setLaunchOnStartup_private(const QString &appName, const QString &guiName,
// TODO: Right now only detection on toggle/startup, not when windows theme is switched while nextcloud is running // TODO: Right now only detection on toggle/startup, not when windows theme is switched while nextcloud is running
static inline bool hasDarkSystray_private() static inline bool hasDarkSystray_private()
{ {
if(Utility::registryGetKeyValue( HKEY_CURRENT_USER, bool hasDarkSystray = true;
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", // Open registry key first, continue only on success (may be legitimately absent in earlier windows versions)
"SystemUsesLightTheme" ) == 1) { HKEY hKey;
return false; LONG lRes = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey);
}
else { // classical windows function - preserve buff size for DWORD, call ExW version, store regkey value in nResult
return true; if (lRes == ERROR_SUCCESS) {
DWORD dwBufferSize(sizeof(DWORD));
DWORD nResult(0);
// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexw
LONG nError = ::RegQueryValueExW(hKey,
L"SystemUsesLightTheme",
NULL,
NULL,
reinterpret_cast<LPBYTE>(&nResult),
&dwBufferSize);
// if RegQuery returned no error and light theme was found, change systray return value
if (nError == ERROR_SUCCESS && nResult == 1)
hasDarkSystray = false;
return hasDarkSystray;
} else {
// fallback to true if regkey could not be determined
return hasDarkSystray;
} }
} }
@@ -264,13 +283,4 @@ bool Utility::registryWalkSubKeys(HKEY hRootKey, const QString &subKey, const st
return retCode != ERROR_NO_MORE_ITEMS; return retCode != ERROR_NO_MORE_ITEMS;
} }
DWORD Utility::convertSizeToDWORD(size_t &convertVar)
{
if( convertVar > UINT_MAX ) {
//throw std::bad_cast();
convertVar = UINT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
return static_cast<DWORD>(convertVar);
}
} // namespace OCC } // namespace OCC

View File

@@ -73,7 +73,7 @@ static void csync_exclude_expand_escapes(QByteArray &input)
line[o++] = line[i]; line[o++] = line[i];
} }
} }
input.resize(OCC::Utility::convertSizeToUint(o)); input.resize(o);
} }
// See http://support.microsoft.com/kb/74496 and // See http://support.microsoft.com/kb/74496 and
@@ -288,11 +288,7 @@ void ExcludedFiles::addManualExclude(const QByteArray &expr)
void ExcludedFiles::addManualExclude(const QByteArray &expr, const QByteArray &basePath) void ExcludedFiles::addManualExclude(const QByteArray &expr, const QByteArray &basePath)
{ {
#if defined(Q_OS_WIN)
Q_ASSERT(basePath.size() >= 2 && basePath.at(1) == ':');
#else
Q_ASSERT(basePath.startsWith('/')); Q_ASSERT(basePath.startsWith('/'));
#endif
Q_ASSERT(basePath.endsWith('/')); Q_ASSERT(basePath.endsWith('/'));
auto key = basePath; auto key = basePath;
@@ -326,11 +322,7 @@ bool ExcludedFiles::loadExcludeFile(const QByteArray & basePath, const QString &
csync_exclude_expand_escapes(line); csync_exclude_expand_escapes(line);
_allExcludes[basePath].append(line); _allExcludes[basePath].append(line);
} }
prepare(basePath);
// nothing to prepare if the user decided to not exclude anything
if(_allExcludes.size())
prepare(basePath);
return true; return true;
} }
@@ -346,8 +338,8 @@ bool ExcludedFiles::reloadExcludeFiles()
_fullRegexDir.clear(); _fullRegexDir.clear();
bool success = true; bool success = true;
for (const auto& basePath : _excludeFiles.keys()) { for (auto basePath : _excludeFiles.keys()) {
for (const auto& file : _excludeFiles.value(basePath)) { for (auto file : _excludeFiles.value(basePath)) {
success = loadExcludeFile(basePath, file); success = loadExcludeFile(basePath, file);
} }
} }

View File

@@ -724,8 +724,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
if (ctx->current == LOCAL_REPLICA) { if (ctx->current == LOCAL_REPLICA) {
ASSERT(dirent->path.startsWith(ctx->local.uri)); // path is relative to uri ASSERT(dirent->path.startsWith(ctx->local.uri)); // path is relative to uri
// "len + 1" to include the slash in-between. // "len + 1" to include the slash in-between.
size_t uriLength = strlen(ctx->local.uri); dirent->path = dirent->path.mid(strlen(ctx->local.uri) + 1);
dirent->path = dirent->path.mid(OCC::Utility::convertSizeToInt(uriLength) + 1);
} }
previous_fs = ctx->current_fs; previous_fs = ctx->current_fs;

View File

@@ -38,7 +38,6 @@
#include "c_alloc.h" #include "c_alloc.h"
#include "c_string.h" #include "c_string.h"
#include "common/filesystembase.h" #include "common/filesystembase.h"
#include "common/utility.h"
/* Convert a locale String to UTF8 */ /* Convert a locale String to UTF8 */
QByteArray c_utf8_from_locale(const mbchar_t *wstr) QByteArray c_utf8_from_locale(const mbchar_t *wstr)
@@ -53,10 +52,10 @@ QByteArray c_utf8_from_locale(const mbchar_t *wstr)
size_t len; size_t len;
len = wcslen(wstr); len = wcslen(wstr);
/* Call once to get the required size. */ /* Call once to get the required size. */
size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, OCC::Utility::convertSizeToInt(len), NULL, 0, NULL, NULL); size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL);
if (size_needed > 0) { if (size_needed > 0) {
dst.resize(size_needed); dst.resize(size_needed);
WideCharToMultiByte(CP_UTF8, 0, wstr, OCC::Utility::convertSizeToInt(len), dst.data(), size_needed, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, wstr, len, dst.data(), size_needed, NULL, NULL);
} }
return dst; return dst;
#else #else
@@ -96,7 +95,7 @@ mbchar_t* c_utf8_string_to_locale(const char *str)
int size_needed; int size_needed;
len = strlen(str); len = strlen(str);
size_needed = MultiByteToWideChar(CP_UTF8, 0, str, OCC::Utility::convertSizeToInt(len), NULL, 0); size_needed = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0);
if (size_needed > 0) { if (size_needed > 0) {
int size_char = (size_needed + 1) * sizeof(mbchar_t); int size_char = (size_needed + 1) * sizeof(mbchar_t);
dst = (mbchar_t*)c_malloc(size_char); dst = (mbchar_t*)c_malloc(size_char);
@@ -115,8 +114,7 @@ mbchar_t* c_utf8_string_to_locale(const char *str)
return NULL; return NULL;
} else { } else {
#ifdef _WIN32 #ifdef _WIN32
size_t strLength = strlen(str); QByteArray unc_str = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(str, strlen(str)));
QByteArray unc_str = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(str, OCC::Utility::convertSizeToInt(strLength)));
mbchar_t *dst = c_utf8_string_to_locale(unc_str); mbchar_t *dst = c_utf8_string_to_locale(unc_str);
return dst; return dst;
#else #else

View File

@@ -57,7 +57,7 @@ csync_vio_handle_t *csync_vio_local_opendir(const char *name) {
handle = (dhandle_t*)c_malloc(sizeof(dhandle_t)); handle = (dhandle_t*)c_malloc(sizeof(dhandle_t));
// the file wildcard has to be attached // the file wildcard has to be attached
size_t len_name = strlen(name); int len_name = strlen(name);
if( len_name ) { if( len_name ) {
char *h = NULL; char *h = NULL;

View File

@@ -35,11 +35,12 @@ set(client_UI_SRCS
addcertificatedialog.ui addcertificatedialog.ui
proxyauthdialog.ui proxyauthdialog.ui
mnemonicdialog.ui mnemonicdialog.ui
wizard/flow2authwidget.ui
wizard/owncloudadvancedsetuppage.ui wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui wizard/owncloudconnectionmethoddialog.ui
wizard/owncloudhttpcredspage.ui wizard/owncloudhttpcredspage.ui
wizard/owncloudoauthcredspage.ui wizard/owncloudoauthcredspage.ui
wizard/flow2authcredspage.ui
wizard/flow2authwidget.ui
wizard/owncloudsetupnocredspage.ui wizard/owncloudsetupnocredspage.ui
wizard/owncloudwizardresultpage.ui wizard/owncloudwizardresultpage.ui
wizard/webview.ui wizard/webview.ui
@@ -90,7 +91,7 @@ set(client_SRCS
syncrunfilelog.cpp syncrunfilelog.cpp
systray.cpp systray.cpp
thumbnailjob.cpp thumbnailjob.cpp
userinfo.cpp quotainfo.cpp
accountstate.cpp accountstate.cpp
addcertificatedialog.cpp addcertificatedialog.cpp
authenticationdialog.cpp authenticationdialog.cpp
@@ -102,14 +103,11 @@ set(client_SRCS
servernotificationhandler.cpp servernotificationhandler.cpp
guiutility.cpp guiutility.cpp
elidedlabel.cpp elidedlabel.cpp
headerbanner.cpp
iconjob.cpp iconjob.cpp
remotewipe.cpp
creds/credentialsfactory.cpp creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp creds/httpcredentialsgui.cpp
creds/oauth.cpp creds/oauth.cpp
creds/flow2auth.cpp creds/flow2auth.cpp
creds/keychainchunk.cpp
creds/webflowcredentials.cpp creds/webflowcredentials.cpp
creds/webflowcredentialsdialog.cpp creds/webflowcredentialsdialog.cpp
wizard/postfixlineedit.cpp wizard/postfixlineedit.cpp
@@ -146,6 +144,8 @@ set(updater_SRCS
IF( APPLE ) IF( APPLE )
list(APPEND client_SRCS cocoainitializer_mac.mm) list(APPEND client_SRCS cocoainitializer_mac.mm)
list(APPEND client_SRCS settingsdialogmac.cpp)
list(REMOVE_ITEM client_SRCS settingsdialog.cpp)
list(APPEND client_SRCS socketapisocket_mac.mm) list(APPEND client_SRCS socketapisocket_mac.mm)
list(APPEND client_SRCS systray.mm) list(APPEND client_SRCS systray.mm)
@@ -175,6 +175,14 @@ set(3rdparty_SRC
../3rdparty/kmessagewidget/kmessagewidget.cpp ../3rdparty/kmessagewidget/kmessagewidget.cpp
) )
if (APPLE)
list(APPEND 3rdparty_SRC
../3rdparty/qtmacgoodies/src/macpreferenceswindow.mm
../3rdparty/qtmacgoodies/src/macstandardicon.mm
../3rdparty/qtmacgoodies/src/macwindow.mm
)
endif()
if(NOT WIN32) if(NOT WIN32)
list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp) list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
else() else()
@@ -330,6 +338,7 @@ ENDIF()
target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator ${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile ${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication ${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget ${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}

View File

@@ -368,7 +368,6 @@ void AccountManager::shutdown()
_accounts.clear(); _accounts.clear();
foreach (const auto &acc, accountsCopy) { foreach (const auto &acc, accountsCopy) {
emit accountRemoved(acc.data()); emit accountRemoved(acc.data());
emit removeAccountFolders(acc.data());
} }
} }

View File

@@ -70,6 +70,7 @@ public:
*/ */
void deleteAccount(AccountState *account); void deleteAccount(AccountState *account);
/** /**
* Creates an account and sets up some basic handlers. * Creates an account and sets up some basic handlers.
* Does *not* add the account to the account manager just yet. * Does *not* add the account to the account manager just yet.
@@ -103,7 +104,6 @@ public slots:
Q_SIGNALS: Q_SIGNALS:
void accountAdded(AccountState *account); void accountAdded(AccountState *account);
void accountRemoved(AccountState *account); void accountRemoved(AccountState *account);
void removeAccountFolders(AccountState *account);
private: private:
AccountManager() {} AccountManager() {}

View File

@@ -26,7 +26,7 @@
#include "configfile.h" #include "configfile.h"
#include "account.h" #include "account.h"
#include "accountstate.h" #include "accountstate.h"
#include "userinfo.h" #include "quotainfo.h"
#include "accountmanager.h" #include "accountmanager.h"
#include "owncloudsetupwizard.h" #include "owncloudsetupwizard.h"
#include "creds/abstractcredentials.h" #include "creds/abstractcredentials.h"
@@ -57,6 +57,10 @@
#include "account.h" #include "account.h"
#ifdef Q_OS_MAC
#include "settingsdialogmac.h"
#endif
namespace OCC { namespace OCC {
Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg) Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg)
@@ -109,13 +113,13 @@ protected:
AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, _ui(new Ui::AccountSettings) , ui(new Ui::AccountSettings)
, _wasDisabledBefore(false) , _wasDisabledBefore(false)
, _accountState(accountState) , _accountState(accountState)
, _userInfo(accountState, false, true) , _quotaInfo(accountState)
, _menuShown(false) , _menuShown(false)
{ {
_ui->setupUi(this); ui->setupUi(this);
_model = new FolderStatusModel; _model = new FolderStatusModel;
_model->setAccountState(_accountState); _model->setAccountState(_accountState);
@@ -123,40 +127,35 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
FolderStatusDelegate *delegate = new FolderStatusDelegate; FolderStatusDelegate *delegate = new FolderStatusDelegate;
delegate->setParent(this); delegate->setParent(this);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching) ui->_folderList->header()->hide();
connect(this, &AccountSettings::styleChanged, delegate, &FolderStatusDelegate::slotStyleChanged); ui->_folderList->setItemDelegate(delegate);
ui->_folderList->setModel(_model);
_ui->_folderList->header()->hide();
_ui->_folderList->setItemDelegate(delegate);
_ui->_folderList->setModel(_model);
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
_ui->_folderList->setMinimumWidth(400); ui->_folderList->setMinimumWidth(400);
#else #else
_ui->_folderList->setMinimumWidth(300); ui->_folderList->setMinimumWidth(300);
#endif #endif
new ToolTipUpdater(_ui->_folderList); new ToolTipUpdater(ui->_folderList);
auto mouseCursorChanger = new MouseCursorChanger(this); auto mouseCursorChanger = new MouseCursorChanger(this);
mouseCursorChanger->folderList = _ui->_folderList; mouseCursorChanger->folderList = ui->_folderList;
mouseCursorChanger->model = _model; mouseCursorChanger->model = _model;
_ui->_folderList->setMouseTracking(true); ui->_folderList->setMouseTracking(true);
_ui->_folderList->setAttribute(Qt::WA_Hover, true); ui->_folderList->setAttribute(Qt::WA_Hover, true);
_ui->_folderList->installEventFilter(mouseCursorChanger); ui->_folderList->installEventFilter(mouseCursorChanger);
createAccountToolbox(); createAccountToolbox();
connect(AccountManager::instance(), &AccountManager::accountAdded, connect(AccountManager::instance(), &AccountManager::accountAdded,
this, &AccountSettings::slotAccountAdded); this, &AccountSettings::slotAccountAdded);
connect(this, &AccountSettings::removeAccountFolders, connect(ui->_folderList, &QWidget::customContextMenuRequested,
AccountManager::instance(), &AccountManager::removeAccountFolders);
connect(_ui->_folderList, &QWidget::customContextMenuRequested,
this, &AccountSettings::slotCustomContextMenuRequested); this, &AccountSettings::slotCustomContextMenuRequested);
connect(_ui->_folderList, &QAbstractItemView::clicked, connect(ui->_folderList, &QAbstractItemView::clicked,
this, &AccountSettings::slotFolderListClicked); this, &AccountSettings::slotFolderListClicked);
connect(_ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus); connect(ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(_ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus); connect(ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(_ui->selectiveSyncNotification, &QLabel::linkActivated, connect(ui->selectiveSyncNotification, &QLabel::linkActivated,
this, &AccountSettings::slotLinkActivated); this, &AccountSettings::slotLinkActivated);
connect(_model, &FolderStatusModel::suggestExpand, _ui->_folderList, &QTreeView::expand); connect(_model, &FolderStatusModel::suggestExpand, ui->_folderList, &QTreeView::expand);
connect(_model, &FolderStatusModel::dirtyChanged, this, &AccountSettings::refreshSelectiveSyncStatus); connect(_model, &FolderStatusModel::dirtyChanged, this, &AccountSettings::refreshSelectiveSyncStatus);
refreshSelectiveSyncStatus(); refreshSelectiveSyncStatus();
connect(_model, &QAbstractItemModel::rowsInserted, connect(_model, &QAbstractItemModel::rowsInserted,
@@ -173,26 +172,25 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
addAction(syncNowWithRemoteDiscovery); addAction(syncNowWithRemoteDiscovery);
connect(_ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync); connect(ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(_ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders); connect(ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
connect(_ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync); connect(ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(_ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders); connect(ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
connect(_ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders); connect(ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders); connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders);
connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders); connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders);
// quotaProgressBar style now set in customizeStyle() QColor color = palette().highlight().color();
/*QColor color = palette().highlight().color(); ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
_ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));*/
_ui->connectLabel->setText(tr("No account configured.")); ui->connectLabel->setText(tr("No account configured."));
connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged); connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged);
slotAccountStateChanged(); slotAccountStateChanged();
connect(&_userInfo, &UserInfo::quotaUpdated, connect(&_quotaInfo, &QuotaInfo::quotaUpdated,
this, &AccountSettings::slotUpdateQuota); this, &AccountSettings::slotUpdateQuota);
// Connect E2E stuff // Connect E2E stuff
@@ -204,10 +202,8 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
{ {
slotNewMnemonicGenerated(); slotNewMnemonicGenerated();
} else { } else {
_ui->encryptionMessage->hide(); ui->encryptionMessage->hide();
} }
customizeStyle();
} }
@@ -229,9 +225,9 @@ void AccountSettings::createAccountToolbox()
menu->addAction(action); menu->addAction(action);
connect(action, &QAction::triggered, this, &AccountSettings::slotDeleteAccount); connect(action, &QAction::triggered, this, &AccountSettings::slotDeleteAccount);
_ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' ')); ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
_ui->_accountToolbox->setMenu(menu); ui->_accountToolbox->setMenu(menu);
_ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup); ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
slotAccountAdded(_accountState); slotAccountAdded(_accountState);
} }
@@ -239,14 +235,14 @@ void AccountSettings::createAccountToolbox()
void AccountSettings::slotNewMnemonicGenerated() void AccountSettings::slotNewMnemonicGenerated()
{ {
_ui->encryptionMessage->setText(tr("This account supports end-to-end encryption")); ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
QAction *mnemonic = new QAction(tr("Enable encryption"), this); QAction *mnemonic = new QAction(tr("Enable encryption"), this);
connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic); connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic);
connect(mnemonic, &QAction::triggered, _ui->encryptionMessage, &KMessageWidget::hide); connect(mnemonic, &QAction::triggered, ui->encryptionMessage, &KMessageWidget::hide);
_ui->encryptionMessage->addAction(mnemonic); ui->encryptionMessage->addAction(mnemonic);
_ui->encryptionMessage->show(); ui->encryptionMessage->show();
} }
void AccountSettings::slotMenuBeforeShow() { void AccountSettings::slotMenuBeforeShow() {
@@ -254,7 +250,7 @@ void AccountSettings::slotMenuBeforeShow() {
return; return;
} }
auto menu = _ui->_accountToolbox->menu(); auto menu = ui->_accountToolbox->menu();
// We can't check this during the initial creation as there is no account yet then // We can't check this during the initial creation as there is no account yet then
if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) { if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) {
@@ -269,7 +265,7 @@ void AccountSettings::slotMenuBeforeShow() {
QString AccountSettings::selectedFolderAlias() const QString AccountSettings::selectedFolderAlias() const
{ {
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex(); QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid()) if (!selected.isValid())
return ""; return "";
return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString(); return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString();
@@ -282,7 +278,16 @@ void AccountSettings::slotOpenAccountWizard()
if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) { if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) {
topLevelWidget()->close(); topLevelWidget()->close();
} }
#ifdef Q_OS_MAC
qCDebug(lcAccountSettings) << parent() << topLevelWidget();
SettingsDialogMac *sd = qobject_cast<SettingsDialogMac *>(topLevelWidget());
if (sd) {
sd->showActivityPage();
} else {
qFatal("nope");
}
#endif
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), nullptr); OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), nullptr);
} }
@@ -298,7 +303,7 @@ void AccountSettings::slotToggleSignInState()
void AccountSettings::doExpand() void AccountSettings::doExpand()
{ {
_ui->_folderList->expandToDepth(0); ui->_folderList->expandToDepth(0);
} }
void AccountSettings::slotShowMnemonic(const QString &mnemonic) { void AccountSettings::slotShowMnemonic(const QString &mnemonic) {
@@ -329,9 +334,9 @@ void AccountSettings::slotEncryptionFlagError(const QByteArray& fileId, int http
void AccountSettings::slotLockForEncryptionSuccess(const QByteArray& fileId, const QByteArray &token) void AccountSettings::slotLockForEncryptionSuccess(const QByteArray& fileId, const QByteArray &token)
{ {
accountsState()->account()->e2e()->setTokenForFolder(fileId, token); accountsState()->account()->e2e()->setTokenForFolder(fileId, token);
FolderMetadata emptyMetadata(accountsState()->account()); FolderMetadata emptyMetadata(accountsState()->account());
auto encryptedMetadata = emptyMetadata.encryptedMetadata(); auto encryptedMetadata = emptyMetadata.encryptedMetadata();
if (encryptedMetadata.isEmpty()) { if (encryptedMetadata.isEmpty()) {
//TODO: Mark the folder as unencrypted as the metadata generation failed. //TODO: Mark the folder as unencrypted as the metadata generation failed.
@@ -343,36 +348,36 @@ void AccountSettings::slotLockForEncryptionSuccess(const QByteArray& fileId, con
return; return;
} }
auto storeMetadataJob = new StoreMetaDataApiJob(accountsState()->account(), fileId, emptyMetadata.encryptedMetadata()); auto storeMetadataJob = new StoreMetaDataApiJob(accountsState()->account(), fileId, emptyMetadata.encryptedMetadata());
connect(storeMetadataJob, &StoreMetaDataApiJob::success, connect(storeMetadataJob, &StoreMetaDataApiJob::success,
this, &AccountSettings::slotUploadMetadataSuccess); this, &AccountSettings::slotUploadMetadataSuccess);
connect(storeMetadataJob, &StoreMetaDataApiJob::error, connect(storeMetadataJob, &StoreMetaDataApiJob::error,
this, &AccountSettings::slotUpdateMetadataError); this, &AccountSettings::slotUpdateMetadataError);
storeMetadataJob->start(); storeMetadataJob->start();
} }
void AccountSettings::slotUploadMetadataSuccess(const QByteArray& folderId) void AccountSettings::slotUploadMetadataSuccess(const QByteArray& folderId)
{ {
const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId); const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId);
auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token); auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token);
connect(unlockJob, &UnlockEncryptFolderApiJob::success, connect(unlockJob, &UnlockEncryptFolderApiJob::success,
this, &AccountSettings::slotUnlockFolderSuccess); this, &AccountSettings::slotUnlockFolderSuccess);
connect(unlockJob, &UnlockEncryptFolderApiJob::error, connect(unlockJob, &UnlockEncryptFolderApiJob::error,
this, &AccountSettings::slotUnlockFolderError); this, &AccountSettings::slotUnlockFolderError);
unlockJob->start(); unlockJob->start();
} }
void AccountSettings::slotUpdateMetadataError(const QByteArray& folderId, int httpReturnCode) void AccountSettings::slotUpdateMetadataError(const QByteArray& folderId, int httpReturnCode)
{ {
Q_UNUSED(httpReturnCode); Q_UNUSED(httpReturnCode);
const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId); const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId);
auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token); auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token);
connect(unlockJob, &UnlockEncryptFolderApiJob::success, connect(unlockJob, &UnlockEncryptFolderApiJob::success,
this, &AccountSettings::slotUnlockFolderSuccess); this, &AccountSettings::slotUnlockFolderSuccess);
connect(unlockJob, &UnlockEncryptFolderApiJob::error, connect(unlockJob, &UnlockEncryptFolderApiJob::error,
this, &AccountSettings::slotUnlockFolderError); this, &AccountSettings::slotUnlockFolderError);
unlockJob->start(); unlockJob->start();
} }
void AccountSettings::slotLockForEncryptionError(const QByteArray& fileId, int httpErrorCode) void AccountSettings::slotLockForEncryptionError(const QByteArray& fileId, int httpErrorCode)
@@ -546,7 +551,7 @@ void AccountSettings::slotEditCurrentIgnoredFiles()
void AccountSettings::slotEditCurrentLocalIgnoredFiles() void AccountSettings::slotEditCurrentLocalIgnoredFiles()
{ {
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex(); QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder) if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return; return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString(); QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
@@ -618,7 +623,7 @@ void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index
void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos) void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
{ {
QTreeView *tv = _ui->_folderList; QTreeView *tv = ui->_folderList;
QModelIndex index = tv->indexAt(pos); QModelIndex index = tv->indexAt(pos);
if (!index.isValid()) { if (!index.isValid()) {
return; return;
@@ -649,7 +654,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
ac = menu->addAction(tr("Edit Ignored Files")); ac = menu->addAction(tr("Edit Ignored Files"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles); connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles);
if (!_ui->_folderList->isExpanded(index)) { if (!ui->_folderList->isExpanded(index)) {
ac = menu->addAction(tr("Choose what to sync")); ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected); ac->setEnabled(folderConnected);
connect(ac, &QAction::triggered, this, &AccountSettings::doExpand); connect(ac, &QAction::triggered, this, &AccountSettings::doExpand);
@@ -688,7 +693,7 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
} }
if (_model->classify(indx) == FolderStatusModel::RootFolder) { if (_model->classify(indx) == FolderStatusModel::RootFolder) {
// tries to find if we clicked on the '...' button. // tries to find if we clicked on the '...' button.
QTreeView *tv = _ui->_folderList; QTreeView *tv = ui->_folderList;
auto pos = tv->mapFromGlobal(QCursor::pos()); auto pos = tv->mapFromGlobal(QCursor::pos());
if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) { if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) {
slotCustomContextMenuRequested(pos); slotCustomContextMenuRequested(pos);
@@ -701,8 +706,8 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
// Expand root items on single click // Expand root items on single click
if (_accountState && _accountState->state() == AccountState::Connected) { if (_accountState && _accountState->state() == AccountState::Connected) {
bool expanded = !(_ui->_folderList->isExpanded(indx)); bool expanded = !(ui->_folderList->isExpanded(indx));
_ui->_folderList->setExpanded(indx, expanded); ui->_folderList->setExpanded(indx, expanded);
} }
} }
} }
@@ -739,7 +744,7 @@ void AccountSettings::slotFolderWizardAccepted()
qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath; qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath;
if (!dir.mkpath(".")) { if (!dir.mkpath(".")) {
QMessageBox::warning(this, tr("Folder creation failed"), QMessageBox::warning(this, tr("Folder creation failed"),
tr("<p>Could not create local folder <i>%1</i>.</p>") tr("<p>Could not create local folder <i>%1</i>.")
.arg(QDir::toNativeSeparators(definition.localPath))); .arg(QDir::toNativeSeparators(definition.localPath)));
return; return;
} }
@@ -784,7 +789,7 @@ void AccountSettings::slotRemoveCurrentFolder()
{ {
FolderMan *folderMan = FolderMan::instance(); FolderMan *folderMan = FolderMan::instance();
auto folder = folderMan->folder(selectedFolderAlias()); auto folder = folderMan->folder(selectedFolderAlias());
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex(); QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (selected.isValid() && folder) { if (selected.isValid() && folder) {
int row = selected.row(); int row = selected.row();
@@ -826,7 +831,7 @@ void AccountSettings::slotOpenCurrentFolder()
void AccountSettings::slotOpenCurrentLocalSubFolder() void AccountSettings::slotOpenCurrentLocalSubFolder()
{ {
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex(); QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder) if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return; return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString(); QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
@@ -840,21 +845,18 @@ void AccountSettings::showConnectionLabel(const QString &message, QStringList er
"border-width: 1px; border-style: solid; border-color: #aaaaaa;" "border-width: 1px; border-style: solid; border-color: #aaaaaa;"
"border-radius:5px;"); "border-radius:5px;");
if (errors.isEmpty()) { if (errors.isEmpty()) {
QString msg = message; ui->connectLabel->setText(message);
Theme::replaceLinkColorStringBackgroundAware(msg); ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setText(msg); ui->connectLabel->setStyleSheet(QString());
_ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setStyleSheet(QString());
} else { } else {
errors.prepend(message); errors.prepend(message);
QString msg = errors.join(QLatin1String("\n")); const QString msg = errors.join(QLatin1String("\n"));
qCDebug(lcAccountSettings) << msg; qCDebug(lcAccountSettings) << msg;
Theme::replaceLinkColorString(msg, QColor("#c1c8e6")); ui->connectLabel->setText(msg);
_ui->connectLabel->setText(msg); ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setToolTip(QString()); ui->connectLabel->setStyleSheet(errStyle);
_ui->connectLabel->setStyleSheet(errStyle);
} }
_ui->accountStatus->setVisible(!message.isEmpty()); ui->accountStatus->setVisible(!message.isEmpty());
} }
void AccountSettings::slotEnableCurrentFolder() void AccountSettings::slotEnableCurrentFolder()
@@ -952,29 +954,29 @@ void AccountSettings::slotOpenOC()
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used) void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
{ {
if (total > 0) { if (total > 0) {
_ui->quotaProgressBar->setVisible(true); ui->quotaProgressBar->setVisible(true);
_ui->quotaProgressBar->setEnabled(true); ui->quotaProgressBar->setEnabled(true);
// workaround the label only accepting ints (which may be only 32 bit wide) // workaround the label only accepting ints (which may be only 32 bit wide)
const double percent = used / (double)total * 100; const double percent = used / (double)total * 100;
const int percentInt = qMin(qRound(percent), 100); const int percentInt = qMin(qRound(percent), 100);
_ui->quotaProgressBar->setValue(percentInt); ui->quotaProgressBar->setValue(percentInt);
QString usedStr = Utility::octetsToString(used); QString usedStr = Utility::octetsToString(used);
QString totalStr = Utility::octetsToString(total); QString totalStr = Utility::octetsToString(total);
QString percentStr = Utility::compactFormatDouble(percent, 1); QString percentStr = Utility::compactFormatDouble(percent, 1);
QString toolTip = tr("%1 (%3%) of %2 in use. Some folders, including network mounted or shared folders, might have different limits.").arg(usedStr, totalStr, percentStr); QString toolTip = tr("%1 (%3%) of %2 in use. Some folders, including network mounted or shared folders, might have different limits.").arg(usedStr, totalStr, percentStr);
_ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr)); ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
_ui->quotaInfoLabel->setToolTip(toolTip); ui->quotaInfoLabel->setToolTip(toolTip);
_ui->quotaProgressBar->setToolTip(toolTip); ui->quotaProgressBar->setToolTip(toolTip);
} else { } else {
_ui->quotaProgressBar->setVisible(false); ui->quotaProgressBar->setVisible(false);
_ui->quotaInfoLabel->setToolTip(QString()); ui->quotaInfoLabel->setToolTip(QString());
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/ /* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
if (total == 0 || total == -1) { if (total == 0 || total == -1) {
_ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available.")); ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
} else { } else {
QString usedStr = Utility::octetsToString(used); QString usedStr = Utility::octetsToString(used);
_ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr)); ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
} }
} }
} }
@@ -983,7 +985,7 @@ void AccountSettings::slotAccountStateChanged()
{ {
int state = _accountState ? _accountState->state() : AccountState::Disconnected; int state = _accountState ? _accountState->state() : AccountState::Disconnected;
if (_accountState) { if (_accountState) {
_ui->sslButton->updateAccountState(_accountState); ui->sslButton->updateAccountState(_accountState);
AccountPtr account = _accountState->account(); AccountPtr account = _accountState->account();
QUrl safeUrl(account->url()); QUrl safeUrl(account->url());
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
@@ -1028,7 +1030,7 @@ void AccountSettings::slotAccountStateChanged()
"<a href='%1'>Click here</a> to re-open the browser.") "<a href='%1'>Click here</a> to re-open the browser.")
.arg(url.toString(QUrl::FullyEncoded))); .arg(url.toString(QUrl::FullyEncoded)));
} else { } else {
showConnectionLabel(tr("Connecting to %1").arg(serverWithUser)); showConnectionLabel(tr("Connecting to %1...").arg(serverWithUser));
} }
} else { } else {
showConnectionLabel(tr("No connection to %1 at %2.") showConnectionLabel(tr("No connection to %1 at %2.")
@@ -1042,14 +1044,14 @@ void AccountSettings::slotAccountStateChanged()
} }
/* Allow to expand the item if the account is connected. */ /* Allow to expand the item if the account is connected. */
_ui->_folderList->setItemsExpandable(state == AccountState::Connected); ui->_folderList->setItemsExpandable(state == AccountState::Connected);
if (state != AccountState::Connected) { if (state != AccountState::Connected) {
/* check if there are expanded root items, if so, close them */ /* check if there are expanded root items, if so, close them */
int i; int i;
for (i = 0; i < _model->rowCount(); ++i) { for (i = 0; i < _model->rowCount(); ++i) {
if (_ui->_folderList->isExpanded(_model->index(i))) if (ui->_folderList->isExpanded(_model->index(i)))
_ui->_folderList->setExpanded(_model->index(i), false); ui->_folderList->setExpanded(_model->index(i), false);
} }
} else if (_model->isDirty()) { } else if (_model->isDirty()) {
// If we connect and have pending changes, show the list. // If we connect and have pending changes, show the list.
@@ -1093,21 +1095,21 @@ void AccountSettings::slotLinkActivated(const QString &link)
// Make sure the folder itself is expanded // Make sure the folder itself is expanded
Folder *f = FolderMan::instance()->folder(alias); Folder *f = FolderMan::instance()->folder(alias);
QModelIndex folderIndx = _model->indexForPath(f, QString()); QModelIndex folderIndx = _model->indexForPath(f, QString());
if (!_ui->_folderList->isExpanded(folderIndx)) { if (!ui->_folderList->isExpanded(folderIndx)) {
_ui->_folderList->setExpanded(folderIndx, true); ui->_folderList->setExpanded(folderIndx, true);
} }
QModelIndex indx = _model->indexForPath(f, myFolder); QModelIndex indx = _model->indexForPath(f, myFolder);
if (indx.isValid()) { if (indx.isValid()) {
// make sure all the parents are expanded // make sure all the parents are expanded
for (auto i = indx.parent(); i.isValid(); i = i.parent()) { for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
if (!_ui->_folderList->isExpanded(i)) { if (!ui->_folderList->isExpanded(i)) {
_ui->_folderList->setExpanded(i, true); ui->_folderList->setExpanded(i, true);
} }
} }
_ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection); ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
_ui->_folderList->setCurrentIndex(indx); ui->_folderList->setCurrentIndex(indx);
_ui->_folderList->scrollTo(indx); ui->_folderList->scrollTo(indx);
} else { } else {
qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder; qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder;
} }
@@ -1116,7 +1118,7 @@ void AccountSettings::slotLinkActivated(const QString &link)
AccountSettings::~AccountSettings() AccountSettings::~AccountSettings()
{ {
delete _ui; delete ui;
} }
void AccountSettings::refreshSelectiveSyncStatus() void AccountSettings::refreshSelectiveSyncStatus()
@@ -1154,8 +1156,8 @@ void AccountSettings::refreshSelectiveSyncStatus()
} }
if (msg.isEmpty()) { if (msg.isEmpty()) {
_ui->selectiveSyncButtons->setVisible(true); ui->selectiveSyncButtons->setVisible(true);
_ui->bigFolderUi->setVisible(false); ui->bigFolderUi->setVisible(false);
} else { } else {
ConfigFile cfg; ConfigFile cfg;
QString info = !cfg.confirmExternalStorage() QString info = !cfg.confirmExternalStorage()
@@ -1164,27 +1166,27 @@ void AccountSettings::refreshSelectiveSyncStatus()
? tr("There are folders that were not synchronized because they are external storages: ") ? tr("There are folders that were not synchronized because they are external storages: ")
: tr("There are folders that were not synchronized because they are too big or external storages: "); : tr("There are folders that were not synchronized because they are too big or external storages: ");
_ui->selectiveSyncNotification->setText(info + msg); ui->selectiveSyncNotification->setText(info + msg);
_ui->selectiveSyncButtons->setVisible(false); ui->selectiveSyncButtons->setVisible(false);
_ui->bigFolderUi->setVisible(true); ui->bigFolderUi->setVisible(true);
shouldBeVisible = true; shouldBeVisible = true;
} }
_ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty()); ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
bool wasVisible = !_ui->selectiveSyncStatus->isHidden(); bool wasVisible = !ui->selectiveSyncStatus->isHidden();
if (wasVisible != shouldBeVisible) { if (wasVisible != shouldBeVisible) {
QSize hint = _ui->selectiveSyncStatus->sizeHint(); QSize hint = ui->selectiveSyncStatus->sizeHint();
if (shouldBeVisible) { if (shouldBeVisible) {
_ui->selectiveSyncStatus->setMaximumHeight(0); ui->selectiveSyncStatus->setMaximumHeight(0);
_ui->selectiveSyncStatus->setVisible(true); ui->selectiveSyncStatus->setVisible(true);
} }
auto anim = new QPropertyAnimation(_ui->selectiveSyncStatus, "maximumHeight", _ui->selectiveSyncStatus); auto anim = new QPropertyAnimation(ui->selectiveSyncStatus, "maximumHeight", ui->selectiveSyncStatus);
anim->setEndValue(shouldBeVisible ? hint.height() : 0); anim->setEndValue(shouldBeVisible ? hint.height() : 0);
anim->start(QAbstractAnimation::DeleteWhenStopped); anim->start(QAbstractAnimation::DeleteWhenStopped);
connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() { connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() {
_ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX); ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
if (!shouldBeVisible) { if (!shouldBeVisible) {
_ui->selectiveSyncStatus->hide(); ui->selectiveSyncStatus->hide();
} }
}); });
} }
@@ -1238,36 +1240,18 @@ void AccountSettings::slotDeleteAccount()
bool AccountSettings::event(QEvent *e) bool AccountSettings::event(QEvent *e)
{ {
if (e->type() == QEvent::Hide || e->type() == QEvent::Show) { if (e->type() == QEvent::Hide || e->type() == QEvent::Show) {
_userInfo.setActive(isVisible()); _quotaInfo.setActive(isVisible());
} }
if (e->type() == QEvent::Show) { if (e->type() == QEvent::Show) {
// Expand the folder automatically only if there's only one, see #4283 // Expand the folder automatically only if there's only one, see #4283
// The 2 is 1 folder + 1 'add folder' button // The 2 is 1 folder + 1 'add folder' button
if (_model->rowCount() <= 2) { if (_model->rowCount() <= 2) {
_ui->_folderList->setExpanded(_model->index(0, 0), true); ui->_folderList->setExpanded(_model->index(0, 0), true);
} }
} }
return QWidget::event(e); return QWidget::event(e);
} }
void AccountSettings::slotStyleChanged()
{
customizeStyle();
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
}
void AccountSettings::customizeStyle()
{
QString msg = _ui->connectLabel->text();
Theme::replaceLinkColorStringBackgroundAware(msg);
_ui->connectLabel->setText(msg);
QColor color = palette().highlight().color();
_ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
}
} // namespace OCC } // namespace OCC
#include "accountsettings.moc" #include "accountsettings.moc"

View File

@@ -22,7 +22,7 @@
#include <QTimer> #include <QTimer>
#include "folder.h" #include "folder.h"
#include "userinfo.h" #include "quotainfo.h"
#include "progressdispatcher.h" #include "progressdispatcher.h"
#include "owncloudgui.h" #include "owncloudgui.h"
#include "folderstatusmodel.h" #include "folderstatusmodel.h"
@@ -63,14 +63,11 @@ signals:
void openFolderAlias(const QString &); void openFolderAlias(const QString &);
void showIssuesList(AccountState *account); void showIssuesList(AccountState *account);
void requesetMnemonic(); void requesetMnemonic();
void removeAccountFolders(AccountState *account);
void styleChanged();
public slots: public slots:
void slotOpenOC(); void slotOpenOC();
void slotUpdateQuota(qint64, qint64); void slotUpdateQuota(qint64, qint64);
void slotAccountStateChanged(); void slotAccountStateChanged();
void slotStyleChanged();
AccountState *accountsState() { return _accountState; } AccountState *accountsState() { return _accountState; }
@@ -131,18 +128,17 @@ private:
bool event(QEvent *) override; bool event(QEvent *) override;
void createAccountToolbox(); void createAccountToolbox();
void openIgnoredFilesDialog(const QString & absFolderPath); void openIgnoredFilesDialog(const QString & absFolderPath);
void customizeStyle();
/// Returns the alias of the selected folder, empty string if none /// Returns the alias of the selected folder, empty string if none
QString selectedFolderAlias() const; QString selectedFolderAlias() const;
Ui::AccountSettings *_ui; Ui::AccountSettings *ui;
FolderStatusModel *_model; FolderStatusModel *_model;
QUrl _OCUrl; QUrl _OCUrl;
bool _wasDisabledBefore; bool _wasDisabledBefore;
AccountState *_accountState; AccountState *_accountState;
UserInfo _userInfo; QuotaInfo _quotaInfo;
QAction *_toggleSignInOutAction; QAction *_toggleSignInOutAction;
QAction *_addAccountAction; QAction *_addAccountAction;

View File

@@ -187,7 +187,7 @@
<item row="0" column="2"> <item row="0" column="2">
<widget class="QToolButton" name="_accountToolbox"> <widget class="QToolButton" name="_accountToolbox">
<property name="text"> <property name="text">
<string notr="true">...</string> <string>...</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -208,7 +208,7 @@
<string/> <string/>
</property> </property>
<property name="text"> <property name="text">
<string>Storage space: </string> <string>Storage space: ...</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::PlainText</enum> <enum>Qt::PlainText</enum>

View File

@@ -14,7 +14,6 @@
#include "accountstate.h" #include "accountstate.h"
#include "accountmanager.h" #include "accountmanager.h"
#include "remotewipe.h"
#include "account.h" #include "account.h"
#include "creds/abstractcredentials.h" #include "creds/abstractcredentials.h"
#include "creds/httpcredentials.h" #include "creds/httpcredentials.h"
@@ -25,11 +24,6 @@
#include <QTimer> #include <QTimer>
#include <qfontmetrics.h> #include <qfontmetrics.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkRequest>
#include <QBuffer>
namespace OCC { namespace OCC {
Q_LOGGING_CATEGORY(lcAccountState, "nextcloud.gui.account.state", QtInfoMsg) Q_LOGGING_CATEGORY(lcAccountState, "nextcloud.gui.account.state", QtInfoMsg)
@@ -42,12 +36,11 @@ AccountState::AccountState(AccountPtr account)
, _waitingForNewCredentials(false) , _waitingForNewCredentials(false)
, _notificationsEtagResponseHeader("*") , _notificationsEtagResponseHeader("*")
, _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay , _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay
, _remoteWipe(new RemoteWipe(_account))
{ {
qRegisterMetaType<AccountState *>("AccountState*"); qRegisterMetaType<AccountState *>("AccountState*");
connect(account.data(), &Account::invalidCredentials, connect(account.data(), &Account::invalidCredentials,
this, &AccountState::slotHandleRemoteWipeCheck); this, &AccountState::slotInvalidCredentials);
connect(account.data(), &Account::credentialsFetched, connect(account.data(), &Account::credentialsFetched,
this, &AccountState::slotCredentialsFetched); this, &AccountState::slotCredentialsFetched);
connect(account.data(), &Account::credentialsAsked, connect(account.data(), &Account::credentialsAsked,
@@ -229,7 +222,7 @@ void AccountState::checkConnectivity()
return; return;
} }
ConnectionValidator *conValidator = new ConnectionValidator(AccountStatePtr(this)); ConnectionValidator *conValidator = new ConnectionValidator(account());
_connectionValidator = conValidator; _connectionValidator = conValidator;
connect(conValidator, &ConnectionValidator::connectionResult, connect(conValidator, &ConnectionValidator::connectionResult,
this, &AccountState::slotConnectionValidatorResult); this, &AccountState::slotConnectionValidatorResult);
@@ -310,7 +303,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
break; break;
case ConnectionValidator::CredentialsWrong: case ConnectionValidator::CredentialsWrong:
case ConnectionValidator::CredentialsNotReady: case ConnectionValidator::CredentialsNotReady:
handleInvalidCredentials(); slotInvalidCredentials();
break; break;
case ConnectionValidator::SslError: case ConnectionValidator::SslError:
setState(SignedOut); setState(SignedOut);
@@ -329,20 +322,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
} }
} }
void AccountState::slotHandleRemoteWipeCheck() void AccountState::slotInvalidCredentials()
{
// make sure it changes account state and icons
signOutByUi();
qCInfo(lcAccountState) << "Invalid credentials for" << _account->url().toString()
<< "checking for remote wipe request";
_waitingForNewCredentials = false;
setState(SignedOut);
}
void AccountState::handleInvalidCredentials()
{ {
if (isSignedOut() || _waitingForNewCredentials) if (isSignedOut() || _waitingForNewCredentials)
return; return;
@@ -363,7 +343,6 @@ void AccountState::handleInvalidCredentials()
account()->credentials()->askFromUser(); account()->credentials()->askFromUser();
} }
void AccountState::slotCredentialsFetched(AbstractCredentials *) void AccountState::slotCredentialsFetched(AbstractCredentials *)
{ {
// Make a connection attempt, no matter whether the credentials are // Make a connection attempt, no matter whether the credentials are

View File

@@ -29,7 +29,6 @@ namespace OCC {
class AccountState; class AccountState;
class Account; class Account;
class RemoteWipe;
typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr; typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr;
@@ -151,9 +150,6 @@ public:
*/ */
void setNavigationAppsEtagResponseHeader(const QByteArray &value); void setNavigationAppsEtagResponseHeader(const QByteArray &value);
///Asks for user credentials
void handleInvalidCredentials();
public slots: public slots:
/// Triggers a ping to the server to update state and /// Triggers a ping to the server to update state and
/// connection status and errors. /// connection status and errors.
@@ -168,11 +164,7 @@ signals:
protected Q_SLOTS: protected Q_SLOTS:
void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList &errors); void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList &errors);
void slotInvalidCredentials();
/// When client gets a 401 or 403 checks if server requested remote wipe
/// before asking for user credentials again
void slotHandleRemoteWipeCheck();
void slotCredentialsFetched(AbstractCredentials *creds); void slotCredentialsFetched(AbstractCredentials *creds);
void slotCredentialsAsked(AbstractCredentials *creds); void slotCredentialsAsked(AbstractCredentials *creds);
@@ -198,13 +190,6 @@ private:
* Milliseconds for which to delay reconnection after 503/maintenance. * Milliseconds for which to delay reconnection after 503/maintenance.
*/ */
int _maintenanceToConnectedDelay; int _maintenanceToConnectedDelay;
/**
* Connects remote wipe check with the account
* the log out triggers the check (loads app password -> create request)
*/
RemoteWipe *_remoteWipe;
}; };
} }

View File

@@ -21,7 +21,7 @@ namespace OCC {
bool operator<(const Activity &rhs, const Activity &lhs) bool operator<(const Activity &rhs, const Activity &lhs)
{ {
return rhs._dateTime > lhs._dateTime; return rhs._dateTime.toMSecsSinceEpoch() > lhs._dateTime.toMSecsSinceEpoch();
} }
bool operator==(const Activity &rhs, const Activity &lhs) bool operator==(const Activity &rhs, const Activity &lhs)

View File

@@ -15,7 +15,6 @@
*/ */
#include "activityitemdelegate.h" #include "activityitemdelegate.h"
#include "activitylistmodel.h"
#include "folderstatusmodel.h" #include "folderstatusmodel.h"
#include "folderman.h" #include "folderman.h"
#include "accountstate.h" #include "accountstate.h"
@@ -27,14 +26,6 @@
#include <QPainter> #include <QPainter>
#include <QApplication> #include <QApplication>
#define FIXME_USE_HIGH_DPI_RATIO
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
#include <QDesktopWidget>
#endif
#define HASQT5_11 (QT_VERSION >= QT_VERSION_CHECK(5,11,0))
namespace OCC { namespace OCC {
int ActivityItemDelegate::_iconHeight = 0; int ActivityItemDelegate::_iconHeight = 0;
@@ -47,12 +38,6 @@ int ActivityItemDelegate::_buttonHeight = 0;
const QString ActivityItemDelegate::_remote_share("remote_share"); const QString ActivityItemDelegate::_remote_share("remote_share");
const QString ActivityItemDelegate::_call("call"); const QString ActivityItemDelegate::_call("call");
ActivityItemDelegate::ActivityItemDelegate()
: QStyledItemDelegate()
{
customizeStyle();
}
int ActivityItemDelegate::iconHeight() int ActivityItemDelegate::iconHeight()
{ {
if (_iconHeight == 0) { if (_iconHeight == 0) {
@@ -102,26 +87,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
int iconSize = 16; int iconSize = 16;
int iconOffset = qRound(fm.height() / 4.0 * 7.0); int iconOffset = qRound(fm.height() / 4.0 * 7.0);
int offset = 4; int offset = 4;
const bool isSelected = (option.state & QStyle::State_Selected);
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
const int device_pixel_ration = QApplication::desktop()->devicePixelRatio();
int pixel_ratio = (device_pixel_ration > 1 ? device_pixel_ration : 1);
#endif
// get the data // get the data
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole)); Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole));
QIcon actionIcon; QIcon actionIcon = qvariant_cast<QIcon>(index.data(ActionIconRole));
const ActivityListModel::ActionIcon icn = qvariant_cast<ActivityListModel::ActionIcon>(index.data(ActionIconRole));
switch(icn.iconType) {
case ActivityListModel::ActivityIconType::iconUseCached: actionIcon = icn.cachedIcon; break;
case ActivityListModel::ActivityIconType::iconActivity: actionIcon = (isSelected ? _iconActivity_sel : _iconActivity); break;
case ActivityListModel::ActivityIconType::iconBell: actionIcon = (isSelected ? _iconBell_sel : _iconBell); break;
case ActivityListModel::ActivityIconType::iconStateError: actionIcon = _iconStateError; break;
case ActivityListModel::ActivityIconType::iconStateWarning: actionIcon = _iconStateWarning; break;
case ActivityListModel::ActivityIconType::iconStateInfo: actionIcon = _iconStateInfo; break;
case ActivityListModel::ActivityIconType::iconStateSync: actionIcon = _iconStateSync; break;
}
QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole)); QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole));
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole)); QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
QString messageText = qvariant_cast<QString>(index.data(MessageRole)); QString messageText = qvariant_cast<QString>(index.data(MessageRole));
@@ -137,27 +106,15 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
// subject text rect // subject text rect
QRect actionTextBox = actionIconRect; QRect actionTextBox = actionIconRect;
#if (HASQT5_11)
int actionTextBoxWidth = fm.horizontalAdvance(actionText);
#else
int actionTextBoxWidth = fm.width(actionText); int actionTextBoxWidth = fm.width(actionText);
#endif
actionTextBox.setTop(option.rect.top() + margin + offset/2); actionTextBox.setTop(option.rect.top() + margin + offset/2);
actionTextBox.setHeight(fm.height()); actionTextBox.setHeight(fm.height());
actionTextBox.setLeft(actionIconRect.right() + margin); actionTextBox.setLeft(actionIconRect.right() + margin);
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
actionTextBoxWidth *= pixel_ratio;
#endif
actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin); actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin);
// message text rect // message text rect
QRect messageTextBox = actionTextBox; QRect messageTextBox = actionTextBox;
#if (HASQT5_11)
int messageTextWidth = fm.horizontalAdvance(messageText);
#else
int messageTextWidth = fm.width(messageText); int messageTextWidth = fm.width(messageText);
#endif
int messageTextTop = option.rect.top() + fm.height() + margin; int messageTextTop = option.rect.top() + fm.height() + margin;
if(actionText.isEmpty()) messageTextTop = option.rect.top() + margin + offset/2; if(actionText.isEmpty()) messageTextTop = option.rect.top() + margin + offset/2;
messageTextBox.setTop(messageTextTop); messageTextBox.setTop(messageTextTop);
@@ -171,21 +128,14 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
// time box rect // time box rect
QRect timeBox = messageTextBox; QRect timeBox = messageTextBox;
#if (HASQT5_11) QString timeStr = tr("%1").arg(timeText);
int timeTextWidth = fm.horizontalAdvance(timeText); int timeTextWidth = fm.width(timeStr);
#else
int timeTextWidth = fm.width(timeText);
#endif
int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2; int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2;
if(messageText.isEmpty() || actionText.isEmpty()) if(messageText.isEmpty() || actionText.isEmpty())
timeTop = option.rect.top() + fm.height() + margin; timeTop = option.rect.top() + fm.height() + margin;
timeBox.setTop(timeTop); timeBox.setTop(timeTop);
timeBox.setHeight(fm.height()); timeBox.setHeight(fm.height());
timeBox.setBottom(timeBox.top() + fm.height()); timeBox.setBottom(timeBox.top() + fm.height());
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
timeTextWidth *= pixel_ratio;
#endif
timeBox.setRight(timeBox.left() + timeTextWidth + margin); timeBox.setRight(timeBox.left() + timeTextWidth + margin);
// buttons - default values // buttons - default values
@@ -220,9 +170,9 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
if(activityType == Activity::Type::NotificationType){ if(activityType == Activity::Type::NotificationType){
// Secondary will be 'Dismiss' or '...' multiple options button // Secondary will be 'Dismiss' or '...' multiple options button
secondaryButton.icon = (isSelected ? _iconClose_sel : _iconClose); secondaryButton.icon = QIcon(QLatin1String(":/client/resources/close.svg"));
if(customList.size() > 1) if(customList.size() > 1)
secondaryButton.icon = (isSelected ? _iconMore_sel : _iconMore); secondaryButton.icon = QIcon(QLatin1String(":/client/resources/more.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize); secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be 'More Information' or 'Accept' // Primary button will be 'More Information' or 'Accept'
@@ -230,11 +180,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
if(objectType == _remote_share) primaryButton.text = tr("Accept"); if(objectType == _remote_share) primaryButton.text = tr("Accept");
if(objectType == _call) primaryButton.text = tr("Join"); if(objectType == _call) primaryButton.text = tr("Join");
#if (HASQT5_11)
primaryButton.rect.setLeft(left - margin * 2 - fm.horizontalAdvance(primaryButton.text));
#else
primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text)); primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text));
#endif
// save info to be able to filter mouse clicks // save info to be able to filter mouse clicks
_buttonHeight = buttonSize; _buttonHeight = buttonSize;
@@ -245,17 +191,12 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} else if(activityType == Activity::SyncResultType){ } else if(activityType == Activity::SyncResultType){
// Secondary will be 'open file manager' with the folder icon // Secondary will be 'open file manager' with the folder icon
secondaryButton.icon = _iconFolder; secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize); secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be 'open browser' // Primary button will be 'open browser'
primaryButton.text = tr("Open Browser"); primaryButton.text = tr("Open Browser");
#if (HASQT5_11)
primaryButton.rect.setLeft(left - margin * 2 - fm.horizontalAdvance(primaryButton.text));
#else
primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text)); primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text));
#endif
// save info to be able to filter mouse clicks // save info to be able to filter mouse clicks
_buttonHeight = buttonSize; _buttonHeight = buttonSize;
@@ -266,7 +207,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} else if(activityType == Activity::SyncFileItemType){ } else if(activityType == Activity::SyncFileItemType){
// Secondary will be 'open file manager' with the folder icon // Secondary will be 'open file manager' with the folder icon
secondaryButton.icon = _iconFolder; secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize); secondaryButton.iconSize = QSize(iconSize, iconSize);
// No primary button on this case // No primary button on this case
@@ -291,7 +232,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
p.setCurrentColorGroup(QPalette::Disabled); p.setCurrentColorGroup(QPalette::Disabled);
// change pen color if the line is selected // change pen color if the line is selected
if (isSelected) if (option.state & QStyle::State_Selected)
painter->setPen(p.color(QPalette::HighlightedText)); painter->setPen(p.color(QPalette::HighlightedText));
else else
painter->setPen(p.color(QPalette::Text)); painter->setPen(p.color(QPalette::Text));
@@ -305,15 +246,8 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
painter->drawText(actionTextBox, elidedAction); painter->drawText(actionTextBox, elidedAction);
// draw the buttons // draw the buttons
if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType) { if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType)
primaryButton.palette = p;
if (isSelected)
primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::HighlightedText));
else
primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::Text));
QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter); QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter);
}
// Since they are errors on local syncing, there is nothing to do in the server // Since they are errors on local syncing, there is nothing to do in the server
if(activityType != Activity::Type::ActivityType) if(activityType != Activity::Type::ActivityType)
@@ -327,13 +261,13 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} }
// change pen color for the time // change pen color for the time
if (isSelected) if (option.state & QStyle::State_Selected)
painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText)); painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText));
else else
painter->setPen(p.color(QPalette::Disabled, QPalette::Text)); painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
// draw the time // draw the time
const QString elidedTime = fm.elidedText(timeText, Qt::ElideRight, spaceLeftForText); const QString elidedTime = fm.elidedText(timeStr, Qt::ElideRight, spaceLeftForText);
painter->drawText(timeBox, elidedTime); painter->drawText(timeBox, elidedTime);
painter->restore(); painter->restore();
@@ -376,32 +310,4 @@ bool ActivityItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
return QStyledItemDelegate::editorEvent(event, model, option, index); return QStyledItemDelegate::editorEvent(event, model, option, index);
} }
void ActivityItemDelegate::slotStyleChanged()
{
customizeStyle();
}
void ActivityItemDelegate::customizeStyle()
{
QPalette pal;
pal.setColor(QPalette::Base, QColor(0,0,0)); // use dark background colour to invert icons
_iconClose = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"));
_iconClose_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"), pal);
_iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
_iconMore_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"), pal);
_iconFolder = QIcon(QLatin1String(":/client/resources/folder.svg"));
_iconActivity = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"));
_iconActivity_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"), pal);
_iconBell = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"));
_iconBell_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"), pal);
_iconStateError = QIcon(QLatin1String(":/client/resources/state-error.svg"));
_iconStateWarning = QIcon(QLatin1String(":/client/resources/state-warning.svg"));
_iconStateInfo = QIcon(QLatin1String(":/client/resources/state-info.svg"));
_iconStateSync = QIcon(QLatin1String(":/client/resources/state-sync.svg"));
}
} // namespace OCC } // namespace OCC

View File

@@ -43,8 +43,6 @@ public:
AccountConnectedRole, AccountConnectedRole,
SyncFileStatusRole }; SyncFileStatusRole };
ActivityItemDelegate();
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override; QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
@@ -53,16 +51,11 @@ public:
static int rowHeight(); static int rowHeight();
static int iconHeight(); static int iconHeight();
public slots:
void slotStyleChanged();
signals: signals:
void primaryButtonClickedOnItemView(const QModelIndex &index); void primaryButtonClickedOnItemView(const QModelIndex &index);
void secondaryButtonClickedOnItemView(const QModelIndex &index); void secondaryButtonClickedOnItemView(const QModelIndex &index);
private: private:
void customizeStyle();
static int _margin; static int _margin;
static int _iconHeight; static int _iconHeight;
static int _primaryButtonWidth; static int _primaryButtonWidth;
@@ -72,23 +65,6 @@ private:
static int _buttonHeight; static int _buttonHeight;
static const QString _remote_share; static const QString _remote_share;
static const QString _call; static const QString _call;
QIcon _iconClose;
QIcon _iconClose_sel;
QIcon _iconMore;
QIcon _iconMore_sel;
QIcon _iconFolder;
QIcon _iconActivity;
QIcon _iconActivity_sel;
QIcon _iconBell;
QIcon _iconBell_sel;
QIcon _iconStateError;
QIcon _iconStateWarning;
QIcon _iconStateInfo;
QIcon _iconStateSync;
}; };
} // namespace OCC } // namespace OCC

View File

@@ -71,14 +71,13 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
return QVariant(list.at(0)); return QVariant(list.at(0));
} }
// File does not exist anymore? Let's try to open its path // File does not exist anymore? Let's try to open its path
if(QFileInfo(relPath).exists()) { list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account());
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account()); if (list.count() > 0) {
if (list.count() > 0) { return QVariant(list.at(0));
return QVariant(list.at(0));
}
} }
} }
return QVariant(); return QVariant();
break;
case ActivityItemDelegate::ActionsLinksRole:{ case ActivityItemDelegate::ActionsLinksRole:{
QList<QVariant> customList; QList<QVariant> customList;
foreach (ActivityLink customItem, a._links) { foreach (ActivityLink customItem, a._links) {
@@ -87,63 +86,61 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
customList << customVariant; customList << customVariant;
} }
return customList; return customList;
break;
} }
case ActivityItemDelegate::ActionIconRole:{ case ActivityItemDelegate::ActionIconRole:
ActionIcon actionIcon;
if(a._type == Activity::NotificationType){ if(a._type == Activity::NotificationType){
QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id); QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id);
if(!cachedIcon.isNull()) { if(!cachedIcon.isNull())
actionIcon.iconType = ActivityIconType::iconUseCached; return cachedIcon;
actionIcon.cachedIcon = cachedIcon; else return QIcon(QLatin1String(":/client/resources/bell.svg"));
} else {
actionIcon.iconType = ActivityIconType::iconBell;
}
} else if(a._type == Activity::SyncResultType){ } else if(a._type == Activity::SyncResultType){
actionIcon.iconType = ActivityIconType::iconStateError; return QIcon(QLatin1String(":/client/resources/state-error.svg"));
} else if(a._type == Activity::SyncFileItemType){ } else if(a._type == Activity::SyncFileItemType){
if(a._status == SyncFileItem::NormalError if(a._status == SyncFileItem::NormalError
|| a._status == SyncFileItem::FatalError || a._status == SyncFileItem::FatalError
|| a._status == SyncFileItem::DetailError || a._status == SyncFileItem::DetailError
|| a._status == SyncFileItem::BlacklistedError) { || a._status == SyncFileItem::BlacklistedError) {
actionIcon.iconType = ActivityIconType::iconStateError; return QIcon(QLatin1String(":/client/resources/state-error.svg"));
} else if(a._status == SyncFileItem::SoftError } else if(a._status == SyncFileItem::SoftError
|| a._status == SyncFileItem::Conflict || a._status == SyncFileItem::Conflict
|| a._status == SyncFileItem::Restoration || a._status == SyncFileItem::Restoration
|| a._status == SyncFileItem::FileLocked){ || a._status == SyncFileItem::FileLocked){
actionIcon.iconType = ActivityIconType::iconStateWarning; return QIcon(QLatin1String(":/client/resources/state-warning.svg"));
} else if(a._status == SyncFileItem::FileIgnored){ } else if(a._status == SyncFileItem::FileIgnored){
actionIcon.iconType = ActivityIconType::iconStateInfo; return QIcon(QLatin1String(":/client/resources/state-info.svg"));
} else {
actionIcon.iconType = ActivityIconType::iconStateSync;
} }
} else { return QIcon(QLatin1String(":/client/resources/state-sync.svg"));
actionIcon.iconType = ActivityIconType::iconActivity;
} }
QVariant icn; return QIcon(QLatin1String(":/client/resources/activity.png"));
icn.setValue(actionIcon); break;
return icn;
}
case ActivityItemDelegate::ObjectTypeRole: case ActivityItemDelegate::ObjectTypeRole:
return a._objectType; return a._objectType;
break;
case ActivityItemDelegate::ActionRole:{ case ActivityItemDelegate::ActionRole:{
QVariant type; QVariant type;
type.setValue(a._type); type.setValue(a._type);
return type; return type;
break;
} }
case ActivityItemDelegate::ActionTextRole: case ActivityItemDelegate::ActionTextRole:
return a._subject; return a._subject;
break;
case ActivityItemDelegate::MessageRole: case ActivityItemDelegate::MessageRole:
return a._message; return a._message;
break;
case ActivityItemDelegate::LinkRole: case ActivityItemDelegate::LinkRole:
return a._link; return a._link;
break;
case ActivityItemDelegate::AccountRole: case ActivityItemDelegate::AccountRole:
return a._accName; return a._accName;
break;
case ActivityItemDelegate::PointInTimeRole: case ActivityItemDelegate::PointInTimeRole:
return Utility::timeAgoInWords(a._dateTime.toLocalTime()); return Utility::timeAgoInWords(a._dateTime);
case Qt::ToolTipRole: break;
return a._dateTime.toLocalTime().toString(Qt::DefaultLocaleShortDate);
case ActivityItemDelegate::AccountConnectedRole: case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected()); return (ast && ast->isConnected());
break;
default: default:
return QVariant(); return QVariant();
} }
@@ -232,29 +229,6 @@ void ActivityListModel::addErrorToActivityList(Activity activity) {
combineActivityLists(); combineActivityLists();
} }
void ActivityListModel::addIgnoredFileToList(Activity newActivity) {
qCInfo(lcActivity) << "First checking for duplicates then add file to the notification list of ignored files: " << newActivity._file;
bool duplicate = false;
if(_listOfIgnoredFiles.size() == 0){
_notificationIgnoredFiles = newActivity;
_notificationIgnoredFiles._subject = tr("Files from the ignore list as well as symbolic links are not synced. This includes:");
_listOfIgnoredFiles.append(newActivity);
return;
}
foreach(Activity activity, _listOfIgnoredFiles){
if(activity._file == newActivity._file){
duplicate = true;
break;
}
}
if(!duplicate){
_notificationIgnoredFiles._message.append(", " + newActivity._file);
}
}
void ActivityListModel::addNotificationToActivityList(Activity activity) { void ActivityListModel::addNotificationToActivityList(Activity activity) {
qCInfo(lcActivity) << "Notification successfully added to the notification list: " << activity._subject; qCInfo(lcActivity) << "Notification successfully added to the notification list: " << activity._subject;
_notificationLists.prepend(activity); _notificationLists.prepend(activity);
@@ -268,7 +242,7 @@ void ActivityListModel::clearNotifications() {
} }
void ActivityListModel::removeActivityFromActivityList(int row) { void ActivityListModel::removeActivityFromActivityList(int row) {
Activity activity = _finalList.at(row); Activity activity = _finalList.at(row);
removeActivityFromActivityList(activity); removeActivityFromActivityList(activity);
combineActivityLists(); combineActivityLists();
} }
@@ -302,31 +276,22 @@ void ActivityListModel::removeActivityFromActivityList(Activity activity) {
} }
} }
void ActivityListModel::combineActivityLists() void ActivityListModel::combineActivityLists()
{ {
ActivityList resultList; ActivityList resultList;
if(_notificationErrorsLists.count() > 0) { std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end()); resultList.append(_notificationErrorsLists);
resultList.append(_notificationErrorsLists);
}
if(_listOfIgnoredFiles.size() > 0)
resultList.append(_notificationIgnoredFiles);
if(_notificationLists.count() > 0) { std::sort(_notificationLists.begin(), _notificationLists.end());
std::sort(_notificationLists.begin(), _notificationLists.end()); resultList.append(_notificationLists);
resultList.append(_notificationLists);
}
if(_syncFileItemLists.count() > 0) { std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end()); resultList.append(_syncFileItemLists);
resultList.append(_syncFileItemLists);
}
if(_activityLists.count() > 0) { std::sort(_activityLists.begin(), _activityLists.end());
std::sort(_activityLists.begin(), _activityLists.end()); resultList.append(_activityLists);
resultList.append(_activityLists);
}
beginResetModel(); beginResetModel();
_finalList.clear(); _finalList.clear();

View File

@@ -38,20 +38,6 @@ class ActivityListModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
enum ActivityIconType {
iconUseCached = 0,
iconActivity,
iconBell,
iconStateError,
iconStateWarning,
iconStateInfo,
iconStateSync
};
struct ActionIcon {
ActivityIconType iconType;
QIcon cachedIcon;
};
explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr); explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
@@ -65,7 +51,6 @@ public:
void addNotificationToActivityList(Activity activity); void addNotificationToActivityList(Activity activity);
void clearNotifications(); void clearNotifications();
void addErrorToActivityList(Activity activity); void addErrorToActivityList(Activity activity);
void addIgnoredFileToList(Activity newActivity);
void addSyncFileItemToActivityList(Activity activity); void addSyncFileItemToActivityList(Activity activity);
void removeActivityFromActivityList(int row); void removeActivityFromActivityList(int row);
void removeActivityFromActivityList(Activity activity); void removeActivityFromActivityList(Activity activity);
@@ -88,8 +73,6 @@ private:
ActivityList _activityLists; ActivityList _activityLists;
ActivityList _syncFileItemLists; ActivityList _syncFileItemLists;
ActivityList _notificationLists; ActivityList _notificationLists;
ActivityList _listOfIgnoredFiles;
Activity _notificationIgnoredFiles;
ActivityList _notificationErrorsLists; ActivityList _notificationErrorsLists;
ActivityList _finalList; ActivityList _finalList;
AccountState *_accountState; AccountState *_accountState;
@@ -98,7 +81,4 @@ private:
int _currentItem = 0; int _currentItem = 0;
}; };
} }
Q_DECLARE_METATYPE(OCC::ActivityListModel::ActionIcon)
#endif // ACTIVITYLISTMODEL_H #endif // ACTIVITYLISTMODEL_H

View File

@@ -89,9 +89,6 @@ ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
this, &ActivityWidget::addError); this, &ActivityWidget::addError);
_removeTimer.setInterval(1000); _removeTimer.setInterval(1000);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
connect(this, &ActivityWidget::styleChanged, delegate, &ActivityItemDelegate::slotStyleChanged);
} }
ActivityWidget::~ActivityWidget() ActivityWidget::~ActivityWidget()
@@ -130,12 +127,11 @@ void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo
} }
if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()) { if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity); _model->removeActivityFromActivityList(activity);
continue; continue;
} }
if(!QFileInfo(f->path() + activity._file).exists()){ if(!QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity); _model->removeActivityFromActivityList(activity);
continue; continue;
@@ -179,7 +175,7 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
Activity activity; Activity activity;
activity._type = Activity::SyncFileItemType; //client activity activity._type = Activity::SyncFileItemType; //client activity
activity._status = item->_status; activity._status = item->_status;
activity._dateTime = QDateTime::currentDateTime(); activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
activity._message = item->_originalFile; activity._message = item->_originalFile;
activity._link = folderInstance->accountState()->account()->url(); activity._link = folderInstance->accountState()->account()->url();
activity._accName = folderInstance->accountState()->account()->displayName(); activity._accName = folderInstance->accountState()->account()->displayName();
@@ -195,12 +191,8 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString; qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString;
activity._subject = item->_errorString; activity._subject = item->_errorString;
if(item->_status == SyncFileItem::Status::FileIgnored) { // add 'protocol error' to activity list
_model->addIgnoredFileToList(activity); _model->addErrorToActivityList(activity);
} else {
// add 'protocol error' to activity list
_model->addErrorToActivityList(activity);
}
} }
} }
} }
@@ -551,12 +543,6 @@ void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCod
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply; qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
} }
void ActivityWidget::slotStyleChanged()
{
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
}
/* ==================================================================== */ /* ==================================================================== */
ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent) ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
@@ -579,9 +565,6 @@ ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
// connect a model signal to stop the animation // connect a model signal to stop the animation
connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation); connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation);
connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities); connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
connect(this, &ActivitySettings::styleChanged, _activityWidget, &ActivityWidget::slotStyleChanged);
} }
void ActivitySettings::slotDisplayActivities(){ void ActivitySettings::slotDisplayActivities(){
@@ -640,14 +623,4 @@ bool ActivitySettings::event(QEvent *e)
ActivitySettings::~ActivitySettings() ActivitySettings::~ActivitySettings()
{ {
} }
void ActivitySettings::slotStyleChanged()
{
if(_progressIndicator)
_progressIndicator->setColor(QGuiApplication::palette().color(QPalette::Text));
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
}
} }

View File

@@ -78,14 +78,12 @@ public slots:
void addError(const QString &folderAlias, const QString &message, ErrorCategory category); void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
void slotProgressInfo(const QString &folder, const ProgressInfo &progress); void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
void slotStyleChanged();
signals: signals:
void guiLog(const QString &, const QString &); void guiLog(const QString &, const QString &);
void rowsInserted(); void rowsInserted();
void hideActivityTab(bool); void hideActivityTab(bool);
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row); void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
void styleChanged();
private slots: private slots:
void slotBuildNotificationDisplay(const ActivityList &list); void slotBuildNotificationDisplay(const ActivityList &list);
@@ -98,7 +96,6 @@ private slots:
void slotSecondaryButtonClickedOnListView(const QModelIndex &index); void slotSecondaryButtonClickedOnListView(const QModelIndex &index);
private: private:
void customizeStyle();
void showLabels(); void showLabels();
QString timeString(QDateTime dt, QLocale::FormatType format) const; QString timeString(QDateTime dt, QLocale::FormatType format) const;
Ui::ActivityWidget *_ui; Ui::ActivityWidget *_ui;
@@ -140,7 +137,6 @@ public slots:
void slotRefresh(); void slotRefresh();
void slotRemoveAccount(); void slotRemoveAccount();
void setNotificationRefreshInterval(std::chrono::milliseconds interval); void setNotificationRefreshInterval(std::chrono::milliseconds interval);
void slotStyleChanged();
private slots: private slots:
void slotRegularNotificationCheck(); void slotRegularNotificationCheck();
@@ -148,7 +144,6 @@ private slots:
signals: signals:
void guiLog(const QString &, const QString &); void guiLog(const QString &, const QString &);
void styleChanged();
private: private:
bool event(QEvent *e) override; bool event(QEvent *e) override;

View File

@@ -48,7 +48,7 @@
<item> <item>
<widget class="QPushButton" name="pushButtonBrowseCertificate"> <widget class="QPushButton" name="pushButtonBrowseCertificate">
<property name="text"> <property name="text">
<string>Browse</string> <string>Browse...</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -57,7 +57,7 @@
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="labelPWDCertificate"> <widget class="QLabel" name="labelPWDCertificate">
<property name="text"> <property name="text">
<string>Certificate password:</string> <string>Certificate password :</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@@ -108,7 +108,6 @@ Application::Application(int &argc, char **argv)
, _userTriggeredConnect(false) , _userTriggeredConnect(false)
, _debugMode(false) , _debugMode(false)
, _backgroundMode(false) , _backgroundMode(false)
, _isQuitting(false)
{ {
_startedAt.start(); _startedAt.start();
@@ -254,9 +253,6 @@ Application::Application(int &argc, char **argv)
// Cleanup at Quit. // Cleanup at Quit.
connect(this, &QCoreApplication::aboutToQuit, this, &Application::slotCleanup); connect(this, &QCoreApplication::aboutToQuit, this, &Application::slotCleanup);
// Allow other classes to hook into isShowingSettingsDialog() signals (re-auth widgets, for example)
connect(_gui.data(), &ownCloudGui::isShowingSettingsDialog, this, &Application::slotGuiIsShowingSettings);
} }
Application::~Application() Application::~Application()
@@ -268,8 +264,6 @@ Application::~Application()
} }
// Remove the account from the account manager so it can be deleted. // Remove the account from the account manager so it can be deleted.
disconnect(AccountManager::instance(), &AccountManager::accountRemoved,
this, &Application::slotAccountStateRemoved);
AccountManager::instance()->shutdown(); AccountManager::instance()->shutdown();
} }
@@ -289,7 +283,7 @@ void Application::slotAccountStateRemoved(AccountState *accountState)
} }
// if there is no more account, show the wizard. // if there is no more account, show the wizard.
if (!_isQuitting && AccountManager::instance()->accounts().isEmpty()) { if (AccountManager::instance()->accounts().isEmpty()) {
// allow to add a new account if there is non any more. Always think // allow to add a new account if there is non any more. Always think
// about single account theming! // about single account theming!
OwncloudSetupWizard::runWizard(this, SLOT(slotownCloudWizardDone(int))); OwncloudSetupWizard::runWizard(this, SLOT(slotownCloudWizardDone(int)));
@@ -312,8 +306,6 @@ void Application::slotAccountStateAdded(AccountState *accountState)
void Application::slotCleanup() void Application::slotCleanup()
{ {
_isQuitting = true;
AccountManager::instance()->save(); AccountManager::instance()->save();
FolderMan::instance()->unloadAndDeleteAllFolders(); FolderMan::instance()->unloadAndDeleteAllFolders();
@@ -651,9 +643,5 @@ void Application::showSettingsDialog()
_gui->slotShowSettings(); _gui->slotShowSettings();
} }
void Application::slotGuiIsShowingSettings()
{
emit isShowingSettingsDialog();
}
} // namespace OCC } // namespace OCC

View File

@@ -82,7 +82,6 @@ protected:
signals: signals:
void folderRemoved(); void folderRemoved();
void folderStateChanged(Folder *); void folderStateChanged(Folder *);
void isShowingSettingsDialog();
protected slots: protected slots:
void slotParseMessage(const QString &, QObject *); void slotParseMessage(const QString &, QObject *);
@@ -92,7 +91,6 @@ protected slots:
void slotAccountStateAdded(AccountState *accountState); void slotAccountStateAdded(AccountState *accountState);
void slotAccountStateRemoved(AccountState *accountState); void slotAccountStateRemoved(AccountState *accountState);
void slotSystemOnlineConfigurationChanged(QNetworkConfiguration); void slotSystemOnlineConfigurationChanged(QNetworkConfiguration);
void slotGuiIsShowingSettings();
private: private:
void setHelp(); void setHelp();
@@ -116,7 +114,6 @@ private:
bool _userTriggeredConnect; bool _userTriggeredConnect;
bool _debugMode; bool _debugMode;
bool _backgroundMode; bool _backgroundMode;
bool _isQuitting;
ClientProxy _proxy; ClientProxy _proxy;

View File

@@ -53,7 +53,7 @@ bool ClientProxy::isUsingSystemDefault()
return cfg.proxyType() == QNetworkProxy::DefaultProxy; return cfg.proxyType() == QNetworkProxy::DefaultProxy;
} }
return true; return false;
} }
QString printQNetworkProxy(const QNetworkProxy &proxy) QString printQNetworkProxy(const QNetworkProxy &proxy)

View File

@@ -52,8 +52,8 @@ void CloudProviderManager::registerSignals()
CloudProviderManager::CloudProviderManager(QObject *parent) : QObject(parent) CloudProviderManager::CloudProviderManager(QObject *parent) : QObject(parent)
{ {
_map = new QMap<QString, CloudProviderWrapper*>(); _map = new QMap<QString, CloudProviderWrapper*>();
_folder_index = 0; QString busName = QString(LIBCLOUDPROVIDERS_DBUS_BUS_NAME);
g_bus_own_name (G_BUS_TYPE_SESSION, LIBCLOUDPROVIDERS_DBUS_BUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, nullptr, on_name_acquired, nullptr, this, nullptr); g_bus_own_name (G_BUS_TYPE_SESSION, busName.toAscii().data(), G_BUS_NAME_OWNER_FLAGS_NONE, nullptr, on_name_acquired, nullptr, this, nullptr);
} }
void CloudProviderManager::slotFolderListChanged(const Folder::Map &folderMap) void CloudProviderManager::slotFolderListChanged(const Folder::Map &folderMap)
@@ -72,7 +72,7 @@ void CloudProviderManager::slotFolderListChanged(const Folder::Map &folderMap)
while (j.hasNext()) { while (j.hasNext()) {
j.next(); j.next();
if (!_map->contains(j.key())) { if (!_map->contains(j.key())) {
auto *cpo = new CloudProviderWrapper(this, j.value(), _folder_index++, _providerExporter); auto *cpo = new CloudProviderWrapper(this, j.value(), _providerExporter);
_map->insert(j.key(), cpo); _map->insert(j.key(), cpo);
} }
} }

View File

@@ -36,7 +36,6 @@ public slots:
private: private:
QMap<QString, CloudProviderWrapper*> *_map; QMap<QString, CloudProviderWrapper*> *_map;
unsigned int _folder_index;
}; };
#endif // CLOUDPROVIDERMANAGER_H #endif // CLOUDPROVIDERMANAGER_H

View File

@@ -33,13 +33,13 @@ using namespace OCC;
GSimpleActionGroup *actionGroup = nullptr; GSimpleActionGroup *actionGroup = nullptr;
CloudProviderWrapper::CloudProviderWrapper(QObject *parent, Folder *folder, int folderId, CloudProvidersProviderExporter* cloudprovider) : QObject(parent) CloudProviderWrapper::CloudProviderWrapper(QObject *parent, Folder *folder, CloudProvidersProviderExporter* cloudprovider) : QObject(parent)
, _folder(folder) , _folder(folder)
{ {
GMenuModel *model; GMenuModel *model;
GActionGroup *action_group; GActionGroup *action_group;
_recentlyChanged = new QList<QPair<QString, QString>>(); _recentlyChanged = new QList<QPair<QString, QString>>();
QString accountName = QString("Folder/%1").arg(folderId); QString accountName = QString("Account%1Folder%2").arg(folder->alias(), folder->accountState()->account()->id());
_cloudProvider = CLOUD_PROVIDERS_PROVIDER_EXPORTER(cloudprovider); _cloudProvider = CLOUD_PROVIDERS_PROVIDER_EXPORTER(cloudprovider);
_cloudProviderAccount = cloud_providers_account_exporter_new(_cloudProvider, accountName.toUtf8().data()); _cloudProviderAccount = cloud_providers_account_exporter_new(_cloudProvider, accountName.toUtf8().data());

View File

@@ -38,7 +38,7 @@ class CloudProviderWrapper : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CloudProviderWrapper(QObject *parent = nullptr, Folder *folder = nullptr, int folderId = 0, CloudProvidersProviderExporter* cloudprovider = nullptr); explicit CloudProviderWrapper(QObject *parent = nullptr, Folder *folder = nullptr, CloudProvidersProviderExporter* cloudprovider = nullptr);
~CloudProviderWrapper(); ~CloudProviderWrapper();
CloudProvidersAccountExporter* accountExporter(); CloudProvidersAccountExporter* accountExporter();
Folder* folder(); Folder* folder();

View File

@@ -22,8 +22,6 @@
#include "connectionvalidator.h" #include "connectionvalidator.h"
#include "account.h" #include "account.h"
#include "accountstate.h"
#include "userinfo.h"
#include "networkjobs.h" #include "networkjobs.h"
#include "clientproxy.h" #include "clientproxy.h"
#include <creds/abstractcredentials.h> #include <creds/abstractcredentials.h>
@@ -36,10 +34,9 @@ Q_LOGGING_CATEGORY(lcConnectionValidator, "nextcloud.sync.connectionvalidator",
// This makes sure we get tried often enough without "ConnectionValidator already running" // This makes sure we get tried often enough without "ConnectionValidator already running"
static qint64 timeoutToUseMsec = qMax(1000, ConnectionValidator::DefaultCallingIntervalMsec - 5 * 1000); static qint64 timeoutToUseMsec = qMax(1000, ConnectionValidator::DefaultCallingIntervalMsec - 5 * 1000);
ConnectionValidator::ConnectionValidator(AccountStatePtr accountState, QObject *parent) ConnectionValidator::ConnectionValidator(AccountPtr account, QObject *parent)
: QObject(parent) : QObject(parent)
, _accountState(accountState) , _account(account)
, _account(accountState->account())
, _isCheckingServerAndAuth(false) , _isCheckingServerAndAuth(false)
{ {
} }
@@ -47,7 +44,7 @@ ConnectionValidator::ConnectionValidator(AccountStatePtr accountState, QObject *
void ConnectionValidator::checkServerAndAuth() void ConnectionValidator::checkServerAndAuth()
{ {
if (!_account) { if (!_account) {
_errors << tr("No Nextcloud account configured"); _errors << tr("No ownCloud account configured");
reportResult(NotConfigured); reportResult(NotConfigured);
return; return;
} }
@@ -268,9 +265,10 @@ void ConnectionValidator::ocsConfigReceived(const QJsonDocument &json, AccountPt
void ConnectionValidator::fetchUser() void ConnectionValidator::fetchUser()
{ {
UserInfo *userInfo = new UserInfo(_accountState.data(), true, true, this); JsonApiJob *job = new JsonApiJob(_account, QLatin1String("ocs/v1.php/cloud/user"), this);
QObject::connect(userInfo, &UserInfo::fetchedLastInfo, this, &ConnectionValidator::slotUserFetched); job->setTimeout(timeoutToUseMsec);
userInfo->setActive(true); QObject::connect(job, &JsonApiJob::jsonReceived, this, &ConnectionValidator::slotUserFetched);
job->start();
} }
bool ConnectionValidator::setAndCheckServerVersion(const QString &version) bool ConnectionValidator::setAndCheckServerVersion(const QString &version)
@@ -302,22 +300,34 @@ bool ConnectionValidator::setAndCheckServerVersion(const QString &version)
return true; return true;
} }
void ConnectionValidator::slotUserFetched(UserInfo *userInfo) void ConnectionValidator::slotUserFetched(const QJsonDocument &json)
{ {
if(userInfo) { QString user = json.object().value("ocs").toObject().value("data").toObject().value("id").toString();
userInfo->setActive(false); if (!user.isEmpty()) {
userInfo->deleteLater(); _account->setDavUser(user);
}
QString displayName = json.object().value("ocs").toObject().value("data").toObject().value("display-name").toString();
if (!displayName.isEmpty()) {
_account->setDavDisplayName(displayName);
} }
#ifndef TOKEN_AUTH_ONLY #ifndef TOKEN_AUTH_ONLY
connect(_account->e2e(), &ClientSideEncryption::initializationFinished, this, &ConnectionValidator::reportConnected); AvatarJob *job = new AvatarJob(_account, _account->davUser(), 128, this);
_account->e2e()->initialize(); job->setTimeout(20 * 1000);
QObject::connect(job, &AvatarJob::avatarPixmap, this, &ConnectionValidator::slotAvatarImage);
job->start();
#else #else
reportResult(Connected); reportResult(Connected);
#endif #endif
} }
#ifndef TOKEN_AUTH_ONLY #ifndef TOKEN_AUTH_ONLY
void ConnectionValidator::slotAvatarImage(const QImage &img)
{
_account->setAvatar(img);
connect(_account->e2e(), &ClientSideEncryption::initializationFinished, this, &ConnectionValidator::reportConnected);
_account->e2e()->initialize();
}
void ConnectionValidator::reportConnected() { void ConnectionValidator::reportConnected() {
reportResult(Connected); reportResult(Connected);
} }

View File

@@ -67,20 +67,23 @@ namespace OCC {
+---------------------------------+ +---------------------------------+
| |
fetchUser fetchUser
Utilizes the UserInfo class to fetch the user and avatar image PropfindJob
|
+-> slotUserFetched
AvatarJob
|
+-> slotAvatarImage -->
+-----------------------------------+ +-----------------------------------+
| |
+-> Client Side Encryption Checks --+ --reportResult() +-> Client Side Encryption Checks --+ --reportResult()
\endcode \endcode
*/ */
class UserInfo;
class ConnectionValidator : public QObject class ConnectionValidator : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ConnectionValidator(AccountStatePtr accountState, QObject *parent = nullptr); explicit ConnectionValidator(AccountPtr account, QObject *parent = nullptr);
enum Status { enum Status {
Undefined, Undefined,
@@ -122,12 +125,13 @@ protected slots:
void slotAuthSuccess(); void slotAuthSuccess();
void slotCapabilitiesRecieved(const QJsonDocument &); void slotCapabilitiesRecieved(const QJsonDocument &);
void slotUserFetched(UserInfo *userInfo); void slotUserFetched(const QJsonDocument &);
#ifndef TOKEN_AUTH_ONLY
void slotAvatarImage(const QImage &img);
#endif
private: private:
#ifndef TOKEN_AUTH_ONLY
void reportConnected(); void reportConnected();
#endif
void reportResult(Status status); void reportResult(Status status);
void checkServerCapabilities(); void checkServerCapabilities();
void fetchUser(); void fetchUser();
@@ -140,7 +144,6 @@ private:
bool setAndCheckServerVersion(const QString &version); bool setAndCheckServerVersion(const QString &version);
QStringList _errors; QStringList _errors;
AccountStatePtr _accountState;
AccountPtr _account; AccountPtr _account;
bool _isCheckingServerAndAuth; bool _isCheckingServerAndAuth;
}; };

View File

@@ -14,12 +14,10 @@
*/ */
#include <QDesktopServices> #include <QDesktopServices>
#include <QApplication>
#include <QClipboard>
#include <QTimer> #include <QTimer>
#include <QBuffer> #include <QBuffer>
#include "account.h" #include "account.h"
#include "flow2auth.h" #include "creds/flow2auth.h"
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument> #include <QJsonDocument>
#include "theme.h" #include "theme.h"
@@ -30,17 +28,6 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg) Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg)
Flow2Auth::Flow2Auth(Account *account, QObject *parent)
: QObject(parent)
, _account(account)
, _isBusy(false)
, _hasToken(false)
{
_pollTimer.setInterval(1000);
QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
}
Flow2Auth::~Flow2Auth() Flow2Auth::~Flow2Auth()
{ {
} }
@@ -60,46 +47,22 @@ QUrl Flow2Auth::authorisationLink() const
void Flow2Auth::openBrowser() void Flow2Auth::openBrowser()
{ {
fetchNewToken(TokenAction::actionOpenBrowser); _pollTimer.stop();
}
void Flow2Auth::copyLinkToClipboard()
{
fetchNewToken(TokenAction::actionCopyLinkToClipboard);
}
void Flow2Auth::fetchNewToken(const TokenAction action)
{
if(_isBusy)
return;
_isBusy = true;
_hasToken = false;
emit statusChanged(PollStatus::statusFetchToken, 0);
// Step 1: Initiate a login, do an anonymous POST request // Step 1: Initiate a login, do an anonymous POST request
QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2")); QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
// add 'Content-Length: 0' header (see https://github.com/nextcloud/desktop/issues/1473) auto job = _account->sendRequest("POST", url);
QNetworkRequest req;
req.setHeader(QNetworkRequest::ContentLengthHeader, "0");
auto job = _account->sendRequest("POST", url, req);
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec())); job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this, action](QNetworkReply *reply) { QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](QNetworkReply *reply) {
auto jsonData = reply->readAll(); auto jsonData = reply->readAll();
QJsonParseError jsonParseError; QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object(); QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
QString pollToken, pollEndpoint, loginUrl;
if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError QString pollToken = json.value("poll").toObject().value("token").toString();
&& !json.isEmpty()) { QString pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
pollToken = json.value("poll").toObject().value("token").toString(); QUrl loginUrl = json["login"].toString();
pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
loginUrl = json["login"].toString();
}
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) { || json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) {
@@ -118,9 +81,7 @@ void Flow2Auth::fetchNewToken(const TokenAction action)
errorReason = tr("The reply from the server did not contain all expected fields"); errorReason = tr("The reply from the server did not contain all expected fields");
} }
qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason; qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason;
emit result(Error, errorReason); emit result(Error);
_pollTimer.stop();
_isBusy = false;
return; return;
} }
@@ -134,50 +95,23 @@ void Flow2Auth::fetchNewToken(const TokenAction action)
ConfigFile cfg; ConfigFile cfg;
std::chrono::milliseconds polltime = cfg.remotePollInterval(); std::chrono::milliseconds polltime = cfg.remotePollInterval();
qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec"; qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec";
_secondsInterval = (polltime.count() / 1000); _pollTimer.setInterval(polltime.count());
_secondsLeft = _secondsInterval; QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft); _pollTimer.start();
if(!_pollTimer.isActive()) {
_pollTimer.start(); // Try to open Browser
if (!QDesktopServices::openUrl(authorisationLink())) {
// We cannot open the browser, then we claim we don't support Flow2Auth.
// Our UI callee should ask the user to copy and open the link.
emit result(NotSupported, QString());
} }
switch(action)
{
case actionOpenBrowser:
// Try to open Browser
if (!QDesktopServices::openUrl(authorisationLink())) {
// We cannot open the browser, then we claim we don't support Flow2Auth.
// Our UI callee will ask the user to copy and open the link.
emit result(NotSupported);
}
break;
case actionCopyLinkToClipboard:
QApplication::clipboard()->setText(authorisationLink().toString(QUrl::FullyEncoded));
emit statusChanged(PollStatus::statusCopyLinkToClipboard, 0);
break;
}
_isBusy = false;
_hasToken = true;
}); });
} }
void Flow2Auth::slotPollTimerTimeout() void Flow2Auth::slotPollTimerTimeout()
{ {
if(_isBusy || !_hasToken) _pollTimer.stop();
return;
_isBusy = true;
_secondsLeft--;
if(_secondsLeft > 0) {
emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
_isBusy = false;
return;
}
emit statusChanged(PollStatus::statusPollNow, 0);
// Step 2: Poll // Step 2: Poll
QNetworkRequest req; QNetworkRequest req;
@@ -194,15 +128,10 @@ void Flow2Auth::slotPollTimerTimeout()
auto jsonData = reply->readAll(); auto jsonData = reply->readAll();
QJsonParseError jsonParseError; QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object(); QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
QUrl serverUrl;
QString loginName, appPassword;
if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError QUrl serverUrl = json["server"].toString();
&& !json.isEmpty()) { QString loginName = json["loginName"].toString();
serverUrl = json["server"].toString(); QString appPassword = json["appPassword"].toString();
loginName = json["loginName"].toString();
appPassword = json["appPassword"].toString();
}
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) { || json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) {
@@ -222,50 +151,26 @@ void Flow2Auth::slotPollTimerTimeout()
} }
qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason; qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason;
// We get a 404 until authentication is done, so don't show this error in the GUI.
if(reply->error() != QNetworkReply::ContentNotFoundError)
emit result(Error, errorReason);
// Forget sensitive data // Forget sensitive data
appPassword.clear(); appPassword.clear();
loginName.clear(); loginName.clear();
// Failed: poll again // Failed: poll again
_secondsLeft = _secondsInterval; _pollTimer.start();
_isBusy = false;
return; return;
} }
_pollTimer.stop();
// Success // Success
qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString(); qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString();
_account->setUrl(serverUrl); _account->setUrl(serverUrl);
emit result(LoggedIn, QString(), loginName, appPassword); emit result(LoggedIn, loginName, appPassword);
// Forget sensitive data // Forget sensitive data
appPassword.clear(); appPassword.clear();
loginName.clear(); loginName.clear();
_loginUrl.clear();
_pollToken.clear();
_pollEndpoint.clear();
_isBusy = false;
_hasToken = false;
}); });
} }
void Flow2Auth::slotPollNow()
{
// poll now if we're not already doing so
if(_isBusy || !_hasToken)
return;
_secondsLeft = 1;
slotPollTimerTimeout();
}
} // namespace OCC } // namespace OCC

View File

@@ -25,23 +25,17 @@ namespace OCC {
* Job that does the authorization, grants and fetches the access token via Login Flow v2 * Job that does the authorization, grants and fetches the access token via Login Flow v2
* *
* See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2 * See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
*
*/ */
class Flow2Auth : public QObject class Flow2Auth : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
enum TokenAction { Flow2Auth(Account *account, QObject *parent)
actionOpenBrowser = 1, : QObject(parent)
actionCopyLinkToClipboard , _account(account)
}; {
enum PollStatus { }
statusPollCountdown = 1,
statusPollNow,
statusFetchToken,
statusCopyLinkToClipboard
};
Flow2Auth(Account *account, QObject *parent);
~Flow2Auth(); ~Flow2Auth();
enum Result { NotSupported, enum Result { NotSupported,
@@ -50,7 +44,6 @@ public:
Q_ENUM(Result); Q_ENUM(Result);
void start(); void start();
void openBrowser(); void openBrowser();
void copyLinkToClipboard();
QUrl authorisationLink() const; QUrl authorisationLink() const;
signals: signals:
@@ -58,29 +51,18 @@ signals:
* The state has changed. * The state has changed.
* when logged in, appPassword has the value of the app password. * when logged in, appPassword has the value of the app password.
*/ */
void result(Flow2Auth::Result result, const QString &errorString = QString(), void result(Flow2Auth::Result result, const QString &user = QString(), const QString &appPassword = QString());
const QString &user = QString(), const QString &appPassword = QString());
void statusChanged(const PollStatus status, int secondsLeft);
public slots:
void slotPollNow();
private slots: private slots:
void slotPollTimerTimeout(); void slotPollTimerTimeout();
private: private:
void fetchNewToken(const TokenAction action);
Account *_account; Account *_account;
QUrl _loginUrl; QUrl _loginUrl;
QString _pollToken; QString _pollToken;
QString _pollEndpoint; QString _pollEndpoint;
QTimer _pollTimer; QTimer _pollTimer;
int _secondsLeft;
int _secondsInterval;
bool _isBusy;
bool _hasToken;
}; };
} // namespace OCC } // namespace OCC

View File

@@ -1,221 +0,0 @@
/*
* Copyright (C) by Michael Schuster <michael@nextcloud.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 "keychainchunk.h"
#include "theme.h"
#include "networkjobs.h"
#include "configfile.h"
#include "creds/abstractcredentials.h"
using namespace QKeychain;
namespace OCC {
Q_LOGGING_CATEGORY(lcKeychainChunk, "nextcloud.sync.credentials.keychainchunk", QtInfoMsg)
namespace KeychainChunk {
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
static void addSettingsToJob(Account *account, QKeychain::Job *job)
{
Q_UNUSED(account)
auto settings = ConfigFile::settingsWithGroup(Theme::instance()->appName());
settings->setParent(job); // make the job parent to make setting deleted properly
job->setSettings(settings.release());
}
#endif
/*
* Job
*/
Job::Job(QObject *parent)
: QObject(parent)
{
_serviceName = Theme::instance()->appName();
}
/*
* WriteJob
*/
WriteJob::WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent)
: Job(parent)
{
_account = account;
_key = key;
// Windows workaround: Split the private key into chunks of 2048 bytes,
// to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
_chunkBuffer = data;
_chunkCount = 0;
}
void WriteJob::start()
{
slotWriteJobDone(nullptr);
}
void WriteJob::slotWriteJobDone(QKeychain::Job *incomingJob)
{
QKeychain::WritePasswordJob *writeJob = static_cast<QKeychain::WritePasswordJob *>(incomingJob);
// errors?
if (writeJob) {
_error = writeJob->error();
_errorString = writeJob->errorString();
if (writeJob->error() != NoError) {
qCWarning(lcKeychainChunk) << "Error while writing" << writeJob->key() << "chunk" << writeJob->errorString();
_chunkBuffer.clear();
}
}
// write a chunk if there is any in the buffer
if (!_chunkBuffer.isEmpty()) {
#if defined(Q_OS_WIN)
// Windows workaround: Split the data into chunks of 2048 bytes,
// to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
auto chunk = _chunkBuffer.left(KeychainChunk::ChunkSize);
_chunkBuffer = _chunkBuffer.right(_chunkBuffer.size() - chunk.size());
#else
// write full data in one chunk on non-Windows, as usual
auto chunk = _chunkBuffer;
_chunkBuffer.clear();
#endif
auto index = (_chunkCount++);
// keep the limit
if (_chunkCount > KeychainChunk::MaxChunks) {
qCWarning(lcKeychainChunk) << "Maximum chunk count exceeded while writing" << writeJob->key() << "chunk" << QString::number(index) << "cutting off after" << QString::number(KeychainChunk::MaxChunks) << "chunks";
writeJob->deleteLater();
_chunkBuffer.clear();
emit finished(this);
return;
}
const QString kck = AbstractCredentials::keychainKey(
_account->url().toString(),
_key + (index > 0 ? (QString(".") + QString::number(index)) : QString()),
_account->id());
QKeychain::WritePasswordJob *job = new QKeychain::WritePasswordJob(_serviceName);
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(_insecureFallback);
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::WriteJob::slotWriteJobDone);
// only add the key's (sub)"index" after the first element, to stay compatible with older versions and non-Windows
job->setKey(kck);
job->setBinaryData(chunk);
job->start();
chunk.clear();
} else {
emit finished(this);
}
writeJob->deleteLater();
}
/*
* ReadJob
*/
ReadJob::ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent)
: Job(parent)
{
_account = account;
_key = key;
_keychainMigration = keychainMigration;
_chunkCount = 0;
_chunkBuffer.clear();
}
void ReadJob::start()
{
_chunkCount = 0;
_chunkBuffer.clear();
const QString kck = AbstractCredentials::keychainKey(
_account->url().toString(),
_key,
_keychainMigration ? QString() : _account->id());
QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(_insecureFallback);
job->setKey(kck);
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
job->start();
}
void ReadJob::slotReadJobDone(QKeychain::Job *incomingJob)
{
// Errors or next chunk?
QKeychain::ReadPasswordJob *readJob = static_cast<QKeychain::ReadPasswordJob *>(incomingJob);
if (readJob) {
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
_chunkBuffer.append(readJob->binaryData());
_chunkCount++;
#if defined(Q_OS_WIN)
// try to fetch next chunk
if (_chunkCount < KeychainChunk::MaxChunks) {
const QString kck = AbstractCredentials::keychainKey(
_account->url().toString(),
_key + QString(".") + QString::number(_chunkCount),
_keychainMigration ? QString() : _account->id());
QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(_insecureFallback);
job->setKey(kck);
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
job->start();
readJob->deleteLater();
return;
} else {
qCWarning(lcKeychainChunk) << "Maximum chunk count for" << readJob->key() << "reached, ignoring after" << KeychainChunk::MaxChunks;
}
#endif
} else {
if (readJob->error() != QKeychain::Error::EntryNotFound ||
((readJob->error() == QKeychain::Error::EntryNotFound) && _chunkCount == 0)) {
_error = readJob->error();
_errorString = readJob->errorString();
qCWarning(lcKeychainChunk) << "Unable to read" << readJob->key() << "chunk" << QString::number(_chunkCount) << readJob->errorString();
}
}
readJob->deleteLater();
}
emit finished(this);
}
} // namespace KeychainChunk
} // namespace OCC

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) by Michael Schuster <michael@nextcloud.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.
*/
#pragma once
#ifndef KEYCHAINCHUNK_H
#define KEYCHAINCHUNK_H
#include <QObject>
#include <keychain.h>
#include "accountfwd.h"
// We don't support insecure fallback
// #define KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK
namespace OCC {
namespace KeychainChunk {
/*
* Workaround for Windows:
*
* Split the keychain entry's data into chunks of 2048 bytes,
* to allow 4k (4096 bit) keys / large certs to be saved (see limits in webflowcredentials.h)
*/
static constexpr int ChunkSize = 2048;
static constexpr int MaxChunks = 10;
/*
* @brief: Abstract base class for KeychainChunk jobs.
*/
class Job : public QObject {
Q_OBJECT
public:
Job(QObject *parent = nullptr);
const QKeychain::Error error() const {
return _error;
}
const QString errorString() const {
return _errorString;
}
QByteArray binaryData() const {
return _chunkBuffer;
}
const bool insecureFallback() const {
return _insecureFallback;
}
// If we use it but don't support insecure fallback, give us nice compilation errors ;p
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
void setInsecureFallback(const bool &insecureFallback)
{
_insecureFallback = insecureFallback;
}
#endif
protected:
QString _serviceName;
Account *_account;
QString _key;
bool _insecureFallback = false;
bool _keychainMigration = false;
QKeychain::Error _error = QKeychain::NoError;
QString _errorString;
int _chunkCount = 0;
QByteArray _chunkBuffer;
}; // class Job
/*
* @brief: Simple wrapper class for QKeychain::WritePasswordJob, splits too large keychain entry's data into chunks on Windows
*/
class WriteJob : public KeychainChunk::Job {
Q_OBJECT
public:
WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent = nullptr);
void start();
signals:
void finished(KeychainChunk::WriteJob *incomingJob);
private slots:
void slotWriteJobDone(QKeychain::Job *incomingJob);
}; // class WriteJob
/*
* @brief: Simple wrapper class for QKeychain::ReadPasswordJob, splits too large keychain entry's data into chunks on Windows
*/
class ReadJob : public KeychainChunk::Job {
Q_OBJECT
public:
ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent = nullptr);
void start();
signals:
void finished(KeychainChunk::ReadJob *incomingJob);
private slots:
void slotReadJobDone(QKeychain::Job *incomingJob);
}; // class ReadJob
} // namespace KeychainChunk
} // namespace OCC
#endif // KEYCHAINCHUNK_H

View File

@@ -18,7 +18,6 @@
#include "theme.h" #include "theme.h"
#include "wizard/webview.h" #include "wizard/webview.h"
#include "webflowcredentialsdialog.h" #include "webflowcredentialsdialog.h"
#include "keychainchunk.h"
using namespace QKeychain; using namespace QKeychain;
@@ -76,7 +75,6 @@ private:
QPointer<const WebFlowCredentials> _cred; QPointer<const WebFlowCredentials> _cred;
}; };
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
static void addSettingsToJob(Account *account, QKeychain::Job *job) static void addSettingsToJob(Account *account, QKeychain::Job *job)
{ {
Q_UNUSED(account) Q_UNUSED(account)
@@ -84,7 +82,6 @@ static void addSettingsToJob(Account *account, QKeychain::Job *job)
settings->setParent(job); // make the job parent to make setting deleted properly settings->setParent(job); // make the job parent to make setting deleted properly
job->setSettings(settings.release()); job->setSettings(settings.release());
} }
#endif
WebFlowCredentials::WebFlowCredentials() WebFlowCredentials::WebFlowCredentials()
: _ready(false) : _ready(false)
@@ -150,32 +147,25 @@ void WebFlowCredentials::fetchFromKeychain() {
} }
void WebFlowCredentials::askFromUser() { void WebFlowCredentials::askFromUser() {
// Determine if the old flow has to be used (GS for now) // LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
// Do a DetermineAuthTypeJob to make sure that the server is still using Flow2 bool useFlow2 = (_account->serverVersionInt() >= Account::makeServerVersion(16, 0, 0));
DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_account->sharedFromThis(), this);
connect(job, &DetermineAuthTypeJob::authType, [this](DetermineAuthTypeJob::AuthType type) {
// LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
bool useFlow2 = (type != DetermineAuthTypeJob::WebViewFlow);
_askDialog = new WebFlowCredentialsDialog(_account, useFlow2); _askDialog = new WebFlowCredentialsDialog(_account, useFlow2);
if (!useFlow2) { if (!useFlow2) {
QUrl url = _account->url(); QUrl url = _account->url();
QString path = url.path() + "/index.php/login/flow"; QString path = url.path() + "/index.php/login/flow";
url.setPath(path); url.setPath(path);
_askDialog->setUrl(url); _askDialog->setUrl(url);
} }
QString msg = tr("You have been logged out of %1 as user %2. Please login again") QString msg = tr("You have been logged out of %1 as user %2. Please login again")
.arg(_account->displayName(), _user); .arg(_account->displayName(), _user);
_askDialog->setInfo(msg); _askDialog->setInfo(msg);
_askDialog->show(); _askDialog->show();
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided); connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
connect(_askDialog, &WebFlowCredentialsDialog::onClose, this, &WebFlowCredentials::slotAskFromUserCancelled);
});
job->start();
qCDebug(lcWebFlowCredentials()) << "User needs to reauth!"; qCDebug(lcWebFlowCredentials()) << "User needs to reauth!";
} }
@@ -183,11 +173,7 @@ void WebFlowCredentials::askFromUser() {
void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host) { void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host) {
Q_UNUSED(host) Q_UNUSED(host)
// Compare the re-entered username case-insensitive and save the new value (avoid breaking the account) if (_user != user) {
// See issue: https://github.com/nextcloud/desktop/issues/1741
if (QString::compare(_user, user, Qt::CaseInsensitive) == 0) {
_user = user;
} else {
qCInfo(lcWebFlowCredentials()) << "Authed with the wrong user!"; qCInfo(lcWebFlowCredentials()) << "Authed with the wrong user!";
QString msg = tr("Please login with the user: %1") QString msg = tr("Please login with the user: %1")
@@ -213,18 +199,10 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
emit asked(); emit asked();
_askDialog->close(); _askDialog->close();
_askDialog->deleteLater(); delete _askDialog;
_askDialog = nullptr; _askDialog = nullptr;
} }
void WebFlowCredentials::slotAskFromUserCancelled() {
qCDebug(lcWebFlowCredentials()) << "User cancelled reauth!";
emit asked();
_askDialog->deleteLater();
_askDialog = nullptr;
}
bool WebFlowCredentials::stillValid(QNetworkReply *reply) { bool WebFlowCredentials::stillValid(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
@@ -245,32 +223,33 @@ void WebFlowCredentials::persist() {
// write cert if there is one // write cert if there is one
if (!_clientSslCertificate.isNull()) { if (!_clientSslCertificate.isNull()) {
auto *job = new KeychainChunk::WriteJob(_account, WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
_user + clientCertificatePEMC, addSettingsToJob(_account, job);
_clientSslCertificate.toPem()); job->setInsecureFallback(false);
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone); connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC, _account->id()));
job->setBinaryData(_clientSslCertificate.toPem());
job->start(); job->start();
} else { } else {
// no cert, just write credentials // no cert, just write credentials
slotWriteClientCertPEMJobDone(nullptr); slotWriteClientCertPEMJobDone();
} }
} }
void WebFlowCredentials::slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob) void WebFlowCredentials::slotWriteClientCertPEMJobDone()
{ {
if(writeJob)
writeJob->deleteLater();
// write ssl key if there is one // write ssl key if there is one
if (!_clientSslKey.isNull()) { if (!_clientSslKey.isNull()) {
auto *job = new KeychainChunk::WriteJob(_account, WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
_user + clientKeyPEMC, addSettingsToJob(_account, job);
_clientSslKey.toPem()); job->setInsecureFallback(false);
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone); connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone);
job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC, _account->id()));
job->setBinaryData(_clientSslKey.toPem());
job->start(); job->start();
} else { } else {
// no key, just write credentials // no key, just write credentials
slotWriteClientKeyPEMJobDone(nullptr); slotWriteClientKeyPEMJobDone();
} }
} }
@@ -285,7 +264,7 @@ void WebFlowCredentials::writeSingleClientCaCertPEM()
// keep the limit // keep the limit
if (index > (_clientSslCaCertificatesMaxCount - 1)) { if (index > (_clientSslCaCertificatesMaxCount - 1)) {
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while writing slot" << QString::number(index) << "cutting off after" << QString::number(_clientSslCaCertificatesMaxCount) << "certs"; qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while writing slot" << QString::number(index) << "), cutting off after " << QString::number(_clientSslCaCertificatesMaxCount) << "certs";
_clientSslCaCertificatesWriteQueue.clear(); _clientSslCaCertificatesWriteQueue.clear();
@@ -293,21 +272,20 @@ void WebFlowCredentials::writeSingleClientCaCertPEM()
return; return;
} }
auto *job = new KeychainChunk::WriteJob(_account, WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
_user + clientCaCertificatePEMC + QString::number(index), addSettingsToJob(_account, job);
cert.toPem()); job->setInsecureFallback(false);
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone); connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
job->setKey(keychainKey(_account->url().toString(), _user + clientCaCertificatePEMC + QString::number(index), _account->id()));
job->setBinaryData(cert.toPem());
job->start(); job->start();
} else { } else {
slotWriteClientCaCertsPEMJobDone(nullptr); slotWriteClientCaCertsPEMJobDone(nullptr);
} }
} }
void WebFlowCredentials::slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob) void WebFlowCredentials::slotWriteClientKeyPEMJobDone()
{ {
if(writeJob)
writeJob->deleteLater();
_clientSslCaCertificatesWriteQueue.clear(); _clientSslCaCertificatesWriteQueue.clear();
// write ca certs if there are any // write ca certs if there are any
@@ -322,16 +300,16 @@ void WebFlowCredentials::slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *w
} }
} }
void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob) void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob)
{ {
// errors / next ca cert? // errors / next ca cert?
if (writeJob && !_clientSslCaCertificates.isEmpty()) { if (incomingJob && !_clientSslCaCertificates.isEmpty()) {
WritePasswordJob *writeJob = static_cast<WritePasswordJob *>(incomingJob);
if (writeJob->error() != NoError) { if (writeJob->error() != NoError) {
qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString(); qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString();
} }
writeJob->deleteLater();
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) { if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
// next ca cert // next ca cert
writeSingleClientCaCertPEM(); writeSingleClientCaCertPEM();
@@ -341,9 +319,7 @@ void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJo
// done storing ca certs, time for the password // done storing ca certs, time for the password
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName()); WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job); addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(false); job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone); connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone);
job->setKey(keychainKey(_account->url().toString(), _user, _account->id())); job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
@@ -382,8 +358,6 @@ void WebFlowCredentials::forgetSensitiveData() {
fetchUser(); fetchUser();
_account->deleteAppPassword();
const QString kck = keychainKey(_account->url().toString(), _user, _account->id()); const QString kck = keychainKey(_account->url().toString(), _user, _account->id());
if (kck.isEmpty()) { if (kck.isEmpty()) {
qCDebug(lcWebFlowCredentials()) << "InvalidateToken: User is empty, bailing out!"; qCDebug(lcWebFlowCredentials()) << "InvalidateToken: User is empty, bailing out!";
@@ -393,15 +367,18 @@ void WebFlowCredentials::forgetSensitiveData() {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName()); DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
job->setInsecureFallback(false); job->setInsecureFallback(false);
job->setKey(kck); job->setKey(kck);
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
djob->deleteLater();
});
job->start(); job->start();
invalidateToken(); invalidateToken();
deleteKeychainEntries(); /* IMPORTANT
/* 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.
*/
//deleteKeychainEntries();
} }
void WebFlowCredentials::setAccount(Account *account) { void WebFlowCredentials::setAccount(Account *account) {
@@ -439,31 +416,34 @@ void WebFlowCredentials::slotFinished(QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
_credentialsValid = true; _credentialsValid = true;
/// Used later for remote wipe
_account->writeAppPasswordOnce(_password);
} }
} }
void WebFlowCredentials::fetchFromKeychainHelper() { void WebFlowCredentials::fetchFromKeychainHelper() {
// Read client cert from keychain // Read client cert from keychain
auto *job = new KeychainChunk::ReadJob(_account, const QString kck = keychainKey(
_user + clientCertificatePEMC, _account->url().toString(),
_keychainMigration); _user + clientCertificatePEMC,
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone); _keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone);
job->start(); job->start();
} }
void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob) void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob)
{ {
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
Q_ASSERT(!readJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless Q_ASSERT(!incomingJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless
if (_retryOnKeyChainError && (readJob->error() == QKeychain::NoBackendAvailable if (_retryOnKeyChainError && (incomingJob->error() == QKeychain::NoBackendAvailable
|| readJob->error() == QKeychain::OtherError)) { || incomingJob->error() == QKeychain::OtherError)) {
// Could be that the backend was not yet available. Wait some extra seconds. // Could be that the backend was not yet available. Wait some extra seconds.
// (Issues #4274 and #6522) // (Issues #4274 and #6522)
// (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain) // (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain)
qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << readJob->errorString(); qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << incomingJob->errorString();
QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper); QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper);
_retryOnKeyChainError = false; _retryOnKeyChainError = false;
return; return;
@@ -472,6 +452,7 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *re
#endif #endif
// Store PEM in memory // Store PEM in memory
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
if (readJob->error() == NoError && readJob->binaryData().length() > 0) { if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem); QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
if (sslCertificateList.length() >= 1) { if (sslCertificateList.length() >= 1) {
@@ -479,19 +460,25 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *re
} }
} }
readJob->deleteLater();
// Load key too // Load key too
auto *job = new KeychainChunk::ReadJob(_account, const QString kck = keychainKey(
_user + clientKeyPEMC, _account->url().toString(),
_keychainMigration); _user + clientKeyPEMC,
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone); _keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
job->start(); job->start();
} }
void WebFlowCredentials::slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob) void WebFlowCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob)
{ {
// Store key in memory // Store key in memory
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
if (readJob->error() == NoError && readJob->binaryData().length() > 0) { if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QByteArray clientKeyPEM = readJob->binaryData(); QByteArray clientKeyPEM = readJob->binaryData();
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it // FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
@@ -506,13 +493,8 @@ void WebFlowCredentials::slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *rea
if (_clientSslKey.isNull()) { if (_clientSslKey.isNull()) {
qCWarning(lcWebFlowCredentials) << "Could not load SSL key into Qt!"; qCWarning(lcWebFlowCredentials) << "Could not load SSL key into Qt!";
} }
clientKeyPEM.clear();
} else {
qCWarning(lcWebFlowCredentials) << "Unable to read client key" << readJob->errorString();
} }
readJob->deleteLater();
// Start fetching client CA certs // Start fetching client CA certs
_clientSslCaCertificates.clear(); _clientSslCaCertificates.clear();
@@ -523,20 +505,28 @@ void WebFlowCredentials::readSingleClientCaCertPEM()
{ {
// try to fetch a client ca cert // try to fetch a client ca cert
if (_clientSslCaCertificates.count() < _clientSslCaCertificatesMaxCount) { if (_clientSslCaCertificates.count() < _clientSslCaCertificatesMaxCount) {
auto *job = new KeychainChunk::ReadJob(_account, const QString kck = keychainKey(
_user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()), _account->url().toString(),
_keychainMigration); _user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()),
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone); _keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone);
job->start(); job->start();
} else { } else {
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while reading, ignoring after" << _clientSslCaCertificatesMaxCount; qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while reading, ignoring after " << _clientSslCaCertificatesMaxCount;
slotReadClientCaCertsPEMJobDone(nullptr); slotReadClientCaCertsPEMJobDone(nullptr);
} }
} }
void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob) { void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomingJob) {
// Store cert in memory // Store key in memory
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
if (readJob) { if (readJob) {
if (readJob->error() == NoError && readJob->binaryData().length() > 0) { if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem); QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
@@ -544,19 +534,15 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob
_clientSslCaCertificates.append(sslCertificateList.at(0)); _clientSslCaCertificates.append(sslCertificateList.at(0));
} }
readJob->deleteLater();
// try next cert // try next cert
readSingleClientCaCertPEM(); readSingleClientCaCertPEM();
return; return;
} else { } else {
if (readJob->error() != QKeychain::Error::EntryNotFound || if (readJob->error() != QKeychain::Error::EntryNotFound ||
((readJob->error() == QKeychain::Error::EntryNotFound) && _clientSslCaCertificates.count() == 0)) { (readJob->error() == QKeychain::Error::EntryNotFound) && _clientSslCaCertificates.count() == 0) {
qCWarning(lcWebFlowCredentials) << "Unable to read client CA cert slot" << QString::number(_clientSslCaCertificates.count()) << readJob->errorString(); qCWarning(lcWebFlowCredentials) << "Unable to read client CA cert slot " << QString::number(_clientSslCaCertificates.count()) << readJob->errorString();
} }
} }
readJob->deleteLater();
} }
// Now fetch the actual server password // Now fetch the actual server password
@@ -566,9 +552,7 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob
_keychainMigration ? QString() : _account->id()); _keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName()); ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job); addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(false); job->setInsecureFallback(false);
job->setKey(kck); job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone); connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone);
@@ -576,7 +560,7 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob
} }
void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) { void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
QKeychain::ReadPasswordJob *job = qobject_cast<ReadPasswordJob *>(incomingJob); QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
QKeychain::Error error = job->error(); QKeychain::Error error = job->error();
// If we could not find the entry try the old entries // If we could not find the entry try the old entries
@@ -599,8 +583,6 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
} }
emit fetched(); emit fetched();
job->deleteLater();
// If keychain data was read from legacy location, wipe these entries and store new ones // If keychain data was read from legacy location, wipe these entries and store new ones
if (_keychainMigration && _ready) { if (_keychainMigration && _ready) {
_keychainMigration = false; _keychainMigration = false;
@@ -611,60 +593,23 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
} }
void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) { void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
auto startDeleteJob = [this, oldKeychainEntries](QString key) { auto startDeleteJob = [this, oldKeychainEntries](QString user) {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName()); DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job); addSettingsToJob(_account, job);
#endif job->setInsecureFallback(true);
job->setInsecureFallback(false);
job->setKey(keychainKey(_account->url().toString(), job->setKey(keychainKey(_account->url().toString(),
key, user,
oldKeychainEntries ? QString() : _account->id())); oldKeychainEntries ? QString() : _account->id()));
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
djob->deleteLater();
});
job->start(); job->start();
}; };
startDeleteJob(_user); startDeleteJob(_user);
startDeleteJob(_user + clientKeyPEMC);
startDeleteJob(_user + clientCertificatePEMC);
/* IMPORTANT - remove later - FIXME MS@2019-12-07 --> for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY! startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
*
* 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.
*/
if(_account->isRemoteWipeRequested_HACK()) {
// <-- FIXME MS@2019-12-07
startDeleteJob(_user + clientKeyPEMC);
startDeleteJob(_user + clientCertificatePEMC);
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
}
#if defined(Q_OS_WIN)
// Also delete key / cert sub-chunks (Windows workaround)
// The first chunk (0) has no suffix, to stay compatible with older versions and non-Windows
for (auto chunk = 1; chunk < KeychainChunk::MaxChunks; chunk++) {
const QString strChunkSuffix = QString(".") + QString::number(chunk);
startDeleteJob(_user + clientKeyPEMC + strChunkSuffix);
startDeleteJob(_user + clientCertificatePEMC + strChunkSuffix);
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
}
}
#endif
// FIXME MS@2019-12-07 -->
} }
// <-- FIXME MS@2019-12-07
} }
} // namespace OCC }

View File

@@ -19,11 +19,6 @@ namespace QKeychain {
namespace OCC { namespace OCC {
namespace KeychainChunk {
class ReadJob;
class WriteJob;
}
class WebFlowCredentialsDialog; class WebFlowCredentialsDialog;
class WebFlowCredentials : public AbstractCredentials class WebFlowCredentials : public AbstractCredentials
@@ -66,16 +61,15 @@ private slots:
void slotFinished(QNetworkReply *reply); void slotFinished(QNetworkReply *reply);
void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host); void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host);
void slotAskFromUserCancelled();
void slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob); void slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob);
void slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob); void slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob);
void slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob); void slotReadClientCaCertsPEMJobDone(QKeychain::Job *incommingJob);
void slotReadPasswordJobDone(QKeychain::Job *incomingJob); void slotReadPasswordJobDone(QKeychain::Job *incomingJob);
void slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob); void slotWriteClientCertPEMJobDone();
void slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob); void slotWriteClientKeyPEMJobDone();
void slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob); void slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob);
void slotWriteJobDone(QKeychain::Job *); void slotWriteJobDone(QKeychain::Job *);
private: private:
@@ -89,7 +83,7 @@ private:
void writeSingleClientCaCertPEM(); void writeSingleClientCaCertPEM();
/* /*
* Since we're limited by Windows limits, we just create our own * Since we're limited by Windows limits we just create our own
* limit to avoid evil things happening by endless recursion * limit to avoid evil things happening by endless recursion
* *
* Better than storing the count and relying on maybe-hacked values * Better than storing the count and relying on maybe-hacked values
@@ -127,6 +121,6 @@ protected:
WebFlowCredentialsDialog *_askDialog; WebFlowCredentialsDialog *_askDialog;
}; };
} // namespace OCC }
#endif // WEBFLOWCREDENTIALS_H #endif // WEBFLOWCREDENTIALS_H

View File

@@ -4,9 +4,6 @@
#include <QLabel> #include <QLabel>
#include "theme.h" #include "theme.h"
#include "application.h"
#include "owncloudgui.h"
#include "headerbanner.h"
#include "wizard/owncloudwizardcommon.h" #include "wizard/owncloudwizardcommon.h"
#include "wizard/webview.h" #include "wizard/webview.h"
#include "wizard/flow2authwidget.h" #include "wizard/flow2authwidget.h"
@@ -22,59 +19,31 @@ WebFlowCredentialsDialog::WebFlowCredentialsDialog(Account *account, bool useFlo
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_layout = new QVBoxLayout(this); _layout = new QVBoxLayout(this);
int spacing = _layout->spacing();
int margin = _layout->margin();
_layout->setSpacing(0);
_layout->setMargin(0);
if(_useFlow2) {
_headerBanner = new HeaderBanner(this);
_layout->addWidget(_headerBanner);
Theme *theme = Theme::instance();
_headerBanner->setup(tr("Log in"), theme->wizardHeaderLogo(), theme->wizardHeaderBanner(),
Qt::AutoText, QString::fromLatin1("color:#fff;"));
}
_containerLayout = new QVBoxLayout(this);
_containerLayout->setSpacing(spacing);
_containerLayout->setMargin(margin);
//QString msg = tr("You have been logged out of %1 as user %2, please login again")
// .arg(_account->displayName(), _user);
_infoLabel = new QLabel(); _infoLabel = new QLabel();
_containerLayout->addWidget(_infoLabel); _layout->addWidget(_infoLabel);
if (_useFlow2) { if (_useFlow2) {
_flow2AuthWidget = new Flow2AuthWidget(); _flow2AuthWidget = new Flow2AuthWidget(account);
_containerLayout->addWidget(_flow2AuthWidget); _layout->addWidget(_flow2AuthWidget);
connect(_flow2AuthWidget, &Flow2AuthWidget::authResult, this, &WebFlowCredentialsDialog::slotFlow2AuthResult); connect(_flow2AuthWidget, &Flow2AuthWidget::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
connect(this, &WebFlowCredentialsDialog::styleChanged, _flow2AuthWidget, &Flow2AuthWidget::slotStyleChanged);
// allow Flow2 page to poll on window activation
connect(this, &WebFlowCredentialsDialog::onActivate, _flow2AuthWidget, &Flow2AuthWidget::slotPollNow);
_flow2AuthWidget->startAuth(account);
} else { } else {
_webView = new WebView(); _webView = new WebView();
_containerLayout->addWidget(_webView); _layout->addWidget(_webView);
connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched); connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
} }
auto app = static_cast<Application *>(qApp);
connect(app, &Application::isShowingSettingsDialog, this, &WebFlowCredentialsDialog::slotShowSettingsDialog);
_errorLabel = new QLabel(); _errorLabel = new QLabel();
_errorLabel->hide(); _errorLabel->hide();
_containerLayout->addWidget(_errorLabel); _layout->addWidget(_errorLabel);
WizardCommon::initErrorLabel(_errorLabel); WizardCommon::initErrorLabel(_errorLabel);
_layout->addLayout(_containerLayout);
setLayout(_layout); setLayout(_layout);
customizeStyle();
} }
void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) { void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
@@ -83,17 +52,11 @@ void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
if (_webView) { if (_webView) {
// Force calling WebView::~WebView() earlier so that _profile and _page are // Force calling WebView::~WebView() earlier so that _profile and _page are
// deleted in the correct order. // deleted in the correct order.
_webView->deleteLater(); delete _webView;
_webView = nullptr;
} }
if (_flow2AuthWidget) { if (_flow2AuthWidget)
_flow2AuthWidget->resetAuth(); delete _flow2AuthWidget;
_flow2AuthWidget->deleteLater();
_flow2AuthWidget = nullptr;
}
emit onClose();
} }
void WebFlowCredentialsDialog::setUrl(const QUrl &url) { void WebFlowCredentialsDialog::setUrl(const QUrl &url) {
@@ -106,9 +69,6 @@ void WebFlowCredentialsDialog::setInfo(const QString &msg) {
} }
void WebFlowCredentialsDialog::setError(const QString &error) { void WebFlowCredentialsDialog::setError(const QString &error) {
// bring window to top
slotShowSettingsDialog();
if (_useFlow2 && _flow2AuthWidget) { if (_useFlow2 && _flow2AuthWidget) {
_flow2AuthWidget->setError(error); _flow2AuthWidget->setError(error);
return; return;
@@ -122,49 +82,4 @@ void WebFlowCredentialsDialog::setError(const QString &error) {
} }
} }
void WebFlowCredentialsDialog::changeEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::StyleChange:
case QEvent::PaletteChange:
case QEvent::ThemeChange:
customizeStyle();
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
break;
case QEvent::ActivationChange:
if(isActiveWindow())
emit onActivate();
break;
default:
break;
}
QDialog::changeEvent(e);
} }
void WebFlowCredentialsDialog::customizeStyle()
{
// HINT: Customize dialog's own style here, if necessary in the future (Dark-/Light-Mode switching)
}
void WebFlowCredentialsDialog::slotShowSettingsDialog()
{
// bring window to top but slightly delay, to avoid being hidden behind the SettingsDialog
QTimer::singleShot(100, this, [this] {
ownCloudGui::raiseDialog(this);
});
}
void WebFlowCredentialsDialog::slotFlow2AuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
{
if(r == Flow2Auth::LoggedIn) {
emit urlCatched(user, appPassword, QString());
} else {
// bring window to top
slotShowSettingsDialog();
}
}
} // namespace OCC

View File

@@ -5,14 +5,12 @@
#include <QUrl> #include <QUrl>
#include "accountfwd.h" #include "accountfwd.h"
#include "creds/flow2auth.h"
class QLabel; class QLabel;
class QVBoxLayout; class QVBoxLayout;
namespace OCC { namespace OCC {
class HeaderBanner;
class WebView; class WebView;
class Flow2AuthWidget; class Flow2AuthWidget;
@@ -32,21 +30,11 @@ public:
protected: protected:
void closeEvent(QCloseEvent * e) override; void closeEvent(QCloseEvent * e) override;
void changeEvent(QEvent *) override;
public slots:
void slotFlow2AuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
void slotShowSettingsDialog();
signals: signals:
void urlCatched(const QString user, const QString pass, const QString host); void urlCatched(const QString user, const QString pass, const QString host);
void styleChanged();
void onActivate();
void onClose();
private: private:
void customizeStyle();
bool _useFlow2; bool _useFlow2;
Flow2AuthWidget *_flow2AuthWidget; Flow2AuthWidget *_flow2AuthWidget;
@@ -55,10 +43,8 @@ private:
QLabel *_errorLabel; QLabel *_errorLabel;
QLabel *_infoLabel; QLabel *_infoLabel;
QVBoxLayout *_layout; QVBoxLayout *_layout;
QVBoxLayout *_containerLayout;
HeaderBanner *_headerBanner;
}; };
} // namespace OCC }
#endif // WEBFLOWCREDENTIALSDIALOG_H #endif // WEBFLOWCREDENTIALSDIALOG_H

View File

@@ -417,13 +417,6 @@ void Folder::createGuiLog(const QString &filename, LogStatus status, int count,
text = tr("%1 could not be synced due to an error. See the log for details.").arg(file); text = tr("%1 could not be synced due to an error. See the log for details.").arg(file);
} }
break; break;
case LogStatusFileLocked:
if (count > 1) {
text = tr("%1 and %n other file(s) are currently locked.", "", count -1).arg(file);
} else {
text = tr("%1 is currently locked.").arg(file);
}
break;
} }
if (!text.isEmpty()) { if (!text.isEmpty()) {
@@ -902,19 +895,13 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item)
} }
// add new directories or remove gone away dirs to the watcher // add new directories or remove gone away dirs to the watcher
if (_folderWatcher && item->isDirectory()) { if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_NEW) {
switch (item->_instruction) { if (_folderWatcher)
case CSYNC_INSTRUCTION_NEW: _folderWatcher->addPath(path() + item->_file);
_folderWatcher->addPath(path() + item->_file); }
break; if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
case CSYNC_INSTRUCTION_REMOVE: if (_folderWatcher)
_folderWatcher->removePath(path() + item->_file); _folderWatcher->removePath(path() + item->_file);
break;
case CSYNC_INSTRUCTION_RENAME:
_folderWatcher->removePath(path() + item->_file);
_folderWatcher->addPath(path() + item->destination());
break;
}
} }
// Success and failure of sync items adjust what the next sync is // Success and failure of sync items adjust what the next sync is
@@ -1085,7 +1072,7 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, bool *cancel
if (!cfgFile.promptDeleteFiles()) if (!cfgFile.promptDeleteFiles())
return; return;
QString msg = dir == SyncFileItem::Down ? tr("All files in the sync folder '%1' were deleted on the server.\n" QString msg = dir == SyncFileItem::Down ? tr("All files in the sync folder '%1' folder were deleted on the server.\n"
"These deletes will be synchronized to your local sync folder, making such files " "These deletes will be synchronized to your local sync folder, making such files "
"unavailable unless you have a right to restore. \n" "unavailable unless you have a right to restore. \n"
"If you decide to restore the files, they will be re-synced with the server if you have rights to do so.\n" "If you decide to restore the files, they will be re-synced with the server if you have rights to do so.\n"

View File

@@ -75,7 +75,7 @@ FolderMan::FolderMan(QObject *parent)
this, &FolderMan::slotScheduleFolderByTime); this, &FolderMan::slotScheduleFolderByTime);
_timeScheduler.start(); _timeScheduler.start();
connect(AccountManager::instance(), &AccountManager::removeAccountFolders, connect(AccountManager::instance(), &AccountManager::accountRemoved,
this, &FolderMan::slotRemoveFoldersForAccount); this, &FolderMan::slotRemoveFoldersForAccount);
connect(_lockWatcher.data(), &LockWatcher::fileUnlocked, connect(_lockWatcher.data(), &LockWatcher::fileUnlocked,
@@ -443,7 +443,7 @@ void FolderMan::slotFolderSyncPaused(Folder *f, bool paused)
void FolderMan::slotFolderCanSyncChanged() void FolderMan::slotFolderCanSyncChanged()
{ {
Folder *f = qobject_cast<Folder *>(sender()); Folder *f = qobject_cast<Folder *>(sender());
ASSERT(f); ASSERT(f);
if (f->canSync()) { if (f->canSync()) {
_socketApi->slotRegisterPath(f->alias()); _socketApi->slotRegisterPath(f->alias());
} else { } else {
@@ -562,7 +562,7 @@ void FolderMan::slotEtagJobDestroyed(QObject * /*o*/)
void FolderMan::slotRunOneEtagJob() void FolderMan::slotRunOneEtagJob()
{ {
if (_currentEtagJob.isNull()) { if (_currentEtagJob.isNull()) {
Folder *folder = nullptr; Folder *folder;
foreach (Folder *f, _folderMap) { foreach (Folder *f, _folderMap) {
if (f->etagJob()) { if (f->etagJob()) {
// Caveat: always grabs the first folder with a job, but we think this is Ok for now and avoids us having a seperate queue. // Caveat: always grabs the first folder with a job, but we think this is Ok for now and avoids us having a seperate queue.
@@ -1084,73 +1084,6 @@ bool FolderMan::startFromScratch(const QString &localFolder)
return true; return true;
} }
void FolderMan::slotWipeFolderForAccount(AccountState *accountState)
{
QVarLengthArray<Folder *, 16> foldersToRemove;
Folder::MapIterator i(_folderMap);
while (i.hasNext()) {
i.next();
Folder *folder = i.value();
if (folder->accountState() == accountState) {
foldersToRemove.append(folder);
}
}
bool success = false;
foreach (const auto &f, foldersToRemove) {
if (!f) {
qCCritical(lcFolderMan) << "Can not remove null folder";
return;
}
qCInfo(lcFolderMan) << "Removing " << f->alias();
const bool currentlyRunning = (_currentSyncFolder == f);
if (currentlyRunning) {
// abort the sync now
terminateSyncProcess();
}
if (_scheduledFolders.removeAll(f) > 0) {
emit scheduleQueueChanged();
}
// wipe database
f->wipe();
// wipe data
QDir userFolder(f->path());
if (userFolder.exists()) {
success = userFolder.removeRecursively();
if (!success) {
qCWarning(lcFolderMan) << "Failed to remove existing folder " << f->path();
} else {
qCInfo(lcFolderMan) << "wipe: Removed file " << f->path();
}
} else {
success = true;
qCWarning(lcFolderMan) << "folder does not exist, can not remove.";
}
f->setSyncPaused(true);
// remove the folder configuration
f->removeFromSettings();
unloadFolder(f);
if (currentlyRunning) {
delete f;
}
_navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
}
emit folderListChanged(_folderMap);
emit wipeDone(accountState, success);
}
void FolderMan::setDirtyProxy() void FolderMan::setDirtyProxy()
{ {
foreach (Folder *f, _folderMap.values()) { foreach (Folder *f, _folderMap.values()) {

View File

@@ -207,11 +207,6 @@ signals:
*/ */
void folderListChanged(const Folder::Map &); void folderListChanged(const Folder::Map &);
/**
* Emitted once slotRemoveFoldersForAccount is done wiping
*/
void wipeDone(AccountState *account, bool success);
public slots: public slots:
/** /**
@@ -236,9 +231,6 @@ public slots:
// slot to schedule an ETag job (from Folder only) // slot to schedule an ETag job (from Folder only)
void slotScheduleETagJob(const QString &alias, RequestEtagJob *job); void slotScheduleETagJob(const QString &alias, RequestEtagJob *job);
/** Wipe folder */
void slotWipeFolderForAccount(AccountState *accountState);
private slots: private slots:
void slotFolderSyncPaused(Folder *, bool paused); void slotFolderSyncPaused(Folder *, bool paused);
void slotFolderCanSyncChanged(); void slotFolderCanSyncChanged();

View File

@@ -40,7 +40,7 @@ namespace OCC {
FolderStatusDelegate::FolderStatusDelegate() FolderStatusDelegate::FolderStatusDelegate()
: QStyledItemDelegate() : QStyledItemDelegate()
{ {
customizeStyle(); m_moreIcon = QIcon(QLatin1String(":/client/resources/more.svg"));
} }
QString FolderStatusDelegate::addFolderText() QString FolderStatusDelegate::addFolderText()
@@ -168,7 +168,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
QString itemString = qvariant_cast<QString>(index.data(SyncProgressItemString)); QString itemString = qvariant_cast<QString>(index.data(SyncProgressItemString));
int warningCount = qvariant_cast<int>(index.data(WarningCount)); int warningCount = qvariant_cast<int>(index.data(WarningCount));
bool syncOngoing = qvariant_cast<bool>(index.data(SyncRunning)); bool syncOngoing = qvariant_cast<bool>(index.data(SyncRunning));
QDateTime syncDate = qvariant_cast<QDateTime>(index.data(SyncDate));
bool syncEnabled = qvariant_cast<bool>(index.data(FolderAccountConnected)); bool syncEnabled = qvariant_cast<bool>(index.data(FolderAccountConnected));
QRect iconRect = option.rect; QRect iconRect = option.rect;
@@ -253,7 +252,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
if (!showProgess) { if (!showProgess) {
painter->setFont(subFont); painter->setFont(subFont);
QString elidedRemotePathText = subFm.elidedText( QString elidedRemotePathText = subFm.elidedText(
tr("Synchronized with local folder (%1)").arg(syncDate.toTimeSpec(Qt::LocalTime).toString(Qt::SystemLocaleShortDate)), tr("Synchronized with local folder"),
Qt::ElideRight, remotePathRect.width()); Qt::ElideRight, remotePathRect.width());
painter->drawText(QStyle::visualRect(option.direction, option.rect, remotePathRect), painter->drawText(QStyle::visualRect(option.direction, option.rect, remotePathRect),
textAlign, elidedRemotePathText); textAlign, elidedRemotePathText);
@@ -273,11 +272,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
rect.setHeight(texts.count() * subFm.height() + 2 * margin); rect.setHeight(texts.count() * subFm.height() + 2 * margin);
rect.setRight(option.rect.right() - margin); rect.setRight(option.rect.right() - margin);
// save previous state to not mess up colours with the background (fixes issue: https://github.com/nextcloud/desktop/issues/1237)
auto oldBrush = painter->brush();
auto oldPen = painter->pen();
auto oldFont = painter->font();
painter->setBrush(color); painter->setBrush(color);
painter->setPen(QColor(0xaa, 0xaa, 0xaa)); painter->setPen(QColor(0xaa, 0xaa, 0xaa));
painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect), painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect),
@@ -295,11 +289,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
textRect.translate(0, textRect.height()); textRect.translate(0, textRect.height());
} }
// restore previous state
painter->setBrush(oldBrush);
painter->setPen(oldPen);
painter->setFont(oldFont);
h = rect.bottom() + margin; h = rect.bottom() + margin;
}; };
@@ -359,7 +348,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
btnOpt.arrowType = Qt::NoArrow; btnOpt.arrowType = Qt::NoArrow;
btnOpt.subControls = QStyle::SC_ToolButton; btnOpt.subControls = QStyle::SC_ToolButton;
btnOpt.rect = optionsButtonVisualRect; btnOpt.rect = optionsButtonVisualRect;
btnOpt.icon = _iconMore; btnOpt.icon = m_moreIcon;
int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize); int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
btnOpt.iconSize = QSize(e,e); btnOpt.iconSize = QSize(e,e);
QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter); QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter);
@@ -433,14 +422,5 @@ QRect FolderStatusDelegate::errorsListRect(QRect within)
return within; return within;
} }
void FolderStatusDelegate::slotStyleChanged()
{
customizeStyle();
}
void FolderStatusDelegate::customizeStyle()
{
_iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
}
} // namespace OCC } // namespace OCC

View File

@@ -26,6 +26,7 @@ class FolderStatusDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
public: public:
QIcon m_moreIcon;
FolderStatusDelegate(); FolderStatusDelegate();
enum datarole { FolderAliasRole = Qt::UserRole + 100, enum datarole { FolderAliasRole = Qt::UserRole + 100,
@@ -43,7 +44,6 @@ public:
SyncProgressItemString, SyncProgressItemString,
WarningCount, WarningCount,
SyncRunning, SyncRunning,
SyncDate,
AddButton // 1 = enabled; 2 = disabled AddButton // 1 = enabled; 2 = disabled
}; };
@@ -61,16 +61,9 @@ public:
static QRect errorsListRect(QRect within); static QRect errorsListRect(QRect within);
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm); static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
public slots:
void slotStyleChanged();
private: private:
void customizeStyle();
static QString addFolderText(); static QString addFolderText();
QPersistentModelIndex _pressedIndex; QPersistentModelIndex _pressedIndex;
QIcon _iconMore;
}; };
} // namespace OCC } // namespace OCC

View File

@@ -186,7 +186,7 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
return QVariant(tr("Error while loading the list of folders from the server.") return QVariant(tr("Error while loading the list of folders from the server.")
+ QString("\n") + x->_lastErrorString); + QString("\n") + x->_lastErrorString);
} else { } else {
return tr("Fetching folder list from server"); return tr("Fetching folder list from server...");
} }
break; break;
default: default:
@@ -218,8 +218,6 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
return f->syncResult().errorStrings(); return f->syncResult().errorStrings();
case FolderStatusDelegate::SyncRunning: case FolderStatusDelegate::SyncRunning:
return f->syncResult().status() == SyncResult::SyncRunning; return f->syncResult().status() == SyncResult::SyncRunning;
case FolderStatusDelegate::SyncDate:
return f->syncResult().syncTime();
case FolderStatusDelegate::HeaderRole: case FolderStatusDelegate::HeaderRole:
return f->shortGuiRemotePathOrAppName(); return f->shortGuiRemotePathOrAppName();
case FolderStatusDelegate::FolderAliasRole: case FolderStatusDelegate::FolderAliasRole:
@@ -1099,15 +1097,15 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
} }
QString message; QString message;
if (pos <= 0) { if (pos <= 0) {
message = tr("Waiting"); message = tr("Waiting...");
} else { } else {
message = tr("Waiting for %n other folder(s)", "", pos); message = tr("Waiting for %n other folder(s)...", "", pos);
} }
pi = SubFolderInfo::Progress(); pi = SubFolderInfo::Progress();
pi._overallSyncString = message; pi._overallSyncString = message;
} else if (state == SyncResult::SyncPrepare) { } else if (state == SyncResult::SyncPrepare) {
pi = SubFolderInfo::Progress(); pi = SubFolderInfo::Progress();
pi._overallSyncString = tr("Preparing to sync"); pi._overallSyncString = tr("Preparing to sync...");
} else if (state == SyncResult::Problem || state == SyncResult::Success) { } else if (state == SyncResult::Problem || state == SyncResult::Success) {
// Reset the progress info after a sync. // Reset the progress info after a sync.
pi = SubFolderInfo::Progress(); pi = SubFolderInfo::Progress();

View File

@@ -19,8 +19,6 @@
#include "folderwatcher.h" #include "folderwatcher.h"
#include "folderwatcher_win.h" #include "folderwatcher_win.h"
#include "common/utility.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <tchar.h> #include <tchar.h>
@@ -54,7 +52,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
// QVarLengthArray ensures the stack-buffer is aligned like double and qint64. // QVarLengthArray ensures the stack-buffer is aligned like double and qint64.
QVarLengthArray<char, 4096 * 10> fileNotifyBuffer; QVarLengthArray<char, 4096 * 10> fileNotifyBuffer;
fileNotifyBuffer.resize(OCC::Utility::convertSizeToInt(fileNotifyBufferSize)); fileNotifyBuffer.resize(fileNotifyBufferSize);
const size_t fileNameBufferSize = 4096; const size_t fileNameBufferSize = 4096;
TCHAR fileNameBuffer[fileNameBufferSize]; TCHAR fileNameBuffer[fileNameBufferSize];
@@ -68,7 +66,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
DWORD dwBytesReturned = 0; DWORD dwBytesReturned = 0;
SecureZeroMemory(pFileNotifyBuffer, fileNotifyBufferSize); SecureZeroMemory(pFileNotifyBuffer, fileNotifyBufferSize);
if (!ReadDirectoryChangesW(_directory, (LPVOID)pFileNotifyBuffer, if (!ReadDirectoryChangesW(_directory, (LPVOID)pFileNotifyBuffer,
OCC::Utility::convertSizeToDWORD(fileNotifyBufferSize), true, fileNotifyBufferSize, true,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytesReturned, &dwBytesReturned,
&overlapped, &overlapped,
@@ -115,7 +113,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer; FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer;
forever { forever {
size_t len = curEntry->FileNameLength / 2; size_t len = curEntry->FileNameLength / 2;
QString file = _path + "\\" + QString::fromWCharArray(curEntry->FileName, OCC::Utility::convertSizeToInt(len)); QString file = _path + "\\" + QString::fromWCharArray(curEntry->FileName, len);
// Unless the file was removed or renamed, get its full long name // Unless the file was removed or renamed, get its full long name
// TODO: We could still try expanding the path in the tricky cases... // TODO: We could still try expanding the path in the tricky cases...
@@ -124,7 +122,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
&& curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) { && curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) {
size_t longNameSize = GetLongPathNameW(reinterpret_cast<LPCWSTR>(file.utf16()), fileNameBuffer, fileNameBufferSize); size_t longNameSize = GetLongPathNameW(reinterpret_cast<LPCWSTR>(file.utf16()), fileNameBuffer, fileNameBufferSize);
if (longNameSize > 0) { if (longNameSize > 0) {
longfile = QString::fromUtf16(reinterpret_cast<const ushort *>(fileNameBuffer), OCC::Utility::convertSizeToInt(longNameSize)); longfile = QString::fromUtf16(reinterpret_cast<const ushort *>(fileNameBuffer), longNameSize);
} else { } else {
qCWarning(lcFolderWatcher) << "Error converting file name to full length, keeping original name."; qCWarning(lcFolderWatcher) << "Error converting file name to full length, keeping original name.";
} }

View File

@@ -41,7 +41,7 @@
<item> <item>
<widget class="QPushButton" name="localFolderChooseBtn"> <widget class="QPushButton" name="localFolderChooseBtn">
<property name="text"> <property name="text">
<string>&amp;Choose</string> <string>&amp;Choose...</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@@ -107,7 +107,7 @@
<enum>QFrame::Plain</enum> <enum>QFrame::Plain</enum>
</property> </property>
<property name="text"> <property name="text">
<string notr="true">TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>
@@ -140,7 +140,7 @@
<item row="1" column="1"> <item row="1" column="1">
<widget class="QPushButton" name="addFolderButton"> <widget class="QPushButton" name="addFolderButton">
<property name="text"> <property name="text">
<string>Create folder</string> <string>Create Folder</string>
</property> </property>
</widget> </widget>
</item> </item>

Some files were not shown because too many files have changed in this diff Show More