mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-03 09:11:33 +02:00
Compare commits
112 Commits
v2.5.3-rc2
...
techprevie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1667f48d3 | ||
|
|
33f9ae827f | ||
|
|
ef4541f834 | ||
|
|
1b6a9bf128 | ||
|
|
7956f2846b | ||
|
|
12a5f2d16a | ||
|
|
817e92ab7a | ||
|
|
f4cd3b95c5 | ||
|
|
a106f89d29 | ||
|
|
322b2fe3a5 | ||
|
|
33520fdee8 | ||
|
|
706cfe88d0 | ||
|
|
653c10be86 | ||
|
|
b38b821480 | ||
|
|
723b8907f2 | ||
|
|
27d639a986 | ||
|
|
8499df8a7c | ||
|
|
80ba3adc33 | ||
|
|
c149465f80 | ||
|
|
cd1c5922d1 | ||
|
|
483630667b | ||
|
|
f746c74bf8 | ||
|
|
63a591b5fd | ||
|
|
8ba1fdb642 | ||
|
|
5bb63b93cc | ||
|
|
1c1111fb7b | ||
|
|
cd6225d418 | ||
|
|
848993baab | ||
|
|
06b32222a4 | ||
|
|
37028c7e19 | ||
|
|
3536c55ae4 | ||
|
|
f337125818 | ||
|
|
80dfe2a703 | ||
|
|
c89d3d97bc | ||
|
|
9035995cda | ||
|
|
e7470fd57b | ||
|
|
aba6ebece7 | ||
|
|
ffa35c183c | ||
|
|
f848e21768 | ||
|
|
8ca06ff0ec | ||
|
|
b886bb9c15 | ||
|
|
181bbebd6f | ||
|
|
98c8fa21c9 | ||
|
|
6eb4986e11 | ||
|
|
c19ecb3c8b | ||
|
|
4d8dfae25f | ||
|
|
dbe3b662db | ||
|
|
4d57f27c51 | ||
|
|
79a2b0488f | ||
|
|
73e79fd40f | ||
|
|
59ed4f3fa7 | ||
|
|
6b050b9c50 | ||
|
|
e100ae15d2 | ||
|
|
bcb0f2cd62 | ||
|
|
ff62968cb8 | ||
|
|
d9ca247db3 | ||
|
|
9235c7cc7f | ||
|
|
3344e18cf0 | ||
|
|
9667421cff | ||
|
|
8db3b4b790 | ||
|
|
7a842d14ad | ||
|
|
ea0a49be67 | ||
|
|
15f7b7641c | ||
|
|
1392b81e49 | ||
|
|
d13c0ce6f2 | ||
|
|
5f974b91ac | ||
|
|
22ee266f4d | ||
|
|
9fc90a5aeb | ||
|
|
363d16d02f | ||
|
|
1fa89d0d76 | ||
|
|
afbaef3f75 | ||
|
|
144852b023 | ||
|
|
7f147d414e | ||
|
|
522068a45f | ||
|
|
196ff7d692 | ||
|
|
5a0b954308 | ||
|
|
5dfc577347 | ||
|
|
ed8a9b3d03 | ||
|
|
b3c07d4b27 | ||
|
|
0bca618c08 | ||
|
|
2e7955ee15 | ||
|
|
e253499d42 | ||
|
|
389ad46eee | ||
|
|
600f35f777 | ||
|
|
967653d081 | ||
|
|
2e4c531604 | ||
|
|
b683f7c893 | ||
|
|
6b2c6c8aec | ||
|
|
0206911639 | ||
|
|
30fb13efca | ||
|
|
0a4476a1fc | ||
|
|
6dff1bafcc | ||
|
|
5335995f0e | ||
|
|
189812d05a | ||
|
|
14c3aeac52 | ||
|
|
2c922e80a7 | ||
|
|
552c8c703c | ||
|
|
cbc5ab2a30 | ||
|
|
b2964c14d5 | ||
|
|
306a5d1fd4 | ||
|
|
be35a173b2 | ||
|
|
5b6ce89f51 | ||
|
|
5b21683bc8 | ||
|
|
6c80f9ed1e | ||
|
|
55aa3d8d78 | ||
|
|
534f246a83 | ||
|
|
3728ba5388 | ||
|
|
0d59aee335 | ||
|
|
45745e7dc7 | ||
|
|
1f456061b6 | ||
|
|
28760f7b1b | ||
|
|
eeb455c54f |
@@ -174,6 +174,8 @@ endif()
|
||||
if(BUILD_CLIENT)
|
||||
if(APPLE)
|
||||
find_package(Sparkle)
|
||||
find_package(FUSE REQUIRED)
|
||||
include_directories(BEFORE ${FUSE_INCLUDE_DIR})
|
||||
endif(APPLE)
|
||||
|
||||
if(UNIX)
|
||||
@@ -183,7 +185,7 @@ if(BUILD_CLIENT)
|
||||
endif()
|
||||
find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
find_package(OpenSSL REQUIRED VERSION 1.1)
|
||||
find_package(OpenSSL 1.1 REQUIRED)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(GLib2)
|
||||
@@ -243,7 +245,9 @@ configure_file(version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||
if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
install(FILES sync-exclude.lst DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/)
|
||||
configure_file(sync-exclude.lst bin/${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/sync-exclude.lst COPYONLY)
|
||||
configure_file(LoopbackFS.icns bin/${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/LoopbackFS.icns COPYONLY)
|
||||
elseif(BUILD_CLIENT)
|
||||
install( FILES sync-exclude.lst DESTINATION ${SYSCONFDIR}/${APPLICATION_SHORTNAME} )
|
||||
configure_file(sync-exclude.lst bin/sync-exclude.lst COPYONLY)
|
||||
configure_file(LoopbackFS.icns bin/LoopbackFS.icns COPYONLY)
|
||||
endif()
|
||||
|
||||
BIN
LoopbackFS.icns
Normal file
BIN
LoopbackFS.icns
Normal file
Binary file not shown.
@@ -21,4 +21,4 @@ option( WITH_CRASHREPORTER "Build crashreporter" OFF )
|
||||
#set( CRASHREPORTER_SUBMIT_URL "https://crash-reports.owncloud.com/submit" CACHE string "URL for crash reporter" )
|
||||
#set( CRASHREPORTER_ICON ":/owncloud-icon.png" )
|
||||
|
||||
option( WITH_PROVIDERS "Build with providers list" OFF )
|
||||
option( WITH_PROVIDERS "Build with providers list" ON )
|
||||
|
||||
@@ -19,7 +19,7 @@ if(APPLE)
|
||||
set( CPACK_GENERATOR "DragNDrop" )
|
||||
set( CPACK_SOURCE_GENERATOR "")
|
||||
set( CPACK_PACKAGE_FILE_NAME ${APPLICATION_SHORTNAME}-${CPACK_PACKAGE_VERSION} )
|
||||
set( CPACK_PACKAGE_ICON ${CMAKE_BINARY_DIR}/src/gui/ownCloud.icns)
|
||||
set( CPACK_PACKAGE_ICON ${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns)
|
||||
|
||||
set( CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/admin/osx/DS_Store.in")
|
||||
# set( CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/admin/osx/DMGBackground.png" )
|
||||
|
||||
@@ -16,3 +16,5 @@ configure_file(create_mac.sh.cmake ${CMAKE_CURRENT_BINARY_DIR}/create_mac.sh)
|
||||
configure_file(macosx.pkgproj.cmake ${CMAKE_CURRENT_BINARY_DIR}/macosx.pkgproj)
|
||||
configure_file(pre_install.sh.cmake ${CMAKE_CURRENT_BINARY_DIR}/pre_install.sh)
|
||||
configure_file(post_install.sh.cmake ${CMAKE_CURRENT_BINARY_DIR}/post_install.sh)
|
||||
configure_file(fuse/FUSE\ for\ macOS\ 3.8.1.pkg ${CMAKE_CURRENT_BINARY_DIR}/fuse/FUSE\ for\ macOS\ 3.8.1.pkg COPYONLY)
|
||||
configure_file(fuse/settings.plist ${CMAKE_CURRENT_BINARY_DIR}/fuse/settings.plist COPYONLY)
|
||||
|
||||
8
admin/osx/fuse/FUSE for Linux Website.webloc
Normal file
8
admin/osx/fuse/FUSE for Linux Website.webloc
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>URL</key>
|
||||
<string>https://github.com/libfuse/libfuse</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
admin/osx/fuse/FUSE for macOS 3.8.1.pkg
Executable file
BIN
admin/osx/fuse/FUSE for macOS 3.8.1.pkg
Executable file
Binary file not shown.
8
admin/osx/fuse/FUSE for macOS Website.webloc
Normal file
8
admin/osx/fuse/FUSE for macOS Website.webloc
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>URL</key>
|
||||
<string>https://osxfuse.github.io</string>
|
||||
</dict>
|
||||
</plist>
|
||||
35
admin/osx/fuse/Uninstaller.app/Contents/Info.plist
Normal file
35
admin/osx/fuse/Uninstaller.app/Contents/Info.plist
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Uninstaller</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>applet</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Uninstaller</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Uninstaller</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>aplt</string>
|
||||
<key>LSMinimumSystemVersionByArchitecture</key>
|
||||
<dict>
|
||||
<key>x86_64</key>
|
||||
<string>10.6</string>
|
||||
</dict>
|
||||
<key>LSRequiresCarbon</key>
|
||||
<true/>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © Benjamin Fleischer. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
admin/osx/fuse/Uninstaller.app/Contents/MacOS/applet
Executable file
BIN
admin/osx/fuse/Uninstaller.app/Contents/MacOS/applet
Executable file
Binary file not shown.
1
admin/osx/fuse/Uninstaller.app/Contents/PkgInfo
Normal file
1
admin/osx/fuse/Uninstaller.app/Contents/PkgInfo
Normal file
@@ -0,0 +1 @@
|
||||
APPLaplt
|
||||
@@ -0,0 +1,10 @@
|
||||
"welcome_text" = "Remove FUSE for macOS?";
|
||||
"welcome_message" = "In case FUSE for macOS was installed using a package manager (e.g. Homebrew, MacPorts), please follow the instructions provided by the package manager for removing FUSE for macOS.\n\nIt is recommended to restart the Mac once the uninstallation is complete. Would you like to continue?";
|
||||
"welcome_no" = "No";
|
||||
"welcome_yes" = "Yes";
|
||||
|
||||
"uninstaller_successful_text" = "Uninstallation successful";
|
||||
"uninstaller_successful_message" = "FUSE for macOS has been sucessfully removed.";
|
||||
|
||||
"uninstaller_failed_text" = "Uninstallation failed";
|
||||
"uninstaller_failed_message" = "FUSE for macOS could not be removed due to an unknown error.";
|
||||
@@ -0,0 +1,10 @@
|
||||
"welcome_text" = "FUSE for macOS entfernen?";
|
||||
"welcome_message" = "Falls FUSE for macOS mit Hilfe eines Paket Managers (z.B. Homebrew, MacPorts) installiert wurde, folgen Sie bitte den Anweisungen des Paket Managers, um FUSE for macOS zu entfernen.\n\nEs wird empfohlen den Mac nach Abschluss der Deinstallation neu zu starten. Möchten Sie fortfahren?";
|
||||
"welcome_no" = "Nein";
|
||||
"welcome_yes" = "Ja";
|
||||
|
||||
"uninstaller_successful_text" = "Deinstallation erfolgreich";
|
||||
"uninstaller_successful_message" = "FUSE for macOS wurde erfolgreich entfernt.";
|
||||
|
||||
"uninstaller_failed_text" = "Deinstallation fehlgeschlagen";
|
||||
"uninstaller_failed_message" = "Bei der Deinstallation von FUSE for macOS ist ein unbekannter Fehler aufgetreten.";
|
||||
247
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/array.sh
Executable file
247
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/array.sh
Executable file
@@ -0,0 +1,247 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2011-2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Requires common.sh
|
||||
# Requires math.sh
|
||||
# Requires string.sh
|
||||
|
||||
|
||||
function array_is_array
|
||||
{
|
||||
if common_is_variable "${1}"
|
||||
then
|
||||
[[ "`declare -p "${1}" 2> /dev/null`" =~ ^"declare -"[^=]{0,}"a"[^=]{0,}" ${1}=" ]]
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function array_create
|
||||
{
|
||||
common_assert "common_variable_is_legal_name `string_escape "${1}"`"
|
||||
|
||||
eval "${1}=()"
|
||||
}
|
||||
|
||||
function array_size
|
||||
{
|
||||
common_assert "array_is_array `string_escape "${1}"`"
|
||||
|
||||
eval "printf \"%u\" \${#${1}[@]}"
|
||||
}
|
||||
|
||||
function array_get
|
||||
{
|
||||
if [[ -z "${3}" ]]
|
||||
then
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
common_assert "math_is_integer `string_escape "${2}"` && [[ ${2} -ge 0 ]]"
|
||||
|
||||
eval "string_escape \"\${${1}[${2}]}\""
|
||||
else
|
||||
common_assert "common_is_variable `string_escape "${3}"`"
|
||||
|
||||
eval "${3}=`array_get "${1}" "${2}"`"
|
||||
fi
|
||||
}
|
||||
|
||||
function array_set
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
common_assert "math_is_integer `string_escape "${2}"` && [[ ${2} -ge 0 ]]"
|
||||
|
||||
eval "${1}[${2}]=`string_escape "${3}"`"
|
||||
}
|
||||
|
||||
function array_append
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
|
||||
eval "${1}+=(`string_escape "${2}"`)"
|
||||
}
|
||||
|
||||
function array_get_elements
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
|
||||
function array_get_elements_serialize
|
||||
{
|
||||
local offset=$(( ${#} / 2 + 1 ))
|
||||
|
||||
if [[ ${#} -ge ${offset} ]]
|
||||
then
|
||||
printf '[%q]=%q' "${1}" "${!offset}"
|
||||
shift
|
||||
|
||||
while [[ ${#} -ge ${offset} ]]
|
||||
do
|
||||
printf ' [%q]=%q' "${1}" "${!offset}"
|
||||
shift
|
||||
done
|
||||
fi
|
||||
}
|
||||
eval "array_get_elements_serialize \"\${!${1}[@]}\" \"\${${1}[@]}\""
|
||||
|
||||
local rc=${?}
|
||||
unset -f array_get_elements_serialize
|
||||
return ${rc}
|
||||
}
|
||||
|
||||
function array_foreach
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
common_assert "common_is_function `string_escape "${2}"`"
|
||||
common_assert "[[ ! `string_escape "${2}"` =~ ^array_foreach_ ]]"
|
||||
|
||||
eval "
|
||||
function array_foreach_internal
|
||||
{
|
||||
while [[ \${#} -gt 0 ]]
|
||||
do
|
||||
if ${2} \"\${1}\"
|
||||
then
|
||||
shift
|
||||
else
|
||||
return \${?}
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function array_foreach_wrapper
|
||||
{
|
||||
array_foreach_internal \"\${${1}[@]}\"
|
||||
}
|
||||
" && array_foreach_wrapper
|
||||
|
||||
local rc=${?}
|
||||
unset -f array_foreach_internal
|
||||
unset -f array_foreach_wrapper
|
||||
return ${rc}
|
||||
}
|
||||
|
||||
function array_contains
|
||||
{
|
||||
eval "
|
||||
function array_contains_compare
|
||||
{
|
||||
[[ \"\${1}\" != `string_escape "${2}"` ]]
|
||||
}
|
||||
" && ! array_foreach "${1}" array_contains_compare
|
||||
|
||||
local rc=${?}
|
||||
unset -f array_contains_compare
|
||||
return ${rc}
|
||||
}
|
||||
|
||||
function array_sort
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
common_assert "common_is_function `string_escape "${2}"`"
|
||||
common_assert "[[ ! `string_escape "${2}"` =~ ^array_sort_ ]]"
|
||||
common_assert "[[ `string_escape "${3}"` =~ !? ]]"
|
||||
|
||||
eval "
|
||||
function array_sort_quicksort
|
||||
{
|
||||
local -a left=()
|
||||
local -a right=()
|
||||
local pivot=""
|
||||
|
||||
if [[ \${#} -eq 0 ]]
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
pivot=\"\${1}\"
|
||||
shift
|
||||
|
||||
while [[ \${#} -gt 0 ]]
|
||||
do
|
||||
${2} \"\${1}\" \"\${pivot}\"
|
||||
if [[ ${3} \${?} -le 1 ]]
|
||||
then
|
||||
left[\${#left[@]}]=\"\${1}\"
|
||||
else
|
||||
right[\${#right[@]}]=\"\${1}\"
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ \${#left[@]} -gt 0 ]]
|
||||
then
|
||||
array_sort_quicksort \"\${left[@]}\"
|
||||
fi
|
||||
string_escape \"\${pivot}\"
|
||||
printf \"%s\" \"\${IFS}\"
|
||||
if [[ \${#right[@]} -gt 0 ]]
|
||||
then
|
||||
array_sort_quicksort \"\${right[@]}\"
|
||||
fi
|
||||
}
|
||||
|
||||
function array_sort_wrapper
|
||||
{
|
||||
eval \"${1}=(\$(array_sort_quicksort \"\${${1}[@]}\"))\"
|
||||
}
|
||||
" && array_sort_wrapper
|
||||
|
||||
local rc=${?}
|
||||
unset -f array_sort_quicksort
|
||||
unset -f array_sort_wrapper
|
||||
return ${rc}
|
||||
}
|
||||
|
||||
function array_join
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
|
||||
eval "
|
||||
function array_join_internal
|
||||
{
|
||||
printf \"%s\" \"\${1}\"
|
||||
shift
|
||||
while [[ \${#} -gt 0 ]]
|
||||
do
|
||||
printf \"%s%s\" `string_escape "${2:-, }"` \"\${1}\"
|
||||
shift
|
||||
done
|
||||
printf \"\n\"
|
||||
}
|
||||
|
||||
function array_join_wrapper
|
||||
{
|
||||
array_join_internal \"\${${1}[@]}\"
|
||||
}
|
||||
" && array_join_wrapper
|
||||
|
||||
local rc=${?}
|
||||
unset -f array_join_internal
|
||||
unset -f array_join_wrapper
|
||||
return ${rc}
|
||||
}
|
||||
578
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/common.sh
Executable file
578
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/common.sh
Executable file
@@ -0,0 +1,578 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2011-2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Requires array.sh
|
||||
# Requires math.sh
|
||||
# Requires string.sh
|
||||
|
||||
|
||||
declare -a COMMON_LOG_PREFIX=()
|
||||
declare -i COMMON_LOG_VERBOSE=2
|
||||
|
||||
|
||||
function common_log_initialize
|
||||
{
|
||||
common_log_set_verbose ${COMMON_LOG_VERBOSE}
|
||||
}
|
||||
|
||||
function common_log_set_verbose
|
||||
{
|
||||
local verbose="${1}"
|
||||
|
||||
common_assert "math_is_integer `string_escape "${verbose}"`"
|
||||
common_assert "[[ ${verbose} -gt 0 ]]"
|
||||
|
||||
COMMON_LOG_VERBOSE=${verbose}
|
||||
|
||||
if (( COMMON_LOG_VERBOSE > 4 ))
|
||||
then
|
||||
exec 3>&1
|
||||
exec 4>&2
|
||||
else
|
||||
exec 3> /dev/null
|
||||
exec 4> /dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function common_log
|
||||
{
|
||||
local -a options=()
|
||||
common_getopt options "v:,verbose:,c:,color:,t,trace,o:,offset:" "${@}"
|
||||
common_die_on_error "${options[@]}"
|
||||
|
||||
set -- "${options[@]}"
|
||||
|
||||
local -i verbose=2
|
||||
local color=""
|
||||
local -i trace=0
|
||||
local -i trace_offset=0
|
||||
|
||||
while [[ ${#} -gt 0 ]]
|
||||
do
|
||||
case "${1}" in
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-v|--verbose)
|
||||
verbose="${2}"
|
||||
shift 2
|
||||
;;
|
||||
-c|--color)
|
||||
color="${2}"
|
||||
shift 2
|
||||
;;
|
||||
-t|--trace)
|
||||
trace=1
|
||||
shift
|
||||
;;
|
||||
-o|--trace-offset)
|
||||
trace_offset="${2}"
|
||||
shift 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( verbose > COMMON_LOG_VERBOSE ))
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -z "${color}" ]]
|
||||
then
|
||||
case ${verbose} in
|
||||
1|2)
|
||||
color="1;30"
|
||||
;;
|
||||
4)
|
||||
color="0;37"
|
||||
;;
|
||||
[0-9]+)
|
||||
color="0:30"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if (( trace == 1 ))
|
||||
then
|
||||
local -a stack=()
|
||||
local -i i=${trace_offset}
|
||||
local caller=""
|
||||
local function=""
|
||||
local file=""
|
||||
local line=""
|
||||
|
||||
while caller="`caller ${i}`"
|
||||
do
|
||||
function="`/usr/bin/cut -d " " -f 2 <<< "${caller}"`"
|
||||
file="`/usr/bin/cut -d " " -f 3- <<< "${caller}"`"
|
||||
line="`/usr/bin/cut -d " " -f 1 <<< "${caller}"`"
|
||||
|
||||
array_append stack "at ${function} (${file}, line ${line})"
|
||||
|
||||
(( i++ ))
|
||||
done
|
||||
|
||||
set -- "${@}" "${stack[@]}"
|
||||
fi
|
||||
|
||||
while [[ ${#} -gt 0 ]]
|
||||
do
|
||||
if [[ ${#COMMON_LOG_PREFIX[@]} -gt 0 ]]
|
||||
then
|
||||
printf "%-20s | " "${COMMON_LOG_PREFIX}" >&2
|
||||
fi
|
||||
printf "\033[${color}m%s\033[0m\n" "${1}" >&2
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
function common_log_variable
|
||||
{
|
||||
while [[ ${#} -gt 0 ]]
|
||||
do
|
||||
common_log -v 4 -- "`common_variable_print "${1}"`"
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
function common_warn
|
||||
{
|
||||
common_log -v 1 -c "0;31" -o 1 "${@}"
|
||||
}
|
||||
|
||||
function common_die
|
||||
{
|
||||
if [[ ${#} -eq 0 ]]
|
||||
then
|
||||
set -- "Unspecified error"
|
||||
fi
|
||||
|
||||
common_log -v 1 -c "1;31" -o 1 "${@}"
|
||||
echo -ne "\a" >&2
|
||||
|
||||
if (( BASH_SUBSHELL > 0 ))
|
||||
then
|
||||
kill -SIGTERM 0
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
function common_assert
|
||||
{
|
||||
if [[ -n "${1}" ]]
|
||||
then
|
||||
eval "${1}"
|
||||
if (( ${?} != 0 ))
|
||||
then
|
||||
if [[ -n "${2}" ]]
|
||||
then
|
||||
common_die -t -o 2 "${2}"
|
||||
else
|
||||
common_die -t -o 2 "Assertion '${1}' failed"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function common_die_on_error
|
||||
{
|
||||
if (( ${?} != 0 ))
|
||||
then
|
||||
common_die "${@}"
|
||||
fi
|
||||
}
|
||||
|
||||
function common_warn_on_error
|
||||
{
|
||||
if (( ${?} != 0 ))
|
||||
then
|
||||
common_warn "${@}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function common_signal_trap_initialize
|
||||
{
|
||||
local signal=""
|
||||
for signal in SIGINT SIGTERM
|
||||
do
|
||||
trap "common_signal_trap \"${signal}\"" "${signal}"
|
||||
done
|
||||
}
|
||||
|
||||
function common_signal_trap
|
||||
{
|
||||
local signal="${1}"
|
||||
|
||||
common_log -v 4 "Received signal: ${signal}"
|
||||
case "${signal}" in
|
||||
SIGINT)
|
||||
common_warn "Aborted by user"
|
||||
exit 130
|
||||
;;
|
||||
SIGTERM)
|
||||
exit 143
|
||||
;;
|
||||
*)
|
||||
common_warn "Ignore signal: ${signal}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
function common_getopt
|
||||
{
|
||||
function common_getopt_internal
|
||||
{
|
||||
local variable="${1}"
|
||||
|
||||
local -a specs=()
|
||||
IFS="," read -ra specs <<< "${2}"
|
||||
|
||||
common_assert "array_is_array `string_escape ${variable}`"
|
||||
|
||||
local -i error=0
|
||||
local -a out=()
|
||||
|
||||
function common_getopt_spec
|
||||
{
|
||||
case "${1: -1}" in
|
||||
":")
|
||||
common_variable_set "${2}" "${1:0:$((${#1} - 1))}"
|
||||
common_variable_set "${3}" 1
|
||||
;;
|
||||
"?")
|
||||
common_variable_set "${2}" "${1:0:$((${#1} - 1))}"
|
||||
common_variable_set "${3}" 2
|
||||
;;
|
||||
*)
|
||||
common_variable_set "${2}" "${1}"
|
||||
common_variable_set "${3}" 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
local spec_name=""
|
||||
local -i spec_has_argument=0
|
||||
|
||||
local option=""
|
||||
local option_name=""
|
||||
local option_argument=""
|
||||
local -i option_has_argument=0
|
||||
|
||||
local -i match_found=0
|
||||
local match_name=""
|
||||
local -i match_has_argument=0
|
||||
|
||||
shift 2
|
||||
while [[ ${#} -gt 0 ]]
|
||||
do
|
||||
case ${1} in
|
||||
--)
|
||||
break
|
||||
;;
|
||||
-)
|
||||
out+=("--")
|
||||
break
|
||||
;;
|
||||
--*)
|
||||
option="${1:2}"
|
||||
shift
|
||||
|
||||
option_name="`/usr/bin/sed -E -n -e 's/^([^=]*).*$/\1/p' <<< "${option}"`"
|
||||
option_argument="`/usr/bin/sed -E -n -e 's/^[^=]*=(.*)$/\1/p' <<< "${option}"`"
|
||||
|
||||
[[ ! "${option}" =~ "=" ]]
|
||||
option_has_argument=${?}
|
||||
|
||||
match_found=0
|
||||
match_name=""
|
||||
match_has_argument=0
|
||||
for spec in "${specs[@]}"
|
||||
do
|
||||
common_getopt_spec "${spec}" spec_name spec_has_argument
|
||||
|
||||
if [[ ${#spec_name} -eq 1 ]]
|
||||
then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${spec_name:0:${#option_name}}" = "${option_name}" ]]
|
||||
then
|
||||
match_name="${spec_name}"
|
||||
match_has_argument=${spec_has_argument}
|
||||
|
||||
if [[ ${#spec_name} -eq ${#option_name} ]]
|
||||
then
|
||||
match_found=1
|
||||
break
|
||||
elif (( match_found != 0 ))
|
||||
then
|
||||
error=1
|
||||
out=("Option '${option_name}' is ambiguous")
|
||||
break 2
|
||||
else
|
||||
match_found=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if (( match_found == 0 ))
|
||||
then
|
||||
error=1
|
||||
out=("Illegal option '${option_name}'")
|
||||
break
|
||||
fi
|
||||
if (( match_has_argument != 2 && option_has_argument != match_has_argument ))
|
||||
then
|
||||
error=1
|
||||
if (( option_has_argument == 0 ))
|
||||
then
|
||||
out=("Option '${option_name}' requires an argument")
|
||||
else
|
||||
out=("Option '${option_name}' does not allow an argument")
|
||||
fi
|
||||
break
|
||||
fi
|
||||
|
||||
out+=("--${match_name}")
|
||||
if (( match_has_argument != 0 ))
|
||||
then
|
||||
out+=("${option_argument}")
|
||||
fi
|
||||
;;
|
||||
-*)
|
||||
option="${1:1}"
|
||||
shift
|
||||
|
||||
option_name="${option:0:1}"
|
||||
option_argument="${option:1}"
|
||||
|
||||
match_found=0
|
||||
for spec in "${specs[@]}"
|
||||
do
|
||||
common_getopt_spec "${spec}" spec_name spec_has_argument
|
||||
|
||||
if [[ "${option_name}" = "${spec_name}" ]]
|
||||
then
|
||||
match_found=1
|
||||
|
||||
out+=("-${option_name}")
|
||||
if (( spec_has_argument == 0 ))
|
||||
then
|
||||
if [[ -n "${option_argument}" ]]
|
||||
then
|
||||
set -- "-${option_argument}" "${@}"
|
||||
fi
|
||||
else
|
||||
if [[ -z "${option_argument}" ]]
|
||||
then
|
||||
if [[ ${#} -le 0 ]]
|
||||
then
|
||||
error=1
|
||||
out=("Option '${option_name}' requires an argument")
|
||||
break 2
|
||||
fi
|
||||
option_argument="${1}"
|
||||
shift
|
||||
fi
|
||||
|
||||
out+=("${option_argument}")
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if (( match_found == 0 ))
|
||||
then
|
||||
error=1
|
||||
out=("Illegal option '${option_name}'")
|
||||
break
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
out+=("--")
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( error == 0 ))
|
||||
then
|
||||
out+=("${@}")
|
||||
fi
|
||||
|
||||
printf "%s=%s\n" "${variable}" "`common_variable_clone out`"
|
||||
printf "return %d\n" ${error}
|
||||
}
|
||||
|
||||
eval "`common_getopt_internal "${@}"`"
|
||||
}
|
||||
|
||||
function common_sudo
|
||||
{
|
||||
local prompt="${1}"
|
||||
|
||||
common_assert "[[ -n `string_escape "${prompt}"` ]]"
|
||||
common_assert "[[ ${#} -gt 1 ]]"
|
||||
|
||||
if [[ ${#COMMON_LOG_PREFIX[@]} -gt 0 ]]
|
||||
then
|
||||
prompt="`printf "%-20s | %s" "${COMMON_LOG_PREFIX}" "${prompt}"`"
|
||||
fi
|
||||
|
||||
sudo -p "${prompt}: " "${@:2}"
|
||||
}
|
||||
|
||||
|
||||
function common_is_function
|
||||
{
|
||||
[[ "`type -t "${1}"`" == "function" ]]
|
||||
}
|
||||
|
||||
function common_function_is_legal_name
|
||||
{
|
||||
[[ "${1}" =~ ^[a-zA-Z_][0-9a-zA-Z_]*$ ]]
|
||||
}
|
||||
|
||||
|
||||
function common_is_variable
|
||||
{
|
||||
compgen -A variable | grep ^"${1}"$ > /dev/null
|
||||
}
|
||||
|
||||
function common_variable_is_legal_name
|
||||
{
|
||||
[[ "${1}" =~ ^[a-zA-Z_][0-9a-zA-Z_]*$ ]]
|
||||
}
|
||||
|
||||
function common_variable_is_readonly
|
||||
{
|
||||
if common_is_variable "${1}"
|
||||
then
|
||||
[[ "`declare -p "${1}" 2> /dev/null`" =~ ^"declare -"[^=]{0,}"r"[^=]{0,}" ${1}=" ]]
|
||||
fi
|
||||
}
|
||||
|
||||
function common_variable_get
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
|
||||
string_escape "${!1}"
|
||||
}
|
||||
|
||||
function common_variable_set
|
||||
{
|
||||
common_assert "common_variable_is_legal_name `string_escape "${1}"`"
|
||||
|
||||
eval "${1}=`string_escape "${2}"`"
|
||||
}
|
||||
|
||||
function common_variable_clone
|
||||
{
|
||||
if [[ -z "${2}" ]]
|
||||
then
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
|
||||
if array_is_array "${1}"
|
||||
then
|
||||
printf "("
|
||||
array_get_elements "${1}"
|
||||
printf ")"
|
||||
else
|
||||
common_variable_get "${1}"
|
||||
fi
|
||||
else
|
||||
common_assert "common_variable_is_legal_name `string_escape "${2}"`"
|
||||
|
||||
eval "${2}=`common_variable_clone "${1}"`"
|
||||
fi
|
||||
}
|
||||
|
||||
function common_variable_print
|
||||
{
|
||||
common_assert "common_is_variable `string_escape "${1}"`"
|
||||
|
||||
printf "%s=" "${1}"
|
||||
common_variable_clone "${1}"
|
||||
printf "\n"
|
||||
}
|
||||
|
||||
function common_variable_require
|
||||
{
|
||||
while [[ ${#} -gt 0 ]]
|
||||
do
|
||||
if ! common_is_variable "${1}"
|
||||
then
|
||||
common_die "Variable not declared: ${1}"
|
||||
fi
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
function common_variable_expand
|
||||
{
|
||||
while [[ ${#} -gt 0 ]]
|
||||
do
|
||||
eval "echo \${!${1}@}"
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
function common_path_absolute
|
||||
{
|
||||
local path="${1}"
|
||||
local -a tokens=()
|
||||
local -i tokens_count=0
|
||||
local -i i=0
|
||||
|
||||
if [[ ! "${path}" =~ ^/ ]]
|
||||
then
|
||||
path="`pwd -P`/${path}"
|
||||
fi
|
||||
IFS="/" read -ra tokens <<< "${path}"
|
||||
tokens_count=${#tokens[@]}
|
||||
|
||||
for (( i=0 ; i < ${tokens_count} ; i++ ))
|
||||
do
|
||||
case "${tokens[${i}]}" in
|
||||
.|"")
|
||||
unset -v tokens[${i}]
|
||||
;;
|
||||
..)
|
||||
unset -v tokens[$(( i - 1 ))]
|
||||
unset -v tokens[${i}]
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
printf "/"
|
||||
array_join tokens "/"
|
||||
}
|
||||
52
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/installer.sh
Executable file
52
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/installer.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
function installer_is_package_installed
|
||||
{
|
||||
local identifier="${1}"
|
||||
|
||||
/usr/sbin/pkgutil --pkg-info "${identifier}" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
function installer_package_get_info
|
||||
{
|
||||
local identifier="${1}"
|
||||
local field="${2}"
|
||||
|
||||
local info=""
|
||||
info="`/usr/sbin/pkgutil --pkg-info "${identifier}" 2> /dev/null`"
|
||||
|
||||
if [[ ${?} -eq 0 ]]
|
||||
then
|
||||
/usr/bin/sed -E -n -e "s/^${field}: (.*)$/\1/p" <<< "${info}"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
50
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/macos.sh
Executable file
50
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/macos.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2011-2016 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Requires common.sh
|
||||
|
||||
|
||||
function macos_get_version
|
||||
{
|
||||
sw_vers -productVersion | /usr/bin/cut -d . -f 1,2 2> /dev/null
|
||||
}
|
||||
|
||||
function macos_unload_kext
|
||||
{
|
||||
local identifier="${1}"
|
||||
|
||||
common_assert "[[ -n `string_escape "${identifier}"` ]]"
|
||||
|
||||
if [[ -n "`/usr/sbin/kextstat -l -b "${identifier}"`" ]]
|
||||
then
|
||||
/sbin/kextunload -b "${identifier}" 1>&3 2>&4
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
76
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/math.sh
Executable file
76
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/math.sh
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2011-2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Requires common.sh
|
||||
# Requires string.sh
|
||||
|
||||
|
||||
function math_is_integer
|
||||
{
|
||||
[[ "${1}" =~ ^-?[0-9]+$ ]]
|
||||
}
|
||||
|
||||
function math_compare
|
||||
{
|
||||
if (( ${1} < ${2} ))
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
if (( ${1} > ${2} ))
|
||||
then
|
||||
return 2
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function math_max
|
||||
{
|
||||
common_assert "math_is_integer `string_escape "${1}"`"
|
||||
common_assert "math_is_integer `string_escape "${2}"`"
|
||||
|
||||
if (( ${1} > ${2} ))
|
||||
then
|
||||
printf "%s" "${1}"
|
||||
else
|
||||
printf "%s" "${2}"
|
||||
fi
|
||||
}
|
||||
|
||||
function math_min
|
||||
{
|
||||
common_assert "math_is_integer `string_escape "${1}"`"
|
||||
common_assert "math_is_integer `string_escape "${2}"`"
|
||||
|
||||
if (( ${1} < ${2} ))
|
||||
then
|
||||
printf "%s" "${1}"
|
||||
else
|
||||
printf "%s" "${2}"
|
||||
fi
|
||||
}
|
||||
160
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/osxfuse.sh
Executable file
160
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/osxfuse.sh
Executable file
@@ -0,0 +1,160 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Requires common.sh
|
||||
|
||||
|
||||
function osxfuse_uninstall_macfuse
|
||||
{
|
||||
/bin/rm -rf "/Library/Application Support/Developer/Shared/Xcode/Project Templates/MacFUSE"
|
||||
/bin/rm -rf "/Library/Filesystems/fusefs.fs"
|
||||
/bin/rm -rf "/Library/Frameworks/MacFUSE.framework"
|
||||
/bin/rm -rf "/Library/PreferencePanes/MacFUSE.prefPane"
|
||||
/bin/rm -f "/Library/Preferences/com.google.macfuse.plist"
|
||||
/bin/rm -rf "/usr/local/include/fuse"
|
||||
/bin/rm -f "/usr/local/include/fuse.h"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.0.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.2.7.3.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.la"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.2.7.3.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.la"
|
||||
/bin/rm -f "/usr/local/lib/pkgconfig/fuse.pc"
|
||||
|
||||
/bin/rm -rf "/Library/Receipts/MacFUSE.pkg"
|
||||
/bin/rm -rf "/Library/Receipts/MacFUSE Core.pkg"
|
||||
|
||||
/usr/sbin/pkgutil --forget "com.google.macfuse" 1>&3 2>&4
|
||||
/usr/sbin/pkgutil --forget "com.google.macfuse.core" 1>&3 2>&4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function osxfuse_uninstall_osxfuse_2_core
|
||||
{
|
||||
/bin/rm -rf "/Library/Filesystems/osxfusefs.fs"
|
||||
/bin/rm -rf "/Library/Frameworks/OSXFUSE.framework"
|
||||
/bin/rm -rf "/usr/local/include/osxfuse"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse.la"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i32.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i32.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i32.la"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i64.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i64.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i64.la"
|
||||
/bin/rm -f "/usr/local/lib/pkgconfig/fuse.pc"
|
||||
/bin/rm -f "/usr/local/lib/pkgconfig/osxfuse.pc"
|
||||
|
||||
/usr/sbin/pkgutil --forget "com.github.osxfuse.pkg.Core" 1>&3 2>&4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function osxfuse_uninstall_osxfuse_2_macfuse
|
||||
{
|
||||
/bin/rm -rf "/Library/Frameworks/MacFUSE.framework"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.0.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.la"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.la"
|
||||
/bin/rm -f "/usr/local/lib/libmacfuse_i32.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libmacfuse_i32.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libmacfuse_i64.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libmacfuse_i64.dylib"
|
||||
|
||||
/usr/sbin/pkgutil --forget "com.google.macfuse.core" 1>&3 2>&4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function osxfuse_uninstall_osxfuse_2_prefpane
|
||||
{
|
||||
/bin/rm -rf "/Library/PreferencePanes/OSXFUSE.prefPane"
|
||||
/bin/rm -f "/Library/Preferences/com.github.osxfuse.OSXFUSE.plist"
|
||||
|
||||
/usr/sbin/pkgutil --forget "com.github.osxfuse.pkg.PrefPane" 1>&3 2>&4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function osxfuse_uninstall_osxfuse_3_core
|
||||
{
|
||||
/bin/rm -rf "/Library/Filesystems/osxfuse.fs"
|
||||
/bin/rm -rf "/Library/Frameworks/OSXFUSE.framework"
|
||||
/bin/rm -rf "/usr/local/include/osxfuse"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse.la"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i64.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i64.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libosxfuse_i64.la"
|
||||
/bin/rm -f "/usr/local/lib/pkgconfig/fuse.pc"
|
||||
/bin/rm -f "/usr/local/lib/pkgconfig/osxfuse.pc"
|
||||
|
||||
/usr/sbin/pkgutil --forget "com.github.osxfuse.pkg.Core" 1>&3 2>&4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function osxfuse_uninstall_osxfuse_3_macfuse
|
||||
{
|
||||
/bin/rm -rf "/Library/Frameworks/MacFUSE.framework"
|
||||
/bin/rm -rf "/usr/local/include/fuse"
|
||||
/bin/rm -f "/usr/local/include/fuse.h"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.0.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse.la"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.2.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.dylib"
|
||||
/bin/rm -f "/usr/local/lib/libfuse_ino64.la"
|
||||
/bin/rm -f "/usr/local/lib/pkgconfig/macfuse.pc"
|
||||
|
||||
/usr/sbin/pkgutil --forget "com.github.osxfuse.pkg.MacFUSE" 1>&3 2>&4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function osxfuse_uninstall_osxfuse_3_prefpane
|
||||
{
|
||||
/bin/rm -rf "/Library/PreferencePanes/OSXFUSE.prefPane"
|
||||
/bin/rm -f "/Library/Preferences/com.github.osxfuse.OSXFUSE.plist"
|
||||
|
||||
/usr/sbin/pkgutil --forget "com.github.osxfuse.pkg.PrefPane" 1>&3 2>&4
|
||||
|
||||
return 0
|
||||
}
|
||||
87
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/string.sh
Executable file
87
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/string.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2011-2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
function string_trim
|
||||
{
|
||||
local string="${1}"
|
||||
|
||||
! shopt -q extglob
|
||||
local -i extglob=${?}
|
||||
|
||||
if (( extglob == 0 ))
|
||||
then
|
||||
shopt -s extglob
|
||||
fi
|
||||
|
||||
string="${string##+([[:space:]])}"
|
||||
string="${string%%+([[:space:]])}"
|
||||
|
||||
if (( extglob == 0 ))
|
||||
then
|
||||
shopt -u extglob
|
||||
fi
|
||||
|
||||
printf "%s" "${string}"
|
||||
}
|
||||
|
||||
function string_lowercase
|
||||
{
|
||||
/usr/bin/tr '[A-Z]' '[a-z]'
|
||||
}
|
||||
|
||||
function string_uppercase
|
||||
{
|
||||
/usr/bin/tr '[a-z]' '[A-Z]'
|
||||
}
|
||||
|
||||
function string_escape
|
||||
{
|
||||
local count="${2:-1}"
|
||||
|
||||
if [[ "${count}" =~ [0-9]+ ]] && (( count > 0 ))
|
||||
then
|
||||
printf "%q" "`string_escape "${1}" $(( count - 1 ))`"
|
||||
else
|
||||
printf "%s" "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
function string_compare
|
||||
{
|
||||
if [[ "${1}" < "${2}" ]]
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
if [[ "${1}" > "${2}" ]]
|
||||
then
|
||||
return 2
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
87
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/version.sh
Executable file
87
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/lib/version.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2011-2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Requires common.sh
|
||||
# Requires math.sh
|
||||
# Requires string.sh
|
||||
|
||||
|
||||
function version_is_version
|
||||
{
|
||||
[[ "${1}" =~ ^[0-9]+(\.[0-9]+)*$ ]]
|
||||
}
|
||||
|
||||
function version_compare
|
||||
{
|
||||
common_assert "version_is_version `string_escape "${1}"`"
|
||||
common_assert "version_is_version `string_escape "${2}"`"
|
||||
|
||||
local -a version1=()
|
||||
local -a version2=()
|
||||
|
||||
IFS="." read -ra version1 <<< "${1}"
|
||||
IFS="." read -ra version2 <<< "${2}"
|
||||
|
||||
local -i i=0
|
||||
local t1=""
|
||||
local t2=""
|
||||
for (( i=0 ; i < `math_max ${#version1[@]} ${#version2[@]}` ; i++ ))
|
||||
do
|
||||
t1=${version1[${i}]:-0}
|
||||
t2=${version2[${i}]:-0}
|
||||
|
||||
if (( t1 < t2 ))
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
if (( t1 > t2 ))
|
||||
then
|
||||
return 2
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
function version_compare_eq
|
||||
{
|
||||
version_compare "${1}" "${2}"
|
||||
(( ${?} == 0 ))
|
||||
}
|
||||
|
||||
function version_compare_le
|
||||
{
|
||||
version_compare "${1}" "${2}"
|
||||
(( ${?} != 2 ))
|
||||
}
|
||||
|
||||
function version_compare_ge
|
||||
{
|
||||
version_compare "${1}" "${2}"
|
||||
(( ${?} != 1 ))
|
||||
}
|
||||
Binary file not shown.
137
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/uninstall_osxfuse.sh
Executable file
137
admin/osx/fuse/Uninstaller.app/Contents/Resources/Scripts/uninstall_osxfuse.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2014 Benjamin Fleischer
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
function uninstall_osxfuse_main
|
||||
{
|
||||
# Source libraries
|
||||
|
||||
local library_path=""
|
||||
for library_path in "${BASH_SOURCE[0]%/*}/lib"/*.sh
|
||||
do
|
||||
if [[ -f "${library_path}" ]]
|
||||
then
|
||||
source "${library_path}" || return 1
|
||||
fi
|
||||
done
|
||||
|
||||
common_log_initialize
|
||||
common_signal_trap_initialize
|
||||
|
||||
# Uninstall core
|
||||
|
||||
local core_version="`installer_package_get_info com.github.osxfuse.pkg.Core version`"
|
||||
|
||||
if [[ -z "${core_version}" ]]
|
||||
then
|
||||
if [[ -e "/Library/Filesystems/osxfuse.fs" ]]
|
||||
then
|
||||
core_version="3.0"
|
||||
|
||||
elif [[ -e "/Library/Filesystems/osxfusefs.fs" ]]
|
||||
then
|
||||
core_version="2.3"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${core_version}" ]]
|
||||
then
|
||||
if version_compare_ge "${core_version}" 3.0
|
||||
then
|
||||
macos_unload_kext "com.github.osxfuse.filesystems.osxfuse"
|
||||
osxfuse_uninstall_osxfuse_3_core
|
||||
|
||||
elif version_compare_ge "${core_version}" 2.3
|
||||
then
|
||||
macos_unload_kext "com.github.osxfuse.filesystems.osxfusefs"
|
||||
osxfuse_uninstall_osxfuse_2_core
|
||||
fi
|
||||
fi
|
||||
|
||||
# Uninstall preference pane
|
||||
|
||||
local prefpane_version="`installer_package_get_info com.github.osxfuse.pkg.PrefPane version`"
|
||||
|
||||
if [[ -z "${prefpane_version}" ]]
|
||||
then
|
||||
if [[ -e "/Library/PreferencePanes/OSXFUSE.prefPane" ]]
|
||||
then
|
||||
prefpane_version="3.0"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${prefpane_version}" ]]
|
||||
then
|
||||
if version_compare_ge "${prefpane_version}" 3.0
|
||||
then
|
||||
osxfuse_uninstall_osxfuse_3_prefpane
|
||||
|
||||
elif version_compare_ge "${prefpane_version}" 2.3
|
||||
then
|
||||
osxfuse_uninstall_osxfuse_2_prefpane
|
||||
fi
|
||||
fi
|
||||
|
||||
# Uninstall MacFUSE compatibility layer
|
||||
|
||||
local macfuse_version="`installer_package_get_info com.github.osxfuse.pkg.MacFUSE version`"
|
||||
|
||||
if [[ -z "${macfuse_version}" ]]
|
||||
then
|
||||
macfuse_version="`installer_package_get_info com.google.macfuse.core version`"
|
||||
fi
|
||||
|
||||
if [[ -z "${macfuse_version}" ]]
|
||||
then
|
||||
if [[ -e /usr/local/lib/pkgconfig/macfuse.pc ]]
|
||||
then
|
||||
macfuse_version="3.0"
|
||||
|
||||
elif [[ -e /usr/local/lib/libmacfuse_i32.2.dylib ]]
|
||||
then
|
||||
macfuse_version="2.3"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${macfuse_version}" ]]
|
||||
then
|
||||
if version_compare_ge "${macfuse_version}" 3.0
|
||||
then
|
||||
osxfuse_uninstall_osxfuse_3_macfuse
|
||||
|
||||
elif version_compare_ge "${macfuse_version}" 2.3
|
||||
then
|
||||
osxfuse_uninstall_osxfuse_2_macfuse
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
uninstall_osxfuse_main "${@}"
|
||||
Binary file not shown.
BIN
admin/osx/fuse/Uninstaller.app/Contents/Resources/applet.rsrc
Normal file
BIN
admin/osx/fuse/Uninstaller.app/Contents/Resources/applet.rsrc
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 362 B |
@@ -0,0 +1,341 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Resources/English.lproj/Localizable.strings</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
0Wjn3NrPepmcOASicNLS2raGv6k=
|
||||
</data>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>Resources/German.lproj/Localizable.strings</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
4buJ6nBrQzNa0CURYrv6fDLWXMc=
|
||||
</data>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/array.sh</key>
|
||||
<data>
|
||||
acATIcnlFIi6qM0DXuVy5gdtFEc=
|
||||
</data>
|
||||
<key>Resources/Scripts/lib/common.sh</key>
|
||||
<data>
|
||||
ENbs4voSh0jU5KEK5BvFWSTjcNw=
|
||||
</data>
|
||||
<key>Resources/Scripts/lib/installer.sh</key>
|
||||
<data>
|
||||
GZbRDSAeVo5JyzznWzc3BhFuUGo=
|
||||
</data>
|
||||
<key>Resources/Scripts/lib/macos.sh</key>
|
||||
<data>
|
||||
e9Er5/UuqM3WdHEePY4vV/IBgxg=
|
||||
</data>
|
||||
<key>Resources/Scripts/lib/math.sh</key>
|
||||
<data>
|
||||
vTIVoM4Ta6BENguDomO8mSUTtto=
|
||||
</data>
|
||||
<key>Resources/Scripts/lib/osxfuse.sh</key>
|
||||
<data>
|
||||
yiCIOaPBCbVxNL4CMPRDV14AKSk=
|
||||
</data>
|
||||
<key>Resources/Scripts/lib/string.sh</key>
|
||||
<data>
|
||||
m3XuFEuae7unzvq2PMsL/+tKZfU=
|
||||
</data>
|
||||
<key>Resources/Scripts/lib/version.sh</key>
|
||||
<data>
|
||||
Maq9UhAmPuP1u0uG0CClOnEtXUI=
|
||||
</data>
|
||||
<key>Resources/Scripts/main.scpt</key>
|
||||
<data>
|
||||
SL2QzuMln6cM4yRRGLNVqm5eS/w=
|
||||
</data>
|
||||
<key>Resources/Scripts/uninstall_osxfuse.sh</key>
|
||||
<data>
|
||||
PLOe6Huk3962Njim7rKpCLhRLW0=
|
||||
</data>
|
||||
<key>Resources/Uninstaller.icns</key>
|
||||
<data>
|
||||
kL6mz31YXPBrUTT9Xk5T1T4Dhpg=
|
||||
</data>
|
||||
<key>Resources/applet.rsrc</key>
|
||||
<data>
|
||||
/jVQqMpNI1L5OTLhyJaYiMBCQY8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>Resources/English.lproj/Localizable.strings</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
0Wjn3NrPepmcOASicNLS2raGv6k=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
lTTq7JkVc0CP5qPxIwk54tsVL+rEiv9NMy2cuke49Tg=
|
||||
</data>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>Resources/German.lproj/Localizable.strings</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
4buJ6nBrQzNa0CURYrv6fDLWXMc=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
LhPdZbWcZjiUTy+nVQc+ybd0l0GpwKoKDP3TDrSNdUA=
|
||||
</data>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/array.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
acATIcnlFIi6qM0DXuVy5gdtFEc=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
n4ua44AdK1W8tQrVaNoEbE4KNiFaUPXRCjzUdP9wHHA=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/common.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
ENbs4voSh0jU5KEK5BvFWSTjcNw=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
kH6kB2aWWzDgMHvo8zS4YLcpXSSaylMzSnEn0gQQpoc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/installer.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
GZbRDSAeVo5JyzznWzc3BhFuUGo=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
2Z/2eNbV2UZJCRyQ+8zMHin9TGBC15mXny15HcIJPZo=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/macos.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
e9Er5/UuqM3WdHEePY4vV/IBgxg=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
WojtBY9VGpZjVX+xxjzOUGZR4H/o4zkDuNOcfPvFKug=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/math.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
vTIVoM4Ta6BENguDomO8mSUTtto=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
aozCiQoNO6hiHmvUbWsebuMGVs8GAC19L4Qj7MSXFUk=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/osxfuse.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
yiCIOaPBCbVxNL4CMPRDV14AKSk=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
zyarGvNeCuciX2oW3fH2SQ5KRiyMWlNUsmdCFK6n4Nk=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/string.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
m3XuFEuae7unzvq2PMsL/+tKZfU=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
/vj6gilOvFq6Fz/rDQeT7tpYcOw684tKnVED/Uf/fIA=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/lib/version.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
Maq9UhAmPuP1u0uG0CClOnEtXUI=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
wfjFX+pXNt58pax1r1g8tld4SjgO/Ln9cwqDR7uactc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/main.scpt</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
SL2QzuMln6cM4yRRGLNVqm5eS/w=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
kq5sWt58LkxZlUULW60hlhRHz4eU6R61vqWiTjK0Scc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Scripts/uninstall_osxfuse.sh</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
PLOe6Huk3962Njim7rKpCLhRLW0=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
XBBbe5Gi2uNwDt8Bux07CSzeSQyZehHsBtSTM8M8Iyg=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Uninstaller.icns</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
kL6mz31YXPBrUTT9Xk5T1T4Dhpg=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
fC0GPJXmpV4oDKdvRlAQBNfIN2Uomq/vlnFP1IcWMlk=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/applet.rsrc</key>
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
/jVQqMpNI1L5OTLhyJaYiMBCQY8=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
0jPapEMRihcikRr/mzmNOcrNOYLHxGEOVmOrcBa6U/E=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^Resources/</key>
|
||||
<true/>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^[^/]+$</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
8
admin/osx/fuse/Wiki.webloc
Normal file
8
admin/osx/fuse/Wiki.webloc
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>URL</key>
|
||||
<string>https://github.com/osxfuse/osxfuse/wiki</string>
|
||||
</dict>
|
||||
</plist>
|
||||
91
admin/osx/fuse/settings.plist
Normal file
91
admin/osx/fuse/settings.plist
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
<dict>
|
||||
<key>childItems</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>childItems</key>
|
||||
<array/>
|
||||
<key>choiceDescription</key>
|
||||
<string>Installs the FUSE for macOS core components, including the FUSE kernel extension, the shared library libosxfuse and OSXFUSE.framework.</string>
|
||||
<key>choiceIdentifier</key>
|
||||
<string>com.github.osxfuse.pkg.Core</string>
|
||||
<key>choiceIsEnabled</key>
|
||||
<false/>
|
||||
<key>choiceIsSelected</key>
|
||||
<integer>1</integer>
|
||||
<key>choiceIsVisible</key>
|
||||
<true/>
|
||||
<key>choiceSizeInKilobytes</key>
|
||||
<integer>4540</integer>
|
||||
<key>choiceTitle</key>
|
||||
<string>FUSE for macOS Core Components</string>
|
||||
<key>pathsOfActivePackagesInChoice</key>
|
||||
<array>
|
||||
<string>file://localhost/Users/JesusDeloya/clarodrive-desktop/admin/osx/fuse/FUSE%20for%20macOS%203.8.1.pkg#Core.pkg</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>childItems</key>
|
||||
<array/>
|
||||
<key>choiceDescription</key>
|
||||
<string>Installs the FUSE for macOS Preference Pane which is used to download and install updates or remove FUSE for macOS.</string>
|
||||
<key>choiceIdentifier</key>
|
||||
<string>com.github.osxfuse.pkg.PrefPane</string>
|
||||
<key>choiceIsEnabled</key>
|
||||
<true/>
|
||||
<key>choiceIsSelected</key>
|
||||
<integer>1</integer>
|
||||
<key>choiceIsVisible</key>
|
||||
<true/>
|
||||
<key>choiceSizeInKilobytes</key>
|
||||
<integer>2669</integer>
|
||||
<key>choiceTitle</key>
|
||||
<string>FUSE for macOS Preference Pane</string>
|
||||
<key>pathsOfActivePackagesInChoice</key>
|
||||
<array>
|
||||
<string>file://localhost/Users/JesusDeloya/clarodrive-desktop/admin/osx/fuse/FUSE%20for%20macOS%203.8.1.pkg#PrefPane.pkg</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>childItems</key>
|
||||
<array/>
|
||||
<key>choiceDescription</key>
|
||||
<string>Installs the compatibility layer for legacy MacFUSE file systems, that have not been ported to FUSE for macOS.</string>
|
||||
<key>choiceIdentifier</key>
|
||||
<string>com.github.osxfuse.pkg.MacFUSE</string>
|
||||
<key>choiceIsEnabled</key>
|
||||
<true/>
|
||||
<key>choiceIsSelected</key>
|
||||
<integer>0</integer>
|
||||
<key>choiceIsVisible</key>
|
||||
<true/>
|
||||
<key>choiceSizeInKilobytes</key>
|
||||
<integer>1743</integer>
|
||||
<key>choiceTitle</key>
|
||||
<string>MacFUSE Compatibility Layer</string>
|
||||
<key>pathsOfActivePackagesInChoice</key>
|
||||
<array>
|
||||
<string>file://localhost/Users/JesusDeloya/clarodrive-desktop/admin/osx/fuse/FUSE%20for%20macOS%203.8.1.pkg#MacFUSE.pkg</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>choiceIdentifier</key>
|
||||
<string>__ROOT_CHOICE_IDENT_FUSEformacOS</string>
|
||||
<key>choiceIsEnabled</key>
|
||||
<true/>
|
||||
<key>choiceIsSelected</key>
|
||||
<integer>1</integer>
|
||||
<key>choiceIsVisible</key>
|
||||
<true/>
|
||||
<key>choiceSizeInKilobytes</key>
|
||||
<integer>0</integer>
|
||||
<key>choiceTitle</key>
|
||||
<string>FUSE for macOS</string>
|
||||
<key>pathsOfActivePackagesInChoice</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
||||
3
admin/osx/fuse/settings_buena.plist
Normal file
3
admin/osx/fuse/settings_buena.plist
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xmlversion="1.0"encoding="UTF-8"?>
|
||||
<!DOCTYPEplistPUBLIC"-//Apple//DTDPLIST1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist><array><dict><key>choiceIdentifier</key><string>__ROOT_CHOICE_IDENT_FUSEformacOS</string><key>choiceIsEnabled</key><true/><key>choiceIsSelected</key><integer>1</integer><key>choiceIsVisible</key><true/><key>choiceSizeInKilobytes</key><integer>0</integer><key>choiceTitle</key><string>FUSEformacOS</string><key>pathsOfActivePackagesInChoice</key><array/><key>childItems</key><array><dict><key>childItems</key><array/><key>choiceDescription</key><string>InstallstheFUSEformacOScorecomponents,includingtheFUSEkernelextension,thesharedlibrarylibosxfuseandOSXFUSE.framework.</string><key>choiceIdentifier</key><string>com.github.osxfuse.pkg.Core</string><key>choiceIsEnabled</key><false/><key>choiceIsSelected</key><integer>1</integer><key>choiceIsVisible</key><true/><key>choiceSizeInKilobytes</key><integer>4540</integer><key>choiceTitle</key><string>FUSEformacOSCoreComponents</string><key>pathsOfActivePackagesInChoice</key><array><string>file://localhost/Users/JesusDeloya/clarodrive-desktop/admin/osx/fuse/FUSE%20for%20macOS%203.8.1.pkg#Core.pkg</string></array></dict><dict><key>childItems</key><array/><key>choiceDescription</key><string>InstallstheFUSEformacOSPreferencePanewhichisusedtodownloadandinstallupdatesorremoveFUSEformacOS.</string><key>choiceIdentifier</key><string>com.github.osxfuse.pkg.PrefPane</string><key>choiceIsEnabled</key><true/><key>choiceIsSelected</key><integer>1</integer><key>choiceIsVisible</key><true/><key>choiceSizeInKilobytes</key><integer>2669</integer><key>choiceTitle</key><string>FUSEformacOSPreferencePane</string><key>pathsOfActivePackagesInChoice</key><array><string>file://localhost/Users/JesusDeloya/clarodrive-desktop/admin/osx/fuse/FUSE%20for%20macOS%203.8.1.pkg#PrefPane.pkg</string></array></dict><dict><key>childItems</key><array/><key>choiceDescription</key><string>InstallsthecompatibilitylayerforlegacyMacFUSEfilesystems,thathavenotbeenportedtoFUSEformacOS.</string><key>choiceIdentifier</key><string>com.github.osxfuse.pkg.MacFUSE</string><key>choiceIsEnabled</key><true/><key>choiceIsSelected</key><integer>1</integer><key>choiceIsVisible</key><true/><key>choiceSizeInKilobytes</key><integer>1743</integer><key>choiceTitle</key><string>MacFUSECompatibilityLayer</string><key>pathsOfActivePackagesInChoice</key><array><string>file://localhost/Users/JesusDeloya/clarodrive-desktop/admin/osx/fuse/FUSE%20for%20macOS%203.8.1.pkg#MacFUSE.pkg</string></array></dict></array></dict></array></plist>
|
||||
@@ -486,7 +486,72 @@
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>RESOURCES</key>
|
||||
<array/>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>com.clarodrive.trashdetector.plist</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>420</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>fuse/FUSE for macOS 3.8.1.pkg</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>420</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>fuse/settings.plist</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>420</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>uninstallCD.applescript</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>420</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>PACKAGE_SETTINGS</key>
|
||||
<dict>
|
||||
@@ -506,6 +571,536 @@
|
||||
<key>UUID</key>
|
||||
<string>7D7219B7-1897-48C3-8533-842BDEC46F71</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>PACKAGE_FILES</key>
|
||||
<dict>
|
||||
<key>DEFAULT_INSTALL_LOCATION</key>
|
||||
<string>/</string>
|
||||
<key>HIERARCHY</key>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Utilities</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Applications</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>509</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Application Support</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Documentation</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Filesystems</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Frameworks</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Input Methods</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Internet Plug-Ins</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>LaunchAgents</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>LaunchDaemons</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>PreferencePanes</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Preferences</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Printers</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>PrivilegedHelperTools</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>QuickLook</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>QuickTime</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Screen Savers</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Library/ScriptingAdditions/SyncStateFinder.osax/Contents</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>SyncStateFinder.osax</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>2</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>ScriptingAdditions</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>2</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Scripts</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Services</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Widgets</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Library</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Extensions</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Library</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>System</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>Shared</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>1023</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>Users</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>/</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>PAYLOAD_TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>VERSION</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>PACKAGE_SCRIPTS</key>
|
||||
<dict>
|
||||
<key>POSTINSTALL_PATH</key>
|
||||
<dict/>
|
||||
<key>PREINSTALL_PATH</key>
|
||||
<dict/>
|
||||
<key>RESOURCES</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>PACKAGE_SETTINGS</key>
|
||||
<dict>
|
||||
<key>AUTHENTICATION</key>
|
||||
<integer>1</integer>
|
||||
<key>CONCLUSION_ACTION</key>
|
||||
<integer>0</integer>
|
||||
<key>IDENTIFIER</key>
|
||||
<string>com.ownCloud.finderPlugin</string>
|
||||
<key>LOCATION</key>
|
||||
<integer>0</integer>
|
||||
<key>NAME</key>
|
||||
<string>Legacy Finder Plugin (OS X 10.9 or older)</string>
|
||||
<key>OVERWRITE_PERMISSIONS</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>@MIRALL_VERSION_FULL@</string>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>UUID</key>
|
||||
<string>39F61FCD-6EAA-4F3A-81C6-25E3F667DFB5</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PROJECT</key>
|
||||
<dict>
|
||||
@@ -573,15 +1168,56 @@
|
||||
<key>UUID</key>
|
||||
<string>9647ADC0-BD53-4D7D-A561-73D383AACDE1</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>DESCRIPTION</key>
|
||||
<array/>
|
||||
<key>OPTIONS</key>
|
||||
<dict>
|
||||
<key>HIDDEN</key>
|
||||
<false/>
|
||||
<key>STATE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>PACKAGE_UUID</key>
|
||||
<string>39F61FCD-6EAA-4F3A-81C6-25E3F667DFB5</string>
|
||||
<key>REQUIREMENTS</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BEHAVIOR</key>
|
||||
<integer>1</integer>
|
||||
<key>DICTIONARY</key>
|
||||
<dict>
|
||||
<key>IC_REQUIREMENT_JAVASCRIPT_FUNCTION</key>
|
||||
<string>olderOsx</string>
|
||||
<key>IC_REQUIREMENT_JAVASCRIPT_PARAMETERS</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>IDENTIFIER</key>
|
||||
<string>fr.whitebox.Packages.requirement.javascript</string>
|
||||
<key>MESSAGE</key>
|
||||
<array/>
|
||||
<key>NAME</key>
|
||||
<string>JavaScript</string>
|
||||
<key>STATE</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>TITLE</key>
|
||||
<array/>
|
||||
<key>TOOLTIP</key>
|
||||
<array/>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>UUID</key>
|
||||
<string>1D2C47E0-5FD3-4623-B934-1347C66782D0</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>REMOVED</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>INSTALLATION TYPE</key>
|
||||
<integer>1</integer>
|
||||
<key>MODE</key>
|
||||
<integer>1</integer>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>INSTALLATION_STEPS</key>
|
||||
<array>
|
||||
@@ -674,7 +1310,7 @@
|
||||
<key>LANGUAGE</key>
|
||||
<string>English</string>
|
||||
<key>VALUE</key>
|
||||
<string>@APPLICATION_NAME_XML_ESCAPED@ Client</string>
|
||||
<string>@APPLICATION_NAME_XML_ESCAPED@</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
@@ -701,7 +1337,7 @@
|
||||
<key>BUILD_PATH</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>@CMAKE_INSTALL_PREFIX@/.</string>
|
||||
<string>../install/.</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
@@ -879,6 +1515,18 @@
|
||||
<string>@CMAKE_INSTALL_DIR@</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SHARED_GLOBAL_DATA</key>
|
||||
<dict>
|
||||
<key>IC_REQUIREMENT_JAVASCRIPT_SHARED_SOURCE_CODE</key>
|
||||
<string>
|
||||
function olderOsx() {
|
||||
if(system.compareVersions(system.version.ProductVersion, '10.10') == -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
</string>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
<key>VERSION</key>
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
# kill the old version. see issue #2044
|
||||
killall @APPLICATION_EXECUTABLE@
|
||||
|
||||
installer -pkg FUSE\ for\ macOS\ 3.8.1.pkg -target / -applyChoiceChangesXML settings.plist
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<file>resources/public.svg</file>
|
||||
<file>resources/confirm.svg</file>
|
||||
<file>resources/copy.svg</file>
|
||||
<file>resources/state-sync.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/"/>
|
||||
</RCC>
|
||||
|
||||
@@ -45,6 +45,9 @@ macro (KDE4_ADD_APP_ICON appsources pattern)
|
||||
if (fn MATCHES ".*128.*" )
|
||||
list (APPEND _icons ${it})
|
||||
endif (fn MATCHES ".*128.*")
|
||||
if (fn MATCHES ".*256.*" )
|
||||
list (APPEND _icons ${it})
|
||||
endif (fn MATCHES ".*256.*")
|
||||
endforeach (it)
|
||||
if (_icons)
|
||||
add_custom_command(OUTPUT ${_outfilename}.ico ${_outfilename}.rc
|
||||
|
||||
@@ -168,7 +168,8 @@ function(ecm_add_app_icon appsources)
|
||||
${icons_at_32px}
|
||||
${icons_at_48px}
|
||||
${icons_at_64px}
|
||||
${icons_at_128px})
|
||||
${icons_at_128px}
|
||||
${icons_at_256px})
|
||||
if (NOT windows_icons)
|
||||
message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
|
||||
endif()
|
||||
|
||||
43
cmake/modules/FindFUSE.cmake
Normal file
43
cmake/modules/FindFUSE.cmake
Normal file
@@ -0,0 +1,43 @@
|
||||
# Find the FUSE includes and library
|
||||
#
|
||||
# Once done this will define
|
||||
# FUSE_FOUND - system has FUSE
|
||||
# FUSE_INCLUDE_DIR - the FUSE include directory
|
||||
# FUSE_LIBRARIES - List of libraries when using FUSE.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
|
||||
# check if already in cache, be silent
|
||||
IF (FUSE_INCLUDE_DIR)
|
||||
SET (FUSE_FIND_QUIETLY TRUE)
|
||||
ENDIF (FUSE_INCLUDE_DIR)
|
||||
|
||||
# find includes
|
||||
FIND_PATH (FUSE_INCLUDE_DIR fuse.h
|
||||
/usr/local/include/osxfuse
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
# find lib
|
||||
if (APPLE)
|
||||
SET(FUSE_NAMES libosxfuse.dylib fuse)
|
||||
else (APPLE)
|
||||
SET(FUSE_NAMES fuse)
|
||||
endif (APPLE)
|
||||
|
||||
FIND_LIBRARY(FUSE_LIBRARIES
|
||||
NAMES ${FUSE_NAMES}
|
||||
PATHS /lib64 /lib /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(FUSE DEFAULT_MSG FUSE_INCLUDE_DIR FUSE_LIBRARIES)
|
||||
mark_as_advanced(FUSE_INCLUDE_DIR FUSE_LIBRARIES)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>@APPLICATION_EXECUTABLE@</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>ownCloud.icns</string>
|
||||
<string>@APPLICATION_ICON_NAME@.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>@APPLICATION_REV_DOMAIN@</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
|
||||
865
dokanLib/dokan.h
Normal file
865
dokanLib/dokan.h
Normal file
@@ -0,0 +1,865 @@
|
||||
/*
|
||||
Dokan : user-mode file system library for Windows
|
||||
|
||||
Copyright (C) 2015 - 2018 Adrien J. <liryna.stark@gmail.com> and Maxime C. <maxime@islog.com>
|
||||
Copyright (C) 2007 - 2011 Hiroki Asakawa <info@dokan-dev.net>
|
||||
|
||||
http://dokan-dev.github.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 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.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DOKAN_H_
|
||||
#define DOKAN_H_
|
||||
|
||||
/** Do not include NTSTATUS. Fix duplicate preprocessor definitions */
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#include <ntstatus.h>
|
||||
|
||||
#include "fileinfo.h"
|
||||
#include "public.h"
|
||||
|
||||
#ifdef _EXPORTING
|
||||
/** Export dokan API see also dokan.def for export */
|
||||
#define DOKANAPI __stdcall
|
||||
#else
|
||||
/** Import dokan API */
|
||||
#define DOKANAPI __declspec(dllimport) __stdcall
|
||||
#endif
|
||||
|
||||
/** Change calling convention to standard call */
|
||||
#define DOKAN_CALLBACK __stdcall
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @file */
|
||||
|
||||
/**
|
||||
* \defgroup Dokan Dokan
|
||||
* \brief Dokan Library const and methods
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/** The current Dokan version (ver 1.0.0). \ref DOKAN_OPTIONS.Version */
|
||||
#define DOKAN_VERSION 100
|
||||
/** Minimum Dokan version (ver 1.0.0) accepted. */
|
||||
#define DOKAN_MINIMUM_COMPATIBLE_VERSION 100
|
||||
/** Maximum number of dokan instances.*/
|
||||
#define DOKAN_MAX_INSTANCES 32
|
||||
/** Driver file name including the DOKAN_MAJOR_API_VERSION */
|
||||
#define DOKAN_DRIVER_NAME L"dokan" DOKAN_MAJOR_API_VERSION L".sys"
|
||||
/** Network provider name including the DOKAN_MAJOR_API_VERSION */
|
||||
#define DOKAN_NP_NAME L"Dokan" DOKAN_MAJOR_API_VERSION
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* \defgroup DOKAN_OPTION DOKAN_OPTION
|
||||
* \brief All DOKAN_OPTION flags used in DOKAN_OPTIONS.Options
|
||||
* \see DOKAN_FILE_INFO
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/** Enable ouput debug message */
|
||||
#define DOKAN_OPTION_DEBUG 1
|
||||
/** Enable ouput debug message to stderr */
|
||||
#define DOKAN_OPTION_STDERR 2
|
||||
/** Use alternate stream */
|
||||
#define DOKAN_OPTION_ALT_STREAM 4
|
||||
/** Enable mount drive as write-protected */
|
||||
#define DOKAN_OPTION_WRITE_PROTECT 8
|
||||
/** Use network drive - Dokan network provider needs to be installed */
|
||||
#define DOKAN_OPTION_NETWORK 16
|
||||
/** Use removable drive */
|
||||
#define DOKAN_OPTION_REMOVABLE 32
|
||||
/** Use mount manager */
|
||||
#define DOKAN_OPTION_MOUNT_MANAGER 64
|
||||
/** Mount the drive on current session only */
|
||||
#define DOKAN_OPTION_CURRENT_SESSION 128
|
||||
/** Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it */
|
||||
#define DOKAN_OPTION_FILELOCK_USER_MODE 256
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* \struct DOKAN_OPTIONS
|
||||
* \brief Dokan mount options used to describe Dokan device behavior.
|
||||
* \see DokanMain
|
||||
*/
|
||||
typedef struct _DOKAN_OPTIONS {
|
||||
/** Version of the Dokan features requested (version "123" is equal to Dokan version 1.2.3). */
|
||||
USHORT Version;
|
||||
/** Number of threads to be used internally by Dokan library. More threads will handle more events at the same time. */
|
||||
USHORT ThreadCount;
|
||||
/** Features enabled for the mount. See \ref DOKAN_OPTION. */
|
||||
ULONG Options;
|
||||
/** FileSystem can store anything here. */
|
||||
ULONG64 GlobalContext;
|
||||
/** Mount point. Can be "M:\" (drive letter) or "C:\mount\dokan" (path in NTFS). */
|
||||
LPCWSTR MountPoint;
|
||||
/**
|
||||
* UNC Name for the Network Redirector
|
||||
* \see <a href="https://msdn.microsoft.com/en-us/library/windows/hardware/ff556761(v=vs.85).aspx">Support for UNC Naming</a>
|
||||
*/
|
||||
LPCWSTR UNCName;
|
||||
/** Max timeout in milliseconds of each request before Dokan gives up. */
|
||||
ULONG Timeout;
|
||||
/** Allocation Unit Size of the volume. This will affect the file size. */
|
||||
ULONG AllocationUnitSize;
|
||||
/** Sector Size of the volume. This will affect the file size. */
|
||||
ULONG SectorSize;
|
||||
} DOKAN_OPTIONS, *PDOKAN_OPTIONS;
|
||||
|
||||
/**
|
||||
* \struct DOKAN_FILE_INFO
|
||||
* \brief Dokan file information on the current operation.
|
||||
*/
|
||||
typedef struct _DOKAN_FILE_INFO {
|
||||
/**
|
||||
* Context that can be used to carry information between operations.
|
||||
* The context can carry whatever type like \c HANDLE, struct, int,
|
||||
* internal reference that will help the implementation understand the request context of the event.
|
||||
*/
|
||||
ULONG64 Context;
|
||||
/** Reserved. Used internally by Dokan library. Never modify. */
|
||||
ULONG64 DokanContext;
|
||||
/** A pointer to DOKAN_OPTIONS which was passed to DokanMain. */
|
||||
PDOKAN_OPTIONS DokanOptions;
|
||||
/**
|
||||
* Process ID for the thread that originally requested a given I/O operation.
|
||||
*/
|
||||
ULONG ProcessId;
|
||||
/**
|
||||
* Requesting a directory file.
|
||||
* Must be set in \ref DOKAN_OPERATIONS.ZwCreateFile if the file appears to be a folder.
|
||||
*/
|
||||
UCHAR IsDirectory;
|
||||
/** Flag if the file has to be deleted during DOKAN_OPERATIONS. Cleanup event. */
|
||||
UCHAR DeleteOnClose;
|
||||
/** Read or write is paging IO. */
|
||||
UCHAR PagingIo;
|
||||
/** Read or write is synchronous IO. */
|
||||
UCHAR SynchronousIo;
|
||||
/** Read or write directly from data source without cache */
|
||||
UCHAR Nocache;
|
||||
/** If \c TRUE, write to the current end of file instead of using the Offset parameter. */
|
||||
UCHAR WriteToEndOfFile;
|
||||
} DOKAN_FILE_INFO, *PDOKAN_FILE_INFO;
|
||||
|
||||
/**
|
||||
* \brief FillFindData Used to add an entry in FindFiles operation
|
||||
* \return 1 if buffer is full, otherwise 0 (currently it never returns 1)
|
||||
*/
|
||||
typedef int(WINAPI *PFillFindData)(PWIN32_FIND_DATAW, PDOKAN_FILE_INFO);
|
||||
|
||||
/**
|
||||
* \brief FillFindStreamData Used to add an entry in FindStreams
|
||||
* \return 1 if buffer is full, otherwise 0 (currently it never returns 1)
|
||||
*/
|
||||
typedef int(WINAPI *PFillFindStreamData)(PWIN32_FIND_STREAM_DATA,
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
// clang-format off
|
||||
|
||||
/**
|
||||
* \struct DOKAN_OPERATIONS
|
||||
* \brief Dokan API callbacks interface
|
||||
*
|
||||
* DOKAN_OPERATIONS is a struct of callbacks that describe all Dokan API operations
|
||||
* that will be called when Windows access to the filesystem.
|
||||
*
|
||||
* If an error occurs, return NTSTATUS (https://support.microsoft.com/en-us/kb/113996).
|
||||
* Win32 Error can be converted to \c NTSTATUS with \ref DokanNtStatusFromWin32
|
||||
*
|
||||
* All callbacks can be set to \c NULL or return \c STATUS_NOT_IMPLEMENTED
|
||||
* if supporting one of them is not desired. Be aware that returning such values to important callbacks
|
||||
* such as DOKAN_OPERATIONS.ZwCreateFile / DOKAN_OPERATIONS.ReadFile / ... would make the filesystem not work or become unstable.
|
||||
*/
|
||||
typedef struct _DOKAN_OPERATIONS {
|
||||
/**
|
||||
* \brief CreateFile Dokan API callback
|
||||
*
|
||||
* CreateFile is called each time a request is made on a file system object.
|
||||
*
|
||||
* In case \c OPEN_ALWAYS & \c CREATE_ALWAYS are successfully opening an
|
||||
* existing file, \c STATUS_OBJECT_NAME_COLLISION should be returned instead of \c STATUS_SUCCESS .
|
||||
* This will inform Dokan that the file has been opened and not created during the request.
|
||||
*
|
||||
* If the file is a directory, CreateFile is also called.
|
||||
* In this case, CreateFile should return \c STATUS_SUCCESS when that directory
|
||||
* can be opened and DOKAN_FILE_INFO.IsDirectory has to be set to \c TRUE.
|
||||
* On the other hand, if DOKAN_FILE_INFO.IsDirectory is set to \c TRUE
|
||||
* but the path targets a file, \c STATUS_NOT_A_DIRECTORY must be returned.
|
||||
*
|
||||
* DOKAN_FILE_INFO.Context can be used to store Data (like \c HANDLE)
|
||||
* that can be retrieved in all other requests related to the Context.
|
||||
* To avoid memory leak, Context needs to be released in DOKAN_OPERATIONS.Cleanup.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param SecurityContext SecurityContext, see https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx
|
||||
* \param DesiredAccess Specifies an <a href="https://msdn.microsoft.com/en-us/library/windows/hardware/ff540466(v=vs.85).aspx">ACCESS_MASK</a> value that determines the requested access to the object.
|
||||
* \param FileAttributes Specifies one or more FILE_ATTRIBUTE_XXX flags, which represent the file attributes to set if a file is created or overwritten.
|
||||
* \param ShareAccess Type of share access, which is specified as zero or any combination of FILE_SHARE_* flags.
|
||||
* \param CreateDisposition Specifies the action to perform if the file does or does not exist.
|
||||
* \param CreateOptions Specifies the options to apply when the driver creates or opens the file.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see <a href="https://msdn.microsoft.com/en-us/library/windows/hardware/ff566424(v=vs.85).aspx">See ZwCreateFile for more information about the parameters of this callback (MSDN).</a>
|
||||
* \see DokanMapKernelToUserCreateFileFlags
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *ZwCreateFile)(LPCWSTR FileName,
|
||||
PDOKAN_IO_SECURITY_CONTEXT SecurityContext,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
ULONG FileAttributes,
|
||||
ULONG ShareAccess,
|
||||
ULONG CreateDisposition,
|
||||
ULONG CreateOptions,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief Cleanup Dokan API callback
|
||||
*
|
||||
* Cleanup request before \ref CloseFile is called.
|
||||
*
|
||||
* When DOKAN_FILE_INFO.DeleteOnClose is \c TRUE, the file in Cleanup must be deleted.
|
||||
* See DeleteFile documentation for explanation.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \see DeleteFile
|
||||
* \see DeleteDirectory
|
||||
*/
|
||||
void(DOKAN_CALLBACK *Cleanup)(LPCWSTR FileName,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief CloseFile Dokan API callback
|
||||
*
|
||||
* Clean remaining Context
|
||||
*
|
||||
* CloseFile is called at the end of the life of the context.
|
||||
* Anything remaining in \ref DOKAN_FILE_INFO.Context must be cleared before returning.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
*/
|
||||
void(DOKAN_CALLBACK *CloseFile)(LPCWSTR FileName,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief ReadFile Dokan API callback
|
||||
*
|
||||
* ReadFile callback on the file previously opened in DOKAN_OPERATIONS.ZwCreateFile.
|
||||
* It can be called by different threads at the same time, so the read/context has to be thread safe.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param Buffer Read buffer that has to be filled with the read result.
|
||||
* \param BufferLength Buffer length and read size to continue with.
|
||||
* \param ReadLength Total data size that has been read.
|
||||
* \param Offset Offset from where the read has to be continued.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see WriteFile
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *ReadFile)(LPCWSTR FileName,
|
||||
LPVOID Buffer,
|
||||
DWORD BufferLength,
|
||||
LPDWORD ReadLength,
|
||||
LONGLONG Offset,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief WriteFile Dokan API callback
|
||||
*
|
||||
* WriteFile callback on the file previously opened in DOKAN_OPERATIONS.ZwCreateFile
|
||||
* It can be called by different threads at the same time, sp the write/context has to be thread safe.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param Buffer Data that has to be written.
|
||||
* \param NumberOfBytesToWrite Buffer length and write size to continue with.
|
||||
* \param NumberOfBytesWritten Total number of bytes that have been written.
|
||||
* \param Offset Offset from where the write has to be continued.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see ReadFile
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *WriteFile)(LPCWSTR FileName,
|
||||
LPCVOID Buffer,
|
||||
DWORD NumberOfBytesToWrite,
|
||||
LPDWORD NumberOfBytesWritten,
|
||||
LONGLONG Offset,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief FlushFileBuffers Dokan API callback
|
||||
*
|
||||
* Clears buffers for this context and causes any buffered data to be written to the file.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *FlushFileBuffers)(LPCWSTR FileName,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief GetFileInformation Dokan API callback
|
||||
*
|
||||
* Get specific information on a file.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param Buffer BY_HANDLE_FILE_INFORMATION struct to fill.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *GetFileInformation)(LPCWSTR FileName,
|
||||
LPBY_HANDLE_FILE_INFORMATION Buffer,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief FindFiles Dokan API callback
|
||||
*
|
||||
* List all files in the requested path
|
||||
* \ref DOKAN_OPERATIONS.FindFilesWithPattern is checked first. If it is not implemented or
|
||||
* returns \c STATUS_NOT_IMPLEMENTED, then FindFiles is called, if implemented.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param FillFindData Callback that has to be called with PWIN32_FIND_DATAW that contain file information.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see FindFilesWithPattern
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *FindFiles)(LPCWSTR FileName,
|
||||
PFillFindData FillFindData,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief FindFilesWithPattern Dokan API callback
|
||||
*
|
||||
* Same as \ref DOKAN_OPERATIONS.FindFiles but with a search pattern.
|
||||
*
|
||||
* \param PathName Path requested by the Kernel on the FileSystem.
|
||||
* \param SearchPattern Search pattern.
|
||||
* \param FillFindData Callback that has to be called with PWIN32_FIND_DATAW that contains file information.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see FindFiles
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *FindFilesWithPattern)(LPCWSTR PathName,
|
||||
LPCWSTR SearchPattern,
|
||||
PFillFindData FillFindData,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief SetFileAttributes Dokan API callback
|
||||
*
|
||||
* Set file attributes on a specific file
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param FileAttributes FileAttributes to set on file.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *SetFileAttributes)(LPCWSTR FileName,
|
||||
DWORD FileAttributes,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief SetFileTime Dokan API callback
|
||||
*
|
||||
* Set file attributes on a specific file
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param CreationTime Creation FILETIME.
|
||||
* \param LastAccessTime LastAccess FILETIME.
|
||||
* \param LastWriteTime LastWrite FILETIME.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *SetFileTime)(LPCWSTR FileName,
|
||||
CONST FILETIME *CreationTime,
|
||||
CONST FILETIME *LastAccessTime,
|
||||
CONST FILETIME *LastWriteTime,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief DeleteFile Dokan API callback
|
||||
*
|
||||
* Check if it is possible to delete a file.
|
||||
*
|
||||
* DeleteFile will also be called with DOKAN_FILE_INFO.DeleteOnClose set to \c FALSE
|
||||
* to notify the driver when the file is no longer requested to be deleted.
|
||||
*
|
||||
* The file in DeleteFile should not be deleted, but instead the file
|
||||
* must be checked as to whether or not it can be deleted,
|
||||
* and \c STATUS_SUCCESS should be returned (when it can be deleted) or
|
||||
* appropriate error codes, such as \c STATUS_ACCESS_DENIED or
|
||||
* \c STATUS_OBJECT_NAME_NOT_FOUND, should be returned.
|
||||
*
|
||||
* When \c STATUS_SUCCESS is returned, a Cleanup call is received afterwards with
|
||||
* DOKAN_FILE_INFO.DeleteOnClose set to \c TRUE. Only then must the closing file
|
||||
* be deleted.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see DeleteDirectory
|
||||
* \see Cleanup
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *DeleteFile)(LPCWSTR FileName,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief DeleteDirectory Dokan API callback
|
||||
*
|
||||
* Check if it is possible to delete a directory.
|
||||
*
|
||||
* DeleteDirectory will also be called with DOKAN_FILE_INFO.DeleteOnClose set to \c FALSE
|
||||
* to notify the driver when the file is no longer requested to be deleted.
|
||||
*
|
||||
* The Directory in DeleteDirectory should not be deleted, but instead
|
||||
* must be checked as to whether or not it can be deleted,
|
||||
* and \c STATUS_SUCCESS should be returned (when it can be deleted) or
|
||||
* appropriate error codes, such as \c STATUS_ACCESS_DENIED,
|
||||
* \c STATUS_OBJECT_PATH_NOT_FOUND, or \c STATUS_DIRECTORY_NOT_EMPTY, should
|
||||
* be returned.
|
||||
*
|
||||
* When \c STATUS_SUCCESS is returned, a Cleanup call is received afterwards with
|
||||
* DOKAN_FILE_INFO.DeleteOnClose set to \c TRUE. Only then must the closing file
|
||||
* be deleted.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or \c NTSTATUS appropriate to the request result.
|
||||
* \ref DeleteFile
|
||||
* \ref Cleanup
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *DeleteDirectory)(LPCWSTR FileName,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief MoveFile Dokan API callback
|
||||
*
|
||||
* Move a file or directory to a new destination
|
||||
*
|
||||
* \param FileName Path for the file to be moved.
|
||||
* \param NewFileName Path for the new location of the file.
|
||||
* \param ReplaceIfExisting If destination already exists, can it be replaced?
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *MoveFile)(LPCWSTR FileName,
|
||||
LPCWSTR NewFileName,
|
||||
BOOL ReplaceIfExisting,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief SetEndOfFile Dokan API callback
|
||||
*
|
||||
* SetEndOfFile is used to truncate or extend a file (physical file size).
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param ByteOffset File length to set.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *SetEndOfFile)(LPCWSTR FileName,
|
||||
LONGLONG ByteOffset,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief SetAllocationSize Dokan API callback
|
||||
*
|
||||
* SetAllocationSize is used to truncate or extend a file.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param AllocSize File length to set.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *SetAllocationSize)(LPCWSTR FileName,
|
||||
LONGLONG AllocSize,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief LockFile Dokan API callback
|
||||
*
|
||||
* Lock file at a specific offset and data length.
|
||||
* This is only used if \ref DOKAN_OPTION_FILELOCK_USER_MODE is enabled.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param ByteOffset Offset from where the lock has to be continued.
|
||||
* \param Length Data length to lock.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see UnlockFile
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *LockFile)(LPCWSTR FileName,
|
||||
LONGLONG ByteOffset,
|
||||
LONGLONG Length,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief UnlockFile Dokan API callback
|
||||
*
|
||||
* Unlock file at a specific offset and data length.
|
||||
* This is only used if \ref DOKAN_OPTION_FILELOCK_USER_MODE is enabled.
|
||||
*
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param ByteOffset Offset from where the lock has to be continued.
|
||||
* \param Length Data length to lock.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see LockFile
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *UnlockFile)(LPCWSTR FileName,
|
||||
LONGLONG ByteOffset,
|
||||
LONGLONG Length,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief GetDiskFreeSpace Dokan API callback
|
||||
*
|
||||
* Retrieves information about the amount of space that is available on a disk volume.
|
||||
* It consits of the total amount of space, the total amount of free space, and
|
||||
* the total amount of free space available to the user that is associated with the calling thread.
|
||||
*
|
||||
* Neither GetDiskFreeSpace nor \ref GetVolumeInformation
|
||||
* save the DOKAN_FILE_INFO.Context.
|
||||
* Before these methods are called, \ref ZwCreateFile may not be called.
|
||||
* (ditto \ref CloseFile and \ref Cleanup)
|
||||
*
|
||||
* \param FreeBytesAvailable Amount of available space.
|
||||
* \param TotalNumberOfBytes Total size of storage space
|
||||
* \param TotalNumberOfFreeBytes Amount of free space
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or \c NTSTATUS appropriate to the request result.
|
||||
* \see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa364937(v=vs.85).aspx"> GetDiskFreeSpaceEx function (MSDN)</a>
|
||||
* \see GetVolumeInformation
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *GetDiskFreeSpace)(PULONGLONG FreeBytesAvailable,
|
||||
PULONGLONG TotalNumberOfBytes,
|
||||
PULONGLONG TotalNumberOfFreeBytes,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief GetVolumeInformation Dokan API callback
|
||||
*
|
||||
* Retrieves information about the file system and volume associated with the specified root directory.
|
||||
*
|
||||
* Neither GetVolumeInformation nor GetDiskFreeSpace
|
||||
* save the \ref DOKAN_FILE_INFO#Context.
|
||||
* Before these methods are called, \ref ZwCreateFile may not be called.
|
||||
* (ditto \ref CloseFile and \ref Cleanup)
|
||||
*
|
||||
* FileSystemName could be anything up to 10 characters.
|
||||
* But Windows check few feature availability based on file system name.
|
||||
* For this, it is recommended to set NTFS or FAT here.
|
||||
*
|
||||
* \c FILE_READ_ONLY_VOLUME is automatically added to the
|
||||
* FileSystemFlags if \ref DOKAN_OPTION_WRITE_PROTECT was
|
||||
* specified in DOKAN_OPTIONS when the volume was mounted.
|
||||
*
|
||||
* \param VolumeNameBuffer A pointer to a buffer that receives the name of a specified volume.
|
||||
* \param VolumeNameSize The length of a volume name buffer.
|
||||
* \param VolumeSerialNumber A pointer to a variable that receives the volume serial number.
|
||||
* \param MaximumComponentLength A pointer to a variable that receives the maximum length.
|
||||
* \param FileSystemFlags A pointer to a variable that receives flags associated with the specified file system.
|
||||
* \param FileSystemNameBuffer A pointer to a buffer that receives the name of the file system.
|
||||
* \param FileSystemNameSize The length of the file system name buffer.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx"> GetVolumeInformation function (MSDN)</a>
|
||||
* \see GetDiskFreeSpace
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *GetVolumeInformation)(LPWSTR VolumeNameBuffer,
|
||||
DWORD VolumeNameSize,
|
||||
LPDWORD VolumeSerialNumber,
|
||||
LPDWORD MaximumComponentLength,
|
||||
LPDWORD FileSystemFlags,
|
||||
LPWSTR FileSystemNameBuffer,
|
||||
DWORD FileSystemNameSize,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief Mounted Dokan API callback
|
||||
*
|
||||
* Called when Dokan successfully mounts the volume.
|
||||
*
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see Unmounted
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *Mounted)(PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief Unmounted Dokan API callback
|
||||
*
|
||||
* Called when Dokan is unmounting the volume.
|
||||
*
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or \c NTSTATUS appropriate to the request result.
|
||||
* \see Unmounted
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *Unmounted)(PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief GetFileSecurity Dokan API callback
|
||||
*
|
||||
* Get specified information about the security of a file or directory.
|
||||
*
|
||||
* Return \c STATUS_NOT_IMPLEMENTED to let dokan library build a sddl of the current process user with authenticate user rights for context menu.
|
||||
* Return \c STATUS_BUFFER_OVERFLOW if buffer size is too small.
|
||||
*
|
||||
* \since Supported since version 0.6.0. The version must be specified in \ref DOKAN_OPTIONS.Version.
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param SecurityInformation A SECURITY_INFORMATION value that identifies the security information being requested.
|
||||
* \param SecurityDescriptor A pointer to a buffer that receives a copy of the security descriptor of the requested file.
|
||||
* \param BufferLength Specifies the size, in bytes, of the buffer.
|
||||
* \param LengthNeeded A pointer to the variable that receives the number of bytes necessary to store the complete security descriptor.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see SetFileSecurity
|
||||
* \see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa446639(v=vs.85).aspx">GetFileSecurity function (MSDN)</a>
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *GetFileSecurity)(LPCWSTR FileName,
|
||||
PSECURITY_INFORMATION SecurityInformation,
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
ULONG BufferLength,
|
||||
PULONG LengthNeeded,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief SetFileSecurity Dokan API callback
|
||||
*
|
||||
* Sets the security of a file or directory object.
|
||||
*
|
||||
* \since Supported since version 0.6.0. The version must be specified in \ref DOKAN_OPTIONS.Version.
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param SecurityInformation Structure that identifies the contents of the security descriptor pointed by \a SecurityDescriptor param.
|
||||
* \param SecurityDescriptor A pointer to a SECURITY_DESCRIPTOR structure.
|
||||
* \param BufferLength Specifies the size, in bytes, of the buffer.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
* \see GetFileSecurity
|
||||
* \see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379577(v=vs.85).aspx">SetFileSecurity function (MSDN)</a>
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *SetFileSecurity)(LPCWSTR FileName,
|
||||
PSECURITY_INFORMATION SecurityInformation,
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
ULONG BufferLength,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief FindStreams Dokan API callback
|
||||
*
|
||||
* Retrieve all NTFS Streams informations on the file.
|
||||
* This is only called if \ref DOKAN_OPTION_ALT_STREAM is enabled.
|
||||
*
|
||||
* \since Supported since version 0.8.0. The version must be specified in \ref DOKAN_OPTIONS.Version.
|
||||
* \param FileName File path requested by the Kernel on the FileSystem.
|
||||
* \param FillFindStreamData Callback that has to be called with PWIN32_FIND_STREAM_DATA that contain stream information.
|
||||
* \param DokanFileInfo Information about the file or directory.
|
||||
* \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result.
|
||||
*/
|
||||
NTSTATUS(DOKAN_CALLBACK *FindStreams)(LPCWSTR FileName,
|
||||
PFillFindStreamData FillFindStreamData,
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
} DOKAN_OPERATIONS, *PDOKAN_OPERATIONS;
|
||||
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* \defgroup DokanMainResult DokanMainResult
|
||||
* \brief \ref DokanMain returns error codes
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/** Dokan mount succeed. */
|
||||
#define DOKAN_SUCCESS 0
|
||||
/** Dokan mount error. */
|
||||
#define DOKAN_ERROR -1
|
||||
/** Dokan mount failed - Bad drive letter. */
|
||||
#define DOKAN_DRIVE_LETTER_ERROR -2
|
||||
/** Dokan mount failed - Can't install driver. */
|
||||
#define DOKAN_DRIVER_INSTALL_ERROR -3
|
||||
/** Dokan mount failed - Driver answer that something is wrong. */
|
||||
#define DOKAN_START_ERROR -4
|
||||
/**
|
||||
* Dokan mount failed.
|
||||
* Can't assign a drive letter or mount point.
|
||||
* Probably already used by another volume.
|
||||
*/
|
||||
#define DOKAN_MOUNT_ERROR -5
|
||||
/**
|
||||
* Dokan mount failed.
|
||||
* Mount point is invalid.
|
||||
*/
|
||||
#define DOKAN_MOUNT_POINT_ERROR -6
|
||||
/**
|
||||
* Dokan mount failed.
|
||||
* Requested an incompatible version.
|
||||
*/
|
||||
#define DOKAN_VERSION_ERROR -7
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* \defgroup Dokan Dokan
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/**
|
||||
* \brief Mount a new Dokan Volume.
|
||||
*
|
||||
* This function block until the device is unmounted.
|
||||
* If the mount fails, it will directly return a \ref DokanMainResult error.
|
||||
*
|
||||
* \param DokanOptions a \ref DOKAN_OPTIONS that describe the mount.
|
||||
* \param DokanOperations Instance of \ref DOKAN_OPERATIONS that will be called for each request made by the kernel.
|
||||
* \return \ref DokanMainResult status.
|
||||
*/
|
||||
int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions,
|
||||
PDOKAN_OPERATIONS DokanOperations);
|
||||
|
||||
/**
|
||||
* \brief Unmount a Dokan device from a driver letter.
|
||||
*
|
||||
* \param DriveLetter Dokan driver letter to unmount.
|
||||
* \return \c TRUE if device was unmounted or False in case of failure or device not found.
|
||||
*/
|
||||
BOOL DOKANAPI DokanUnmount(WCHAR DriveLetter);
|
||||
|
||||
/**
|
||||
* \brief Unmount a Dokan device from a mount point
|
||||
*
|
||||
* \param MountPoint Mount point to unmount ("Z", "Z:", "Z:\", "Z:\MyMountPoint").
|
||||
* \return \c TRUE if device was unmounted or False in case of failure or device not found.
|
||||
*/
|
||||
BOOL DOKANAPI DokanRemoveMountPoint(LPCWSTR MountPoint);
|
||||
|
||||
/**
|
||||
* \brief Unmount a Dokan device from a mount point
|
||||
*
|
||||
* Same as \ref DokanRemoveMountPoint
|
||||
* If Safe is \c TRUE, it will broadcast to all desktops and Shells
|
||||
* Safe should not be used during DLL_PROCESS_DETACH
|
||||
*
|
||||
* \see DokanRemoveMountPoint
|
||||
*
|
||||
* \param MountPoint Mount point to unmount ("Z", "Z:", "Z:\", "Z:\MyMountPoint").
|
||||
* \param Safe Process is not in DLL_PROCESS_DETACH state.
|
||||
* \return True if device was unmounted or False in case of failure or device not found.
|
||||
*/
|
||||
BOOL DOKANAPI DokanRemoveMountPointEx(LPCWSTR MountPoint, BOOL Safe);
|
||||
|
||||
/**
|
||||
* \brief Checks whether Name matches Expression
|
||||
*
|
||||
* \param Expression Expression can contain wildcard characters (? and *)
|
||||
* \param Name Name to check
|
||||
* \param IgnoreCase Case sensitive or not
|
||||
* \return result if name matches the expression
|
||||
*/
|
||||
BOOL DOKANAPI DokanIsNameInExpression(LPCWSTR Expression, LPCWSTR Name,
|
||||
BOOL IgnoreCase);
|
||||
|
||||
/**
|
||||
* \brief Get the version of Dokan.
|
||||
* The returned ULONG is the version number without the dots.
|
||||
* \return The version of Dokan
|
||||
*/
|
||||
ULONG DOKANAPI DokanVersion();
|
||||
|
||||
/**
|
||||
* \brief Get the version of the Dokan driver.
|
||||
* The returned ULONG is the version number without the dots.
|
||||
* \return The version of Dokan driver.
|
||||
*/
|
||||
ULONG DOKANAPI DokanDriverVersion();
|
||||
|
||||
/**
|
||||
* \brief Extends the timeout of the current IO operation in driver.
|
||||
*
|
||||
* \param Timeout Extended time in milliseconds requested.
|
||||
* \param DokanFileInfo \ref DOKAN_FILE_INFO of the operation to extend.
|
||||
* \return If the operation was successful.
|
||||
*/
|
||||
BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief Get the handle to Access Token.
|
||||
*
|
||||
* This method needs be called in <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx">CreateFile</a> callback.
|
||||
* The caller must call <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx">CloseHandle</a>
|
||||
* for the returned handle.
|
||||
*
|
||||
* \param DokanFileInfo \ref DOKAN_FILE_INFO of the operation to extend.
|
||||
* \return A handle to the account token for the user on whose behalf the code is running.
|
||||
*/
|
||||
HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
/**
|
||||
* \brief Get active Dokan mount points.
|
||||
*
|
||||
* \param list Allocate array of DOKAN_CONTROL.
|
||||
* \param length Number of \ref DOKAN_CONTROL instances in list.
|
||||
* \param uncOnly Get only instances that have UNC Name.
|
||||
* \param nbRead Number of instances successfully retrieved.
|
||||
* \return List retrieved or not.
|
||||
*/
|
||||
BOOL DOKANAPI DokanGetMountPointList(PDOKAN_CONTROL list, ULONG length,
|
||||
BOOL uncOnly, PULONG nbRead);
|
||||
|
||||
/**
|
||||
* \brief Convert \ref DOKAN_OPERATIONS.ZwCreateFile parameters to <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx">CreateFile</a> parameters.
|
||||
*
|
||||
* Dokan Kernel forward the DesiredAccess directly from the IRP_MJ_CREATE.
|
||||
* This DesiredAccess has been converted from generic rights (user CreateFile request) to standard rights and will be converted back here.
|
||||
* https://msdn.microsoft.com/windows/hardware/drivers/ifs/access-mask
|
||||
*
|
||||
* \param DesiredAccess DesiredAccess from \ref DOKAN_OPERATIONS.ZwCreateFile.
|
||||
* \param FileAttributes FileAttributes from \ref DOKAN_OPERATIONS.ZwCreateFile.
|
||||
* \param CreateOptions CreateOptions from \ref DOKAN_OPERATIONS.ZwCreateFile.
|
||||
* \param CreateDisposition CreateDisposition from \ref DOKAN_OPERATIONS.ZwCreateFile.
|
||||
* \param outDesiredAccess New <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx">CreateFile</a> dwDesiredAccess.
|
||||
* \param outFileAttributesAndFlags New <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx">CreateFile</a> dwFlagsAndAttributes.
|
||||
* \param outCreationDisposition New <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx">CreateFile</a> dwCreationDisposition.
|
||||
* \see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx">CreateFile function (MSDN)</a>
|
||||
*/
|
||||
void DOKANAPI DokanMapKernelToUserCreateFileFlags(
|
||||
ACCESS_MASK DesiredAccess, ULONG FileAttributes, ULONG CreateOptions, ULONG CreateDisposition,
|
||||
ACCESS_MASK* outDesiredAccess, DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition);
|
||||
|
||||
/**
|
||||
* \brief Convert WIN32 error to NTSTATUS
|
||||
*
|
||||
* https://support.microsoft.com/en-us/kb/113996
|
||||
*
|
||||
* \param Error Win32 Error to convert
|
||||
* \return NTSTATUS associate to the ERROR.
|
||||
*/
|
||||
NTSTATUS DOKANAPI DokanNtStatusFromWin32(DWORD Error);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // DOKAN_H_
|
||||
BIN
dokanLib/dokan1.lib
Normal file
BIN
dokanLib/dokan1.lib
Normal file
Binary file not shown.
1252
dokanLib/fileinfo.h
Normal file
1252
dokanLib/fileinfo.h
Normal file
File diff suppressed because it is too large
Load Diff
407
dokanLib/public.h
Normal file
407
dokanLib/public.h
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
Dokan : user-mode file system library for Windows
|
||||
|
||||
Copyright (C) 2015 - 2018 Adrien J. <liryna.stark@gmail.com> and Maxime C. <maxime@islog.com>
|
||||
Copyright (C) 2007 - 2011 Hiroki Asakawa <info@dokan-dev.net>
|
||||
|
||||
http://dokan-dev.github.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 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.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PUBLIC_H_
|
||||
#define PUBLIC_H_
|
||||
|
||||
#ifndef DOKAN_MAJOR_API_VERSION
|
||||
#define DOKAN_MAJOR_API_VERSION L"1"
|
||||
#include <minwindef.h>
|
||||
//#include "C:/Program Files (x86)/Windows Kits/8.1/Include/shared/minwindef.h"
|
||||
#endif
|
||||
|
||||
#define DOKAN_DRIVER_VERSION 0x0000190
|
||||
|
||||
#define EVENT_CONTEXT_MAX_SIZE (1024 * 32)
|
||||
|
||||
#define IOCTL_TEST \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_SET_DEBUG_MODE \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_EVENT_WAIT \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_EVENT_INFO \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_EVENT_RELEASE \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_EVENT_START \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_EVENT_WRITE \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_KEEPALIVE \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_SERVICE_WAIT \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_RESET_TIMEOUT \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80B, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_GET_ACCESS_TOKEN \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80C, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_EVENT_MOUNTPOINT_LIST \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80D, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_MOUNTPOINT_CLEANUP \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80E, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define DRIVER_FUNC_INSTALL 0x01
|
||||
#define DRIVER_FUNC_REMOVE 0x02
|
||||
|
||||
#define DOKAN_MOUNTED 1
|
||||
#define DOKAN_USED 2
|
||||
#define DOKAN_START_FAILED 3
|
||||
|
||||
#define DOKAN_DEVICE_MAX 10
|
||||
|
||||
#define DOKAN_DEFAULT_SECTOR_SIZE 512
|
||||
#define DOKAN_DEFAULT_ALLOCATION_UNIT_SIZE 512
|
||||
#define DOKAN_DEFAULT_DISK_SIZE 1024 * 1024 * 1024
|
||||
|
||||
// used in CCB->Flags and FCB->Flags
|
||||
#define DOKAN_FILE_DIRECTORY 1
|
||||
#define DOKAN_FILE_DELETED 2
|
||||
#define DOKAN_FILE_OPENED 4
|
||||
#define DOKAN_DIR_MATCH_ALL 8
|
||||
#define DOKAN_DELETE_ON_CLOSE 16
|
||||
#define DOKAN_PAGING_IO 32
|
||||
#define DOKAN_SYNCHRONOUS_IO 64
|
||||
#define DOKAN_WRITE_TO_END_OF_FILE 128
|
||||
#define DOKAN_NOCACHE 256
|
||||
#define DOKAN_FILE_CHANGE_LAST_WRITE 512
|
||||
|
||||
// used in DOKAN_START->DeviceType
|
||||
#define DOKAN_DISK_FILE_SYSTEM 0
|
||||
#define DOKAN_NETWORK_FILE_SYSTEM 1
|
||||
|
||||
/*
|
||||
* This structure is used for copying UNICODE_STRING from the kernel mode driver
|
||||
* into the user mode driver.
|
||||
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff564879(v=vs.85).aspx
|
||||
*/
|
||||
typedef struct _DOKAN_UNICODE_STRING_INTERMEDIATE {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
WCHAR Buffer[1];
|
||||
} DOKAN_UNICODE_STRING_INTERMEDIATE, *PDOKAN_UNICODE_STRING_INTERMEDIATE;
|
||||
|
||||
/*
|
||||
* This structure is used for copying ACCESS_STATE from the kernel mode driver
|
||||
* into the user mode driver.
|
||||
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff538840(v=vs.85).aspx
|
||||
*/
|
||||
typedef struct _DOKAN_ACCESS_STATE_INTERMEDIATE {
|
||||
BOOLEAN SecurityEvaluated;
|
||||
BOOLEAN GenerateAudit;
|
||||
BOOLEAN GenerateOnClose;
|
||||
BOOLEAN AuditPrivileges;
|
||||
ULONG Flags;
|
||||
ACCESS_MASK RemainingDesiredAccess;
|
||||
ACCESS_MASK PreviouslyGrantedAccess;
|
||||
ACCESS_MASK OriginalDesiredAccess;
|
||||
|
||||
// Offset from the beginning of this structure to a SECURITY_DESCRIPTOR
|
||||
// if 0 that means there is no security descriptor
|
||||
ULONG SecurityDescriptorOffset;
|
||||
|
||||
// Offset from the beginning of this structure to a
|
||||
// DOKAN_UNICODE_STRING_INTERMEDIATE
|
||||
ULONG UnicodeStringObjectNameOffset;
|
||||
|
||||
// Offset from the beginning of this structure to a
|
||||
// DOKAN_UNICODE_STRING_INTERMEDIATE
|
||||
ULONG UnicodeStringObjectTypeOffset;
|
||||
} DOKAN_ACCESS_STATE_INTERMEDIATE, *PDOKAN_ACCESS_STATE_INTERMEDIATE;
|
||||
|
||||
typedef struct _DOKAN_ACCESS_STATE {
|
||||
BOOLEAN SecurityEvaluated;
|
||||
BOOLEAN GenerateAudit;
|
||||
BOOLEAN GenerateOnClose;
|
||||
BOOLEAN AuditPrivileges;
|
||||
ULONG Flags;
|
||||
ACCESS_MASK RemainingDesiredAccess;
|
||||
ACCESS_MASK PreviouslyGrantedAccess;
|
||||
ACCESS_MASK OriginalDesiredAccess;
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
||||
UNICODE_STRING ObjectName;
|
||||
UNICODE_STRING ObjectType;
|
||||
} DOKAN_ACCESS_STATE, *PDOKAN_ACCESS_STATE;
|
||||
|
||||
/*
|
||||
* This structure is used for copying IO_SECURITY_CONTEXT from the kernel mode
|
||||
* driver into the user mode driver.
|
||||
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx
|
||||
*/
|
||||
typedef struct _DOKAN_IO_SECURITY_CONTEXT_INTERMEDIATE {
|
||||
DOKAN_ACCESS_STATE_INTERMEDIATE AccessState;
|
||||
ACCESS_MASK DesiredAccess;
|
||||
} DOKAN_IO_SECURITY_CONTEXT_INTERMEDIATE,
|
||||
*PDOKAN_IO_SECURITY_CONTEXT_INTERMEDIATE;
|
||||
|
||||
typedef struct _DOKAN_IO_SECURITY_CONTEXT {
|
||||
DOKAN_ACCESS_STATE AccessState;
|
||||
ACCESS_MASK DesiredAccess;
|
||||
} DOKAN_IO_SECURITY_CONTEXT, *PDOKAN_IO_SECURITY_CONTEXT;
|
||||
|
||||
typedef struct _CREATE_CONTEXT {
|
||||
DOKAN_IO_SECURITY_CONTEXT_INTERMEDIATE SecurityContext;
|
||||
ULONG FileAttributes;
|
||||
ULONG CreateOptions;
|
||||
ULONG ShareAccess;
|
||||
ULONG FileNameLength;
|
||||
|
||||
// Offset from the beginning of this structure to the string
|
||||
ULONG FileNameOffset;
|
||||
} CREATE_CONTEXT, *PCREATE_CONTEXT;
|
||||
|
||||
typedef struct _CLEANUP_CONTEXT {
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
|
||||
} CLEANUP_CONTEXT, *PCLEANUP_CONTEXT;
|
||||
|
||||
typedef struct _CLOSE_CONTEXT {
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
|
||||
} CLOSE_CONTEXT, *PCLOSE_CONTEXT;
|
||||
|
||||
typedef struct _DIRECTORY_CONTEXT {
|
||||
ULONG FileInformationClass;
|
||||
ULONG FileIndex;
|
||||
ULONG BufferLength;
|
||||
ULONG DirectoryNameLength;
|
||||
ULONG SearchPatternLength;
|
||||
ULONG SearchPatternOffset;
|
||||
WCHAR DirectoryName[1];
|
||||
WCHAR SearchPatternBase[1];
|
||||
|
||||
} DIRECTORY_CONTEXT, *PDIRECTORY_CONTEXT;
|
||||
|
||||
typedef struct _READ_CONTEXT {
|
||||
LARGE_INTEGER ByteOffset;
|
||||
ULONG BufferLength;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} READ_CONTEXT, *PREAD_CONTEXT;
|
||||
|
||||
typedef struct _WRITE_CONTEXT {
|
||||
LARGE_INTEGER ByteOffset;
|
||||
ULONG BufferLength;
|
||||
ULONG BufferOffset;
|
||||
ULONG RequestLength;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[2];
|
||||
// "2" means to keep last null of contents to write
|
||||
} WRITE_CONTEXT, *PWRITE_CONTEXT;
|
||||
|
||||
typedef struct _FILEINFO_CONTEXT {
|
||||
ULONG FileInformationClass;
|
||||
ULONG BufferLength;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILEINFO_CONTEXT, *PFILEINFO_CONTEXT;
|
||||
|
||||
typedef struct _SETFILE_CONTEXT {
|
||||
ULONG FileInformationClass;
|
||||
ULONG BufferLength;
|
||||
ULONG BufferOffset;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} SETFILE_CONTEXT, *PSETFILE_CONTEXT;
|
||||
|
||||
typedef struct _VOLUME_CONTEXT {
|
||||
ULONG FsInformationClass;
|
||||
ULONG BufferLength;
|
||||
} VOLUME_CONTEXT, *PVOLUME_CONTEXT;
|
||||
|
||||
typedef struct _LOCK_CONTEXT {
|
||||
LARGE_INTEGER ByteOffset;
|
||||
LARGE_INTEGER Length;
|
||||
ULONG Key;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} LOCK_CONTEXT, *PLOCK_CONTEXT;
|
||||
|
||||
typedef struct _FLUSH_CONTEXT {
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FLUSH_CONTEXT, *PFLUSH_CONTEXT;
|
||||
|
||||
typedef struct _UNMOUNT_CONTEXT {
|
||||
WCHAR DeviceName[64];
|
||||
ULONG Option;
|
||||
} UNMOUNT_CONTEXT, *PUNMOUNT_CONTEXT;
|
||||
|
||||
typedef struct _SECURITY_CONTEXT {
|
||||
SECURITY_INFORMATION SecurityInformation;
|
||||
ULONG BufferLength;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} SECURITY_CONTEXT, *PSECURITY_CONTEXT;
|
||||
|
||||
typedef struct _SET_SECURITY_CONTEXT {
|
||||
SECURITY_INFORMATION SecurityInformation;
|
||||
ULONG BufferLength;
|
||||
ULONG BufferOffset;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} SET_SECURITY_CONTEXT, *PSET_SECURITY_CONTEXT;
|
||||
|
||||
typedef struct _EVENT_CONTEXT {
|
||||
ULONG Length;
|
||||
ULONG MountId;
|
||||
ULONG SerialNumber;
|
||||
ULONG ProcessId;
|
||||
UCHAR MajorFunction;
|
||||
UCHAR MinorFunction;
|
||||
ULONG Flags;
|
||||
ULONG FileFlags;
|
||||
ULONG64 Context;
|
||||
union {
|
||||
DIRECTORY_CONTEXT Directory;
|
||||
READ_CONTEXT Read;
|
||||
WRITE_CONTEXT Write;
|
||||
FILEINFO_CONTEXT File;
|
||||
CREATE_CONTEXT Create;
|
||||
CLOSE_CONTEXT Close;
|
||||
SETFILE_CONTEXT SetFile;
|
||||
CLEANUP_CONTEXT Cleanup;
|
||||
LOCK_CONTEXT Lock;
|
||||
VOLUME_CONTEXT Volume;
|
||||
FLUSH_CONTEXT Flush;
|
||||
UNMOUNT_CONTEXT Unmount;
|
||||
SECURITY_CONTEXT Security;
|
||||
SET_SECURITY_CONTEXT SetSecurity;
|
||||
} Operation;
|
||||
} EVENT_CONTEXT, *PEVENT_CONTEXT;
|
||||
|
||||
#define WRITE_MAX_SIZE \
|
||||
(EVENT_CONTEXT_MAX_SIZE - sizeof(EVENT_CONTEXT) - 256 * sizeof(WCHAR))
|
||||
|
||||
typedef struct _EVENT_INFORMATION {
|
||||
ULONG SerialNumber;
|
||||
NTSTATUS Status;
|
||||
ULONG Flags;
|
||||
union {
|
||||
struct {
|
||||
ULONG Index;
|
||||
} Directory;
|
||||
struct {
|
||||
ULONG Flags;
|
||||
ULONG Information;
|
||||
} Create;
|
||||
struct {
|
||||
LARGE_INTEGER CurrentByteOffset;
|
||||
} Read;
|
||||
struct {
|
||||
LARGE_INTEGER CurrentByteOffset;
|
||||
} Write;
|
||||
struct {
|
||||
UCHAR DeleteOnClose;
|
||||
} Delete;
|
||||
struct {
|
||||
ULONG Timeout;
|
||||
} ResetTimeout;
|
||||
struct {
|
||||
HANDLE Handle;
|
||||
} AccessToken;
|
||||
} Operation;
|
||||
ULONG64 Context;
|
||||
ULONG BufferLength;
|
||||
UCHAR Buffer[8];
|
||||
|
||||
} EVENT_INFORMATION, *PEVENT_INFORMATION;
|
||||
|
||||
#define DOKAN_EVENT_ALTERNATIVE_STREAM_ON 1
|
||||
#define DOKAN_EVENT_WRITE_PROTECT 2
|
||||
#define DOKAN_EVENT_REMOVABLE 4
|
||||
#define DOKAN_EVENT_MOUNT_MANAGER 8
|
||||
#define DOKAN_EVENT_CURRENT_SESSION 16
|
||||
#define DOKAN_EVENT_FILELOCK_USER_MODE 32
|
||||
|
||||
typedef struct _EVENT_DRIVER_INFO {
|
||||
ULONG DriverVersion;
|
||||
ULONG Status;
|
||||
ULONG DeviceNumber;
|
||||
ULONG MountId;
|
||||
WCHAR DeviceName[64];
|
||||
} EVENT_DRIVER_INFO, *PEVENT_DRIVER_INFO;
|
||||
|
||||
typedef struct _EVENT_START {
|
||||
ULONG UserVersion;
|
||||
ULONG DeviceType;
|
||||
ULONG Flags;
|
||||
WCHAR MountPoint[260];
|
||||
WCHAR UNCName[64];
|
||||
ULONG IrpTimeout;
|
||||
} EVENT_START, *PEVENT_START;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201)
|
||||
typedef struct _DOKAN_RENAME_INFORMATION {
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS1)
|
||||
union {
|
||||
BOOLEAN ReplaceIfExists; // FileRenameInformation
|
||||
ULONG Flags; // FileRenameInformationEx
|
||||
} DUMMYUNIONNAME;
|
||||
#else
|
||||
BOOLEAN ReplaceIfExists;
|
||||
#endif
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} DOKAN_RENAME_INFORMATION, *PDOKAN_RENAME_INFORMATION;
|
||||
#pragma warning(pop)
|
||||
|
||||
typedef struct _DOKAN_LINK_INFORMATION {
|
||||
BOOLEAN ReplaceIfExists;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} DOKAN_LINK_INFORMATION, *PDOKAN_LINK_INFORMATION;
|
||||
|
||||
/**
|
||||
* \struct DOKAN_CONTROL
|
||||
* \brief Dokan Control
|
||||
*/
|
||||
typedef struct _DOKAN_CONTROL {
|
||||
/** File System Type */
|
||||
ULONG Type;
|
||||
/** Mount point. Can be "M:\" (drive letter) or "C:\mount\dokan" (path in NTFS) */
|
||||
WCHAR MountPoint[MAX_PATH];
|
||||
/** UNC name used for network volume */
|
||||
WCHAR UNCName[64];
|
||||
/** Disk Device Name */
|
||||
WCHAR DeviceName[64];
|
||||
/** Volume Device Object */
|
||||
PVOID64 DeviceObject;
|
||||
/** Session ID of calling process */
|
||||
ULONG SessionId;
|
||||
} DOKAN_CONTROL, *PDOKAN_CONTROL;
|
||||
|
||||
#endif // PUBLIC_H_
|
||||
1
resources/state-sync.svg
Normal file
1
resources/state-sync.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.87498 0 0 .87498 .26458 -255.9)"><circle cx="2.1167" cy="294.88" r="2.1167" fill="#2268ab" stroke-width=".25066"/><g fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="m3.0714 295.43a1.1024 1.1024 0 0 1-0.95473 0.55121 1.1024 1.1024 0 0 1-0.95473-0.55121" stroke="#fff" stroke-width=".44097"/><path transform="scale(-1)" d="m-1.1619-294.33a1.1024 1.1024 0 0 1-0.95473 0.55122 1.1024 1.1024 0 0 1-0.95473-0.55122" stroke="#fff" stroke-width=".44097"/><path d="m1.4349 295.15-0.52538-4e-5 1.138e-4 0.52563" stroke="#faffff" stroke-width=".52916"/><path d="m2.815 294.62 0.52538 4e-5 -1.138e-4 -0.52563" stroke="#faffff" stroke-width=".52916"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 800 B |
@@ -22,8 +22,11 @@
|
||||
SyncClientProxy *_syncClientProxy;
|
||||
NSMutableSet *_registeredDirectories;
|
||||
NSString *_shareMenuTitle;
|
||||
NSString *_mirror_path;
|
||||
NSString *_fs_path;
|
||||
NSMutableDictionary *_strings;
|
||||
NSMutableArray *_menuItems;
|
||||
Boolean shouldBeChecked;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -76,6 +76,10 @@
|
||||
}
|
||||
|
||||
NSString* normalizedPath = [[url path] decomposedStringWithCanonicalMapping];
|
||||
if([normalizedPath containsString: _fs_path]) {
|
||||
[_syncClientProxy askForIcon:normalizedPath isDirectory:isDir];
|
||||
normalizedPath = [normalizedPath stringByReplacingOccurrencesOfString: _fs_path withString: _mirror_path];
|
||||
}
|
||||
[_syncClientProxy askForIcon:normalizedPath isDirectory:isDir];
|
||||
}
|
||||
|
||||
@@ -117,9 +121,16 @@
|
||||
|
||||
NSString *paths = [self selectedPathsSeparatedByRecordSeparator];
|
||||
// calling this IPC calls us back from client with several MENU_ITEM entries and then our askOnSocket returns again
|
||||
if([paths containsString:_fs_path]) {
|
||||
paths = [paths stringByReplacingOccurrencesOfString:_fs_path withString:_mirror_path];
|
||||
}
|
||||
|
||||
[_syncClientProxy askOnSocket:paths query:@"GET_MENU_ITEMS"];
|
||||
|
||||
id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"];
|
||||
|
||||
[_syncClientProxy askOnSocket:paths query:@"GET_DOWNLOAD_MODE"];
|
||||
|
||||
if (contextMenuTitle && !onlyRootsSelected) {
|
||||
NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
|
||||
NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
@@ -134,12 +145,22 @@
|
||||
NSMenuItem *actionItem = [subMenu addItemWithTitle:[item valueForKey:@"text"]
|
||||
action:@selector(subMenuActionClicked:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"];
|
||||
if(shouldBeChecked && [command isEqualToString:@"OFFLINE_DOWNLOAD_MODE"]) {
|
||||
actionItem.state = NSOnState;
|
||||
} else if(!shouldBeChecked && [command isEqualToString:@"ONLINE_DOWNLOAD_MODE"]) {
|
||||
actionItem.state = NSOnState;
|
||||
} else {
|
||||
actionItem.state = NSOffState;
|
||||
}
|
||||
[actionItem setTag:idx];
|
||||
[actionItem setTarget:self];
|
||||
NSString *flags = [item valueForKey:@"flags"]; // e.g. "d"
|
||||
if ([flags rangeOfString:@"d"].location != NSNotFound) {
|
||||
[actionItem setEnabled:false];
|
||||
}
|
||||
//if ([flags rangeOfString:@"d"].location != NSNotFound) {
|
||||
// [actionItem setEnabled:false];
|
||||
//}
|
||||
|
||||
idx++;
|
||||
}
|
||||
return menu;
|
||||
@@ -147,10 +168,14 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)subMenuActionClicked:(id)sender {
|
||||
- (void)subMenuActionClicked:(id)sender
|
||||
{
|
||||
long idx = [(NSMenuItem*)sender tag];
|
||||
NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"];
|
||||
NSString *paths = [self selectedPathsSeparatedByRecordSeparator];
|
||||
if([paths containsString: _fs_path]) {
|
||||
paths = [paths stringByReplacingOccurrencesOfString: _fs_path withString: _mirror_path];
|
||||
}
|
||||
[_syncClientProxy askOnSocket:paths query:command];
|
||||
}
|
||||
|
||||
@@ -159,9 +184,23 @@
|
||||
- (void)setResultForPath:(NSString*)path result:(NSString*)result
|
||||
{
|
||||
NSString *normalizedPath = [path decomposedStringWithCanonicalMapping];
|
||||
if([normalizedPath containsString: _mirror_path]) {
|
||||
[[FIFinderSyncController defaultController] setBadgeIdentifier:result forURL:[NSURL fileURLWithPath:normalizedPath]];
|
||||
normalizedPath = [normalizedPath stringByReplacingOccurrencesOfString: _mirror_path withString: _fs_path];
|
||||
}
|
||||
|
||||
[[FIFinderSyncController defaultController] setBadgeIdentifier:result forURL:[NSURL fileURLWithPath:normalizedPath]];
|
||||
}
|
||||
|
||||
- (void)setDownloadMode:(NSString *)mode
|
||||
{
|
||||
if( [mode isEqualToString:@"OFFLINE"] ) {
|
||||
shouldBeChecked = true;
|
||||
} else {
|
||||
shouldBeChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reFetchFileNameCacheForPath:(NSString*)path
|
||||
{
|
||||
}
|
||||
@@ -170,12 +209,21 @@
|
||||
{
|
||||
assert(_registeredDirectories);
|
||||
[_registeredDirectories addObject:[NSURL fileURLWithPath:path]];
|
||||
_mirror_path = path;
|
||||
}
|
||||
|
||||
- (void)registerFs:(NSString*)path
|
||||
{
|
||||
assert(_registeredDirectories);
|
||||
[_registeredDirectories addObject:[NSURL fileURLWithPath:path]];
|
||||
_fs_path = path;
|
||||
[FIFinderSyncController defaultController].directoryURLs = _registeredDirectories;
|
||||
}
|
||||
|
||||
- (void)unregisterPath:(NSString*)path
|
||||
{
|
||||
[_registeredDirectories removeObject:[NSURL fileURLWithPath:path]];
|
||||
[_registeredDirectories removeObject:[NSURL fileURLWithPath:_fs_path]];
|
||||
[FIFinderSyncController defaultController].directoryURLs = _registeredDirectories;
|
||||
}
|
||||
|
||||
@@ -188,7 +236,9 @@
|
||||
{
|
||||
_menuItems = [[NSMutableArray alloc] init];
|
||||
}
|
||||
- (void)addMenuItem:(NSDictionary *)item {
|
||||
|
||||
- (void)addMenuItem:(NSDictionary *)item
|
||||
{
|
||||
[_menuItems addObject:item];
|
||||
}
|
||||
|
||||
@@ -205,5 +255,4 @@
|
||||
[FIFinderSyncController defaultController].directoryURLs = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@end
|
||||
@@ -17,8 +17,10 @@
|
||||
|
||||
@protocol SyncClientProxyDelegate <NSObject>
|
||||
- (void)setResultForPath:(NSString*)path result:(NSString*)result;
|
||||
- (void)setDownloadMode:(NSString*)mode;
|
||||
- (void)reFetchFileNameCacheForPath:(NSString*)path;
|
||||
- (void)registerPath:(NSString*)path;
|
||||
- (void)registerFs:(NSString*)path;
|
||||
- (void)unregisterPath:(NSString*)path;
|
||||
- (void)setString:(NSString*)key value:(NSString*)value;
|
||||
- (void)resetMenuItems;
|
||||
|
||||
@@ -113,9 +113,15 @@
|
||||
} else if( [[chunks objectAtIndex:0] isEqualToString:@"UPDATE_VIEW"] ) {
|
||||
NSString *path = [chunks objectAtIndex:1];
|
||||
[_delegate reFetchFileNameCacheForPath:path];
|
||||
} else if( [[chunks objectAtIndex:0] isEqualToString:@"GET_DOWNLOAD_MODE"] ) {
|
||||
NSString *mode = [chunks objectAtIndex:1];
|
||||
[_delegate setDownloadMode:mode];
|
||||
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_PATH"] ) {
|
||||
NSString *path = [chunks objectAtIndex:1];
|
||||
[_delegate registerPath:path];
|
||||
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_DRIVEFS"] ) {
|
||||
NSString *path = [chunks objectAtIndex:1];
|
||||
[_delegate registerFs:path];
|
||||
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"UNREGISTER_PATH"] ) {
|
||||
NSString *path = [chunks objectAtIndex:1];
|
||||
[_delegate unregisterPath:path];
|
||||
|
||||
@@ -63,7 +63,7 @@ class SocketConnect(GObject.GObject):
|
||||
self._watch_id = 0
|
||||
self._sock = None
|
||||
self._listeners = [self._update_registered_paths, self._get_version]
|
||||
self._remainder = ''.encode()
|
||||
self._remainder = ''.encode() if python3 else ''
|
||||
self.protocolVersion = '1.0'
|
||||
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
|
||||
# all over the other objects.
|
||||
@@ -82,7 +82,7 @@ class SocketConnect(GObject.GObject):
|
||||
# print("Server command: " + cmd)
|
||||
if self.connected:
|
||||
try:
|
||||
self._sock.send(cmd.encode())
|
||||
self._sock.send(cmd.encode() if python3 else cmd)
|
||||
except:
|
||||
print("Sending failed.")
|
||||
self.reconnect()
|
||||
@@ -134,7 +134,8 @@ class SocketConnect(GObject.GObject):
|
||||
return []
|
||||
data = self._remainder[:end]
|
||||
self._remainder = self._remainder[end+1:]
|
||||
return data.decode().split('\n')
|
||||
data = data.decode() if python3 else data
|
||||
return data.split('\n')
|
||||
|
||||
# Notify is the raw answer from the socket
|
||||
def _handle_notify(self, source, condition):
|
||||
|
||||
@@ -34,7 +34,7 @@ using namespace std;
|
||||
#define PIPE_TIMEOUT 5*1000 //ms
|
||||
#define SOCK_BUFFER 4096
|
||||
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo(const std::wstring &files)
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
@@ -45,32 +45,36 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo(const std::wstri
|
||||
if (!socket.Connect(pipename)) {
|
||||
return {};
|
||||
}
|
||||
socket.SendMsg(L"GET_STRINGS:CONTEXT_MENU_TITLE\n");
|
||||
socket.SendMsg((L"GET_MENU_ITEMS:" + files + L"\n").data());
|
||||
socket.SendMsg(L"SHARE_MENU_TITLE\n");
|
||||
|
||||
ContextMenuInfo info;
|
||||
std::wstring response;
|
||||
int sleptCount = 0;
|
||||
while (sleptCount < 5) {
|
||||
if (socket.ReadLine(&response)) {
|
||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_DRIVEFS:"))) {
|
||||
wstring responsePath = response.substr(17); // length of REGISTER_DRIVEFS:
|
||||
info._defaultFileStreamLetterDrive = responsePath;
|
||||
//setLetterDrive(responsePath);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||
wstring responsePath = response.substr(14); // length of REGISTER_PATH
|
||||
info.watchedDirectories.push_back(responsePath);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"STRING:"))) {
|
||||
wstring stringName, stringValue;
|
||||
if (!StringUtil::extractChunks(response, stringName, stringValue))
|
||||
continue;
|
||||
if (stringName == L"CONTEXT_MENU_TITLE")
|
||||
info.contextMenuTitle = move(stringValue);
|
||||
} else if (StringUtil::begins_with(response, wstring(L"MENU_ITEM:"))) {
|
||||
wstring commandName, flags, title;
|
||||
if (!StringUtil::extractChunks(response, commandName, flags, title))
|
||||
continue;
|
||||
info.menuItems.push_back({ commandName, flags, title });
|
||||
} else if (StringUtil::begins_with(response, wstring(L"GET_MENU_ITEMS:END"))) {
|
||||
break; // Stop once we completely received the last sent request
|
||||
else if (StringUtil::begins_with(response, wstring(L"SHARE_MENU_TITLE:"))) {
|
||||
info.shareMenuTitle = response.substr(17); // length of SHARE_MENU_TITLE:
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"STREAM_SUBMENU_TITLE:"))) {
|
||||
info.streamSubMenuTitle = response.substr(21);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"STREAM_OFFLINE_ITEM_TITLE:"))) {
|
||||
info.streamOfflineItemTitle = response.substr(26);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"STREAM_ONLINE_ITEM_TITLE:"))) {
|
||||
info.streamOnlineItemTitle = response.substr(25);
|
||||
break; // Stop once we received the last sent request
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
Sleep(50);
|
||||
@@ -80,7 +84,7 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo(const std::wstri
|
||||
return info;
|
||||
}
|
||||
|
||||
void OCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &path)
|
||||
void OCClientInterface::ShareObject(const std::wstring &path)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
@@ -93,8 +97,73 @@ void OCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &pat
|
||||
}
|
||||
|
||||
wchar_t msg[SOCK_BUFFER] = { 0 };
|
||||
if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"%s:%s\n", verb, path.c_str())))
|
||||
if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"SHARE:%s\n", path.c_str())))
|
||||
{
|
||||
socket.SendMsg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void OCClientInterface::SetDownloadMode(const std::wstring &path, bool online)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
CommunicationSocket socket;
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
|
||||
return;
|
||||
}
|
||||
if (!socket.Connect(pipename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t msg[SOCK_BUFFER] = { 0 };
|
||||
if (SUCCEEDED(StringCchPrintf(msg,
|
||||
SOCK_BUFFER,
|
||||
L"SET_DOWNLOAD_MODE:%s|%d\n",
|
||||
path.c_str(),
|
||||
online)
|
||||
)
|
||||
)
|
||||
{
|
||||
socket.SendMsg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring OCClientInterface::GetDownloadMode(const std::wstring &path)
|
||||
{
|
||||
CommunicationSocket socket;
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT))
|
||||
return L"";
|
||||
if (!socket.Connect(pipename))
|
||||
return L"";
|
||||
|
||||
wchar_t msg[SOCK_BUFFER] = { 0 };
|
||||
if (!SUCCEEDED(
|
||||
StringCchPrintf(msg,
|
||||
SOCK_BUFFER,
|
||||
L"GET_DOWNLOAD_MODE:%s\n",
|
||||
path.c_str())
|
||||
)
|
||||
)
|
||||
return L"";
|
||||
|
||||
socket.SendMsg(msg);
|
||||
std::wstring response;
|
||||
|
||||
int sleptCount = 0;
|
||||
while (sleptCount < 5)
|
||||
if (socket.ReadLine(&response))
|
||||
{
|
||||
if (StringUtil::begins_with(response, wstring(L"GET_DOWNLOAD_MODE:"))) {
|
||||
wstring downloadMode = response.substr(18);
|
||||
return downloadMode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Sleep(50);
|
||||
++sleptCount;
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
|
||||
@@ -45,15 +45,18 @@ class OCClientInterface
|
||||
public:
|
||||
struct ContextMenuInfo {
|
||||
std::vector<std::wstring> watchedDirectories;
|
||||
std::wstring contextMenuTitle;
|
||||
struct MenuItem
|
||||
{
|
||||
std::wstring command, flags, title;
|
||||
};
|
||||
std::vector<MenuItem> menuItems;
|
||||
std::wstring shareMenuTitle;
|
||||
std::wstring streamSubMenuTitle;
|
||||
std::wstring streamOfflineItemTitle;
|
||||
std::wstring streamOnlineItemTitle;
|
||||
std::wstring _defaultFileStreamLetterDrive;
|
||||
|
||||
};
|
||||
static ContextMenuInfo FetchInfo(const std::wstring &files);
|
||||
static void SendRequest(const wchar_t *verb, const std::wstring &path);
|
||||
static ContextMenuInfo FetchInfo();
|
||||
static void ShareObject(const std::wstring &path);
|
||||
static void SetDownloadMode(const std::wstring &path, bool online);
|
||||
static std::wstring GetDownloadMode(const std::wstring &path);
|
||||
|
||||
};
|
||||
|
||||
#endif //ABSTRACTSOCKETHANDLER_H
|
||||
|
||||
@@ -21,11 +21,31 @@
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <StringUtil.h>
|
||||
#include <assert.h>
|
||||
|
||||
extern HINSTANCE g_hInst;
|
||||
extern long g_cDllRef;
|
||||
|
||||
#define IDM_FIRST 0
|
||||
#define IDM_SHARE 0
|
||||
#define IDM_DRIVEMENU 1
|
||||
#define IDM_DRIVEMENU_OFFLINE 2
|
||||
#define IDM_DRIVEMENU_ONLINE 3
|
||||
#define IDM_LAST 4
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
OCContextMenu::OCContextMenu(void)
|
||||
: m_cRef(1)
|
||||
, m_pszMenuText(L"&Share")
|
||||
, m_pszVerb("ocshare")
|
||||
, m_pwszVerb(L"ocshare")
|
||||
, m_pszVerbCanonicalName("OCShareViaOC")
|
||||
, m_pwszVerbCanonicalName(L"OCShareViaOC")
|
||||
, m_pszVerbHelpText("Share via ownCloud")
|
||||
, m_pwszVerbHelpText(L"Share via ownCloud")
|
||||
{
|
||||
InterlockedIncrement(&g_cDllRef);
|
||||
}
|
||||
@@ -35,6 +55,14 @@ OCContextMenu::~OCContextMenu(void)
|
||||
InterlockedDecrement(&g_cDllRef);
|
||||
}
|
||||
|
||||
|
||||
void OCContextMenu::OnVerbDisplayFileName(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::ContextMenuInfo info = OCClientInterface::FetchInfo();
|
||||
OCClientInterface::ShareObject(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
|
||||
#pragma region IUnknown
|
||||
|
||||
// Query to the interface the component supported.
|
||||
@@ -75,12 +103,12 @@ IFACEMETHODIMP_(ULONG) OCContextMenu::Release()
|
||||
IFACEMETHODIMP OCContextMenu::Initialize(
|
||||
LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID)
|
||||
{
|
||||
m_selectedFiles.clear();
|
||||
|
||||
if (!pDataObj) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM stm;
|
||||
|
||||
@@ -88,19 +116,14 @@ IFACEMETHODIMP OCContextMenu::Initialize(
|
||||
// Get an HDROP handle.
|
||||
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
|
||||
if (hDrop) {
|
||||
// Ignore multi-selections
|
||||
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
|
||||
for (int i = 0; i < nFiles; ++i) {
|
||||
if (nFiles == 1) {
|
||||
// Get the path of the file.
|
||||
wchar_t buffer[MAX_PATH];
|
||||
|
||||
if (!DragQueryFile(hDrop, i, buffer, ARRAYSIZE(buffer))) {
|
||||
m_selectedFiles.clear();
|
||||
break;
|
||||
if (0 != DragQueryFile(hDrop, 0, m_szSelectedFile, ARRAYSIZE(m_szSelectedFile)))
|
||||
{
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
if (i)
|
||||
m_selectedFiles += L'\x1e';
|
||||
m_selectedFiles += buffer;
|
||||
}
|
||||
|
||||
GlobalUnlock(stm.hGlobal);
|
||||
@@ -111,7 +134,7 @@ IFACEMETHODIMP OCContextMenu::Initialize(
|
||||
|
||||
// If any value other than S_OK is returned from the method, the context
|
||||
// menu item is not displayed.
|
||||
return m_selectedFiles.empty() ? E_FAIL : S_OK;
|
||||
return hr;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -130,86 +153,236 @@ void InsertSeperator(HMENU hMenu, UINT indexMenu)
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
||||
{
|
||||
//< Comment for file streaming test.
|
||||
/*
|
||||
// If uFlags include CMF_DEFAULTONLY then we should not do anything.
|
||||
if (CMF_DEFAULTONLY & uFlags)
|
||||
{
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
*/
|
||||
|
||||
m_info = OCClientInterface::FetchInfo(m_selectedFiles);
|
||||
if (m_info.menuItems.empty()) {
|
||||
OCClientInterface::ContextMenuInfo info = OCClientInterface::FetchInfo();
|
||||
|
||||
bool skip = true;
|
||||
size_t selectedFileLength = wcslen(m_szSelectedFile);
|
||||
for (const std::wstring path : info.watchedDirectories) {
|
||||
if (StringUtil::isDescendantOf(m_szSelectedFile, selectedFileLength, path)){
|
||||
skip = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
|
||||
InsertSeperator(hMenu, indexMenu++);
|
||||
InsertSeperator(hMenu, indexMenu);
|
||||
indexMenu++;
|
||||
|
||||
HMENU hSubmenu = CreateMenu();
|
||||
// Query the download mode
|
||||
std::wstring downloadMode = OCClientInterface::GetDownloadMode(m_szSelectedFile);
|
||||
bool checkOnlineItem = downloadMode == L"ONLINE";
|
||||
bool checkOfflineItem = downloadMode == L"OFFLINE";
|
||||
|
||||
// Insert the drive Online|Offline submenu
|
||||
{
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.hSubMenu = hSubmenu;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &m_info.contextMenuTitle[0];
|
||||
|
||||
if (!InsertMenuItem(hMenu, indexMenu++, TRUE, &mii))
|
||||
// Create the submenu
|
||||
HMENU hDriveSubMenu = CreateMenu();
|
||||
if (!hDriveSubMenu)
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
InsertSeperator(hMenu, indexMenu++);
|
||||
// Setup the "Online" item
|
||||
MENUITEMINFO menuInfoDriveOnline {0};
|
||||
menuInfoDriveOnline.cbSize = sizeof (MENUITEMINFO);
|
||||
menuInfoDriveOnline.fMask = MIIM_STRING;
|
||||
menuInfoDriveOnline.dwTypeData = &info.streamOnlineItemTitle[0];
|
||||
menuInfoDriveOnline.fMask |= MIIM_ID;
|
||||
menuInfoDriveOnline.wID = idCmdFirst + IDM_DRIVEMENU_ONLINE;
|
||||
menuInfoDriveOnline.fMask |= MIIM_STATE;
|
||||
menuInfoDriveOnline.fState = MFS_ENABLED;
|
||||
if (checkOnlineItem)
|
||||
menuInfoDriveOnline.fState |= MFS_CHECKED;
|
||||
// Insert it into the submenu
|
||||
if(!InsertMenuItem(hDriveSubMenu,
|
||||
0, // At position zero
|
||||
TRUE, // indicates the existing item by using its zero-based position. (For example, the first item in the menu has a position of 0.)
|
||||
&menuInfoDriveOnline
|
||||
))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
UINT indexSubMenu = 0;
|
||||
for (auto &item : m_info.menuItems) {
|
||||
bool disabled = item.flags.find(L'd') != std::string::npos;
|
||||
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
|
||||
mii.wID = idCmdFirst + indexSubMenu;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &item.title[0];
|
||||
mii.fState = disabled ? MFS_DISABLED : MFS_ENABLED;
|
||||
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu, true, &mii))
|
||||
// Setup the "Online" item
|
||||
MENUITEMINFO menuInfoDriveOffline {0};
|
||||
menuInfoDriveOffline.cbSize = sizeof (MENUITEMINFO);
|
||||
menuInfoDriveOffline.fMask = MIIM_STRING;
|
||||
menuInfoDriveOffline.dwTypeData = &info.streamOfflineItemTitle[0];
|
||||
menuInfoDriveOffline.fMask |= MIIM_ID;
|
||||
menuInfoDriveOffline.wID = idCmdFirst + IDM_DRIVEMENU_OFFLINE;
|
||||
menuInfoDriveOffline.fMask |= MIIM_STATE;
|
||||
menuInfoDriveOffline.fState = MFS_ENABLED;
|
||||
if (checkOfflineItem)
|
||||
menuInfoDriveOffline.fState |= MFS_CHECKED;
|
||||
// Insert it into the submenu
|
||||
if (!InsertMenuItem(hDriveSubMenu,
|
||||
1, // At position one
|
||||
TRUE, // indicates the existing item by using its zero-based position. (For example, the first item in the menu has a position of 0.)
|
||||
&menuInfoDriveOffline
|
||||
))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
indexSubMenu++;
|
||||
|
||||
|
||||
// Setup the "Share" item
|
||||
MENUITEMINFO menuInfoDriveShare{ 0 };
|
||||
menuInfoDriveShare.cbSize = sizeof(MENUITEMINFO);
|
||||
menuInfoDriveShare.fMask = MIIM_STRING;
|
||||
menuInfoDriveShare.dwTypeData = &info.shareMenuTitle[0];
|
||||
menuInfoDriveShare.fMask |= MIIM_ID;
|
||||
menuInfoDriveShare.wID = idCmdFirst + IDM_SHARE;
|
||||
menuInfoDriveShare.fMask |= MIIM_STATE;
|
||||
menuInfoDriveShare.fState = MFS_ENABLED;
|
||||
|
||||
//if (checkOfflineItem)
|
||||
//menuInfoDriveShare.fState |= MFS_CHECKED;
|
||||
|
||||
// Insert it into the submenu
|
||||
if (!InsertMenuItem(hDriveSubMenu,
|
||||
2, // At position one
|
||||
TRUE, // indicates the existing item by using its zero-based position. (For example, the first item in the menu has a position of 0.)
|
||||
&menuInfoDriveShare
|
||||
))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
|
||||
// Insert the submenu below the "share" item
|
||||
MENUITEMINFO hDriveSubMenuInfo;
|
||||
hDriveSubMenuInfo.cbSize = sizeof (MENUITEMINFO);
|
||||
hDriveSubMenuInfo.fMask = MIIM_SUBMENU | MIIM_STATE | MIIM_STRING;
|
||||
hDriveSubMenuInfo.fState = MFS_ENABLED;
|
||||
// TODO: obtener el texto del cliente/gui
|
||||
hDriveSubMenuInfo.dwTypeData = &info.streamSubMenuTitle[0];
|
||||
hDriveSubMenuInfo.hSubMenu = hDriveSubMenu;
|
||||
|
||||
// Insert the subitem into the
|
||||
if (!InsertMenuItem(hMenu,
|
||||
indexMenu++,
|
||||
TRUE,
|
||||
&hDriveSubMenuInfo
|
||||
))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
}
|
||||
|
||||
indexMenu++;
|
||||
InsertSeperator(hMenu, indexMenu);
|
||||
|
||||
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
|
||||
// Set the code value to the offset of the largest command identifier
|
||||
// that was assigned, plus one (1).
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(indexSubMenu));
|
||||
|
||||
//< Comment for file streaming test.
|
||||
//return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_SHARE + 1));
|
||||
|
||||
//< Append for file streaming test.
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_LAST));
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
||||
{
|
||||
std::wstring command;
|
||||
|
||||
// For the Unicode case, if the high-order word is not zero, the
|
||||
// command's verb string is in lpcmi->lpVerbW.
|
||||
if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
|
||||
{
|
||||
command = ((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW;
|
||||
} else {
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
|
||||
auto offset = LOWORD(pici->lpVerb);
|
||||
if (offset >= m_info.menuItems.size())
|
||||
// Is the verb supported by this context menu extension?
|
||||
if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, m_pwszVerb) == 0)
|
||||
{
|
||||
OnVerbDisplayFileName(pici->hwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
|
||||
command = m_info.menuItems[offset].command;
|
||||
}
|
||||
}
|
||||
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
else
|
||||
{
|
||||
// Is the command identifier offset supported by this context menu
|
||||
// extension?
|
||||
if (LOWORD(pici->lpVerb) == IDM_SHARE)
|
||||
{
|
||||
OnVerbDisplayFileName(pici->hwnd);
|
||||
}
|
||||
else if (LOWORD(pici->lpVerb) == IDM_DRIVEMENU_ONLINE)
|
||||
{
|
||||
OnDriveMenuOnline(pici->hwnd);
|
||||
}
|
||||
else if (LOWORD(pici->lpVerb) == IDM_DRIVEMENU_OFFLINE)
|
||||
{
|
||||
OnDriveMenuOffline(pici->hwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
OCClientInterface::SendRequest(command.data(), m_selectedFiles);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCommand,
|
||||
UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
|
||||
{
|
||||
if (idCommand < m_info.menuItems.size() && uFlags == GCS_VERBW) {
|
||||
return StringCchCopyW(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_info.menuItems[idCommand].command.data());
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
|
||||
if (idCommand == IDM_SHARE)
|
||||
{
|
||||
switch (uFlags)
|
||||
{
|
||||
case GCS_HELPTEXTW:
|
||||
// Only useful for pre-Vista versions of Windows that have a
|
||||
// Status bar.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_pwszVerbHelpText);
|
||||
break;
|
||||
|
||||
case GCS_VERBW:
|
||||
// GCS_VERBW is an optional feature that enables a caller to
|
||||
// discover the canonical name for the verb passed in through
|
||||
// idCommand.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_pwszVerbCanonicalName);
|
||||
break;
|
||||
|
||||
default:
|
||||
hr = S_OK;
|
||||
}
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
|
||||
// If the command (idCommand) is not supported by this context menu
|
||||
// extension handler, return E_INVALIDARG.
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void OCContextMenu::OnDriveMenuOffline(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::SetDownloadMode(std::wstring(m_szSelectedFile), false);
|
||||
}
|
||||
|
||||
void OCContextMenu::OnDriveMenuOnline(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::SetDownloadMode(std::wstring(m_szSelectedFile), true);
|
||||
}
|
||||
|
||||
|
||||
#pragma endregion
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
#pragma once
|
||||
#include <shlobj.h> // For IShellExtInit and IContextMenu
|
||||
#include <string>
|
||||
#include "OCClientInterface.h"
|
||||
|
||||
class OCContextMenu : public IShellExtInit, public IContextMenu
|
||||
{
|
||||
@@ -45,9 +43,27 @@ private:
|
||||
// Reference count of component.
|
||||
long m_cRef;
|
||||
|
||||
// The name of the selected files (separated by '\x1e')
|
||||
std::wstring m_selectedFiles;
|
||||
OCClientInterface::ContextMenuInfo m_info;
|
||||
// The name of the selected file.
|
||||
wchar_t m_szSelectedFile[MAX_PATH];
|
||||
|
||||
// The method that handles the "display" verb.
|
||||
void OnVerbDisplayFileName(HWND hWnd);
|
||||
|
||||
/// @brief changes the
|
||||
/// @param hWnd
|
||||
void OnDriveMenuOnline(HWND hWnd);
|
||||
|
||||
/// @brief
|
||||
/// @param hWnd
|
||||
void OnDriveMenuOffline(HWND hWnd);
|
||||
|
||||
PWSTR m_pszMenuText;
|
||||
PCSTR m_pszVerb;
|
||||
PCWSTR m_pwszVerb;
|
||||
PCSTR m_pszVerbCanonicalName;
|
||||
PCWSTR m_pwszVerbCanonicalName;
|
||||
PCSTR m_pszVerbHelpText;
|
||||
PCWSTR m_pwszVerbHelpText;
|
||||
};
|
||||
|
||||
#endif //OCCONTEXTMENU_H
|
||||
|
||||
@@ -72,13 +72,14 @@ void RemotePathChecker::workerThreadLoop()
|
||||
|
||||
std::wstring response;
|
||||
while (!_stop && socket.ReadLine(&response)) {
|
||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||
wstring responsePath = response.substr(14); // length of REGISTER_PATH:
|
||||
|
||||
auto sharedPtrCopy = atomic_load(&_watchedDirectories);
|
||||
auto vectorCopy = make_shared<vector<wstring>>(*sharedPtrCopy);
|
||||
vectorCopy->push_back(responsePath);
|
||||
atomic_store(&_watchedDirectories, shared_ptr<const vector<wstring>>(vectorCopy));
|
||||
|
||||
atomic_store(&_watchedDirectories, shared_ptr<const vector<wstring>>(vectorCopy));
|
||||
|
||||
// We don't keep track of all files and can't know which file is currently visible
|
||||
// to the user, but at least reload the root dir so that any shortcut to the root
|
||||
@@ -197,6 +198,8 @@ bool RemotePathChecker::IsMonitoredPath(const wchar_t* filePath, int* state)
|
||||
}
|
||||
|
||||
auto path = std::wstring(filePath);
|
||||
_pending.push(filePath);
|
||||
|
||||
|
||||
auto it = _cache.find(path);
|
||||
if (it != _cache.end()) {
|
||||
|
||||
@@ -40,8 +40,11 @@ public:
|
||||
~RemotePathChecker();
|
||||
std::shared_ptr<const std::vector<std::wstring>> WatchedDirectories() const;
|
||||
bool IsMonitoredPath(const wchar_t* filePath, int* state);
|
||||
void setLetterDrive(std::wstring str);
|
||||
std::wstring getLetterDrive();
|
||||
|
||||
private:
|
||||
std::wstring _defaultFileStreamLetterDrive;
|
||||
FileState _StrToFileState(const std::wstring &str);
|
||||
std::mutex _mutex;
|
||||
std::atomic<bool> _stop;
|
||||
|
||||
2
src/3rdparty/qtmacgoodies
vendored
2
src/3rdparty/qtmacgoodies
vendored
Submodule src/3rdparty/qtmacgoodies updated: 4ffbff5d5f...2965f75667
@@ -41,6 +41,7 @@
|
||||
"(" path " == " prefix " OR " IS_PREFIX_PATH_OF(prefix, path) ")"
|
||||
|
||||
namespace OCC {
|
||||
SyncJournalDb* SyncJournalDb::_instance = 0;
|
||||
|
||||
Q_LOGGING_CATEGORY(lcDb, "nextcloud.sync.database", QtInfoMsg)
|
||||
|
||||
@@ -100,6 +101,16 @@ SyncJournalDb::SyncJournalDb(const QString &dbFilePath, QObject *parent)
|
||||
if (_journalMode.isEmpty()) {
|
||||
_journalMode = defaultJournalMode(_dbFile);
|
||||
}
|
||||
|
||||
#ifndef Q_OS_LINUX
|
||||
ASSERT(!_instance);
|
||||
_instance = this;
|
||||
#endif
|
||||
}
|
||||
|
||||
SyncJournalDb *SyncJournalDb::instance()
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
QString SyncJournalDb::makeDbName(const QString &localPath,
|
||||
@@ -488,6 +499,18 @@ bool SyncJournalDb::checkConnect()
|
||||
return sqlFail("Create table version", createQuery);
|
||||
}
|
||||
|
||||
// table for streaming information
|
||||
SqlQuery createStreamingSql(_db);
|
||||
createStreamingSql.prepare(
|
||||
"CREATE TABLE IF NOT EXISTS syncmode ("
|
||||
"path TEXT PRIMARY KEY,"
|
||||
"mode TEXT DEFAULT ('O'),"
|
||||
"lastaccess TEXT DEFAULT (''),"
|
||||
"downloaded TEXT DEFAULT ('N'))"
|
||||
);
|
||||
if (!createStreamingSql.exec())
|
||||
return sqlFail("create streaming table", createQuery);
|
||||
|
||||
bool forceRemoteDiscovery = false;
|
||||
|
||||
SqlQuery versionQuery("SELECT major, minor, patch FROM version;", _db);
|
||||
@@ -578,6 +601,48 @@ bool SyncJournalDb::checkConnect()
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _getErrorBlacklistQuery);
|
||||
}
|
||||
|
||||
/* _setErrorBlacklistQuery.reset(new SqlQuery(_db));
|
||||
if (_setErrorBlacklistQuery->prepare("INSERT OR REPLACE INTO blacklist "
|
||||
"(path, lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory) "
|
||||
"VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)")) {
|
||||
return sqlFail("prepare _setErrorBlacklistQuery", *_setErrorBlacklistQuery);
|
||||
}*/
|
||||
|
||||
// Sync mode
|
||||
|
||||
if (!_getSyncModeDownloadQuery.initOrReset("SELECT downloaded FROM syncmode WHERE path=?1;", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _getSyncModeDownloadQuery);
|
||||
}
|
||||
|
||||
if (!_setSyncModeDownloadQuery.initOrReset("UPDATE syncmode SET downloaded=?2 WHERE path=?1;", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _setSyncModeDownloadQuery);
|
||||
}
|
||||
|
||||
if (!_getSyncModeQuery.initOrReset("SELECT mode FROM syncmode WHERE path=?1;", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _getSyncModeQuery);
|
||||
}
|
||||
|
||||
if (!_setSyncModeQuery.initOrReset("INSERT OR REPLACE INTO syncmode (path, mode) VALUES (?1, ?2);", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _setSyncModeQuery);
|
||||
}
|
||||
|
||||
if (!_deleteSyncModeQuery.initOrReset("DELETE FROM syncmode WHERE path=?1;", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _deleteSyncModeQuery);
|
||||
}
|
||||
|
||||
if (!_getSyncModePathsQuery.initOrReset("SELECT path from syncmode", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _getSyncModePathsQuery);
|
||||
}
|
||||
|
||||
// Last access time
|
||||
if (!_getLastAccessQuery.initOrReset("SELECT lastaccess FROM syncmode WHERE path=?1;", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _getLastAccessQuery);
|
||||
}
|
||||
|
||||
if (!_setLastAccessQuery.initOrReset("UPDATE syncmode SET lastaccess=?1 WHERE path=?2;", _db)) {
|
||||
return sqlFail("prepare _getErrorBlacklistQuery", _setLastAccessQuery);
|
||||
}
|
||||
|
||||
// don't start a new transaction now
|
||||
commitInternal(QString("checkConnect End"), false);
|
||||
|
||||
@@ -600,7 +665,6 @@ void SyncJournalDb::close()
|
||||
qCInfo(lcDb) << "Closing DB" << _dbFile;
|
||||
|
||||
commitTransaction();
|
||||
|
||||
_db.close();
|
||||
_avoidReadFromDbOnNextSyncFilter.clear();
|
||||
_metadataTableIsEmpty = false;
|
||||
@@ -2035,6 +2099,7 @@ void SyncJournalDb::commitInternal(const QString &context, bool startTrans)
|
||||
SyncJournalDb::~SyncJournalDb()
|
||||
{
|
||||
close();
|
||||
_instance = 0;
|
||||
}
|
||||
|
||||
bool SyncJournalDb::isConnected()
|
||||
@@ -2064,4 +2129,185 @@ bool operator==(const SyncJournalDb::UploadInfo &lhs,
|
||||
&& lhs._contentChecksum == rhs._contentChecksum;
|
||||
}
|
||||
|
||||
SyncJournalDb::SyncModeDownload SyncJournalDb::getSyncModeDownload(QString const & path)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return SYNCMODE_DOWNLOADED_NONE;
|
||||
_getSyncModeDownloadQuery.reset_and_clear_bindings();
|
||||
_getSyncModeDownloadQuery.bindValue(1, path);
|
||||
if (!_getSyncModeDownloadQuery.exec()) {
|
||||
qWarning() << "Error SQL statement getSyncModeDownload: "
|
||||
<< _getSyncModeDownloadQuery.lastQuery() << " :"
|
||||
<< _getSyncModeDownloadQuery.error();
|
||||
return SYNCMODE_DOWNLOADED_NONE;
|
||||
}
|
||||
if (!_getSyncModeDownloadQuery.next())
|
||||
return SYNCMODE_DOWNLOADED_NONE;
|
||||
QString modeStr = _getSyncModeDownloadQuery.stringValue(0);
|
||||
if (modeStr.isEmpty())
|
||||
return SYNCMODE_DOWNLOADED_NONE;
|
||||
return static_cast<SyncModeDownload>(modeStr.begin()->toLatin1());
|
||||
}
|
||||
|
||||
int SyncJournalDb::setSyncModeDownload(QString const & path, SyncModeDownload mode)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return -1;
|
||||
QString modeStr(static_cast<char>(mode));
|
||||
_setSyncModeDownloadQuery.reset_and_clear_bindings();
|
||||
_setSyncModeDownloadQuery.bindValue(1, path);
|
||||
_setSyncModeDownloadQuery.bindValue(2, modeStr);
|
||||
if (!_setSyncModeDownloadQuery.exec()) {
|
||||
qWarning() << "Error SQL statement setSyncModeDownload: "
|
||||
<< _setSyncModeDownloadQuery.lastQuery() << " :"
|
||||
<< _setSyncModeDownloadQuery.error();
|
||||
return -1;
|
||||
}
|
||||
return _setSyncModeDownloadQuery.numRowsAffected();
|
||||
}
|
||||
|
||||
|
||||
SyncJournalDb::SyncMode SyncJournalDb::getSyncMode(QString const & path)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return SYNCMODE_NONE;
|
||||
_getSyncModeQuery.reset_and_clear_bindings();
|
||||
_getSyncModeQuery.bindValue(1, path);
|
||||
if (!_getSyncModeQuery.exec()) {
|
||||
qWarning() << "Error SQL statement getSyncMode: "
|
||||
<< _getSyncModeQuery.lastQuery() << " :"
|
||||
<< _getSyncModeQuery.error();
|
||||
return SYNCMODE_NONE;
|
||||
}
|
||||
if (!_getSyncModeQuery.next())
|
||||
return SYNCMODE_NONE;
|
||||
QString modeStr = _getSyncModeQuery.stringValue(0);
|
||||
if (modeStr.isEmpty())
|
||||
return SYNCMODE_NONE;
|
||||
return static_cast<SyncMode>(modeStr.begin()->toLatin1());
|
||||
}
|
||||
|
||||
int SyncJournalDb::setSyncMode(QString const & path, SyncMode mode)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return -1;
|
||||
QString modeStr(static_cast<char>(mode));
|
||||
_setSyncModeQuery.reset_and_clear_bindings();
|
||||
_setSyncModeQuery.bindValue(1, path);
|
||||
_setSyncModeQuery.bindValue(2, modeStr);
|
||||
if (!_setSyncModeQuery.exec()) {
|
||||
qWarning() << "Error SQL statement setSyncMode: "
|
||||
<< _setSyncModeQuery.lastQuery() << " :"
|
||||
<< _setSyncModeQuery.error();
|
||||
return -1;
|
||||
}
|
||||
return _setSyncModeQuery.numRowsAffected();
|
||||
}
|
||||
|
||||
int SyncJournalDb::deleteSyncMode(QString const & path)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return -1;
|
||||
_deleteSyncModeQuery.reset_and_clear_bindings();
|
||||
_deleteSyncModeQuery.bindValue(1, path);
|
||||
if (!_deleteSyncModeQuery.exec()) {
|
||||
qWarning() << "Error SQL statement setSyncMode: "
|
||||
<< _deleteSyncModeQuery.lastQuery() << " :"
|
||||
<< _deleteSyncModeQuery.error();
|
||||
return -1;
|
||||
}
|
||||
return _deleteSyncModeQuery.numRowsAffected();
|
||||
}
|
||||
|
||||
QDateTime SyncJournalDb::getLastAccess(QString const & path)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return QDateTime{};
|
||||
_getLastAccessQuery.reset_and_clear_bindings();
|
||||
_getLastAccessQuery.bindValue(1, path);
|
||||
if (!_getLastAccessQuery.exec()) {
|
||||
qWarning() << "Error SQL statement getSyncMode: "
|
||||
<< _getLastAccessQuery.lastQuery() << " :"
|
||||
<< _getLastAccessQuery.error();
|
||||
return QDateTime{};
|
||||
}
|
||||
if (!_getLastAccessQuery.next())
|
||||
return QDateTime{};
|
||||
|
||||
QString dateString = _getLastAccessQuery.stringValue(0);
|
||||
QString format = "yyyy-MM-dd HH:mm:ss";
|
||||
QDateTime lastAccessDateTime = QDateTime::fromString(dateString, format);
|
||||
|
||||
if (lastAccessDateTime.isNull())
|
||||
qWarning() << "getLastAccess: "
|
||||
"Invalid date returned from journal DB";
|
||||
|
||||
return lastAccessDateTime;
|
||||
}
|
||||
|
||||
int SyncJournalDb::updateLastAccess(QString const & path)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return -1;
|
||||
|
||||
QString format = "yyyy-MM-dd HH:mm:ss";
|
||||
QString currentDateTime = QDateTime::currentDateTime().toString(format);
|
||||
|
||||
_setLastAccessQuery.reset_and_clear_bindings();
|
||||
_setLastAccessQuery.bindValue(1, currentDateTime);
|
||||
_setLastAccessQuery.bindValue(2, path);
|
||||
if (!_setLastAccessQuery.exec()) {
|
||||
qWarning() << "Error SQL statement setSyncMode: "
|
||||
<< _setLastAccessQuery.lastQuery() << " :"
|
||||
<< _setLastAccessQuery.error();
|
||||
return -1;
|
||||
}
|
||||
return _setLastAccessQuery.numRowsAffected();
|
||||
}
|
||||
|
||||
qint64 SyncJournalDb::secondsSinceLastAccess(QString const & path)
|
||||
{
|
||||
QDateTime lastAccess = getLastAccess(path);
|
||||
if (lastAccess.isNull())
|
||||
return -1;
|
||||
qint64 seconds = lastAccess.secsTo(QDateTime::currentDateTime());
|
||||
// secsTo() might return negative values if the lastAccess datetime is after currentDateTime
|
||||
if (seconds < 0)
|
||||
return -1;
|
||||
return seconds;
|
||||
}
|
||||
|
||||
QList<QString> SyncJournalDb::getSyncModePaths()
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return QList<QString> {};
|
||||
QString path;
|
||||
_getSyncModePathsQuery.reset_and_clear_bindings();
|
||||
if (!_getSyncModePathsQuery.exec()) {
|
||||
qWarning() << "Error SQL statement getSyncModePaths: "
|
||||
<< _getSyncModePathsQuery.lastQuery() << " :"
|
||||
<< _getSyncModePathsQuery.error();
|
||||
return QList<QString>{};
|
||||
}
|
||||
|
||||
QList<QString> list;
|
||||
while (_getSyncModePathsQuery.next())
|
||||
list.append(_getSyncModePathsQuery.stringValue(0));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void SyncJournalDb::emitSyncStatusChanged(){
|
||||
qWarning() << Q_FUNC_INFO << "Syncing status changed! Emitting emitSyncStatusChanged.";
|
||||
emit syncStatusChanged();
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
namespace OCC {
|
||||
class SyncJournalFileRecord;
|
||||
class SyncJournalErrorBlacklistRecord;
|
||||
|
||||
/**
|
||||
* @brief Class that handles the sync database
|
||||
@@ -44,6 +45,7 @@ class OCSYNC_EXPORT SyncJournalDb : public QObject
|
||||
public:
|
||||
explicit SyncJournalDb(const QString &dbFilePath, QObject *parent = 0);
|
||||
virtual ~SyncJournalDb();
|
||||
static SyncJournalDb* instance();
|
||||
|
||||
/// Create a journal path for a specific configuration
|
||||
static QString makeDbName(const QString &localPath,
|
||||
@@ -145,7 +147,6 @@ public:
|
||||
void avoidRenamesOnNextSync(const QString &path) { avoidRenamesOnNextSync(path.toUtf8()); }
|
||||
void avoidRenamesOnNextSync(const QByteArray &path);
|
||||
void setPollInfo(const PollInfo &);
|
||||
|
||||
QVector<PollInfo> getPollInfos();
|
||||
|
||||
enum SelectiveSyncListType {
|
||||
@@ -222,13 +223,13 @@ public:
|
||||
/// Store a new or updated record in the database
|
||||
void setConflictRecord(const ConflictRecord &record);
|
||||
|
||||
/// Retrieve a conflict record by path of the file with the conflict tag
|
||||
/// Retrieve a conflict record by path of the _conflict- file
|
||||
ConflictRecord conflictRecord(const QByteArray &path);
|
||||
|
||||
/// Delete a conflict record by path of the file with the conflict tag
|
||||
/// Delete a conflict record by path of the _conflict- file
|
||||
void deleteConflictRecord(const QByteArray &path);
|
||||
|
||||
/// Return all paths of files with a conflict tag in the name and records in the db
|
||||
/// Return all paths of _conflict- files with records in the db
|
||||
QByteArrayList conflictRecordPaths();
|
||||
|
||||
|
||||
@@ -239,6 +240,90 @@ public:
|
||||
*/
|
||||
void clearFileTable();
|
||||
|
||||
enum SyncMode {
|
||||
SYNCMODE_NONE = '\0',
|
||||
SYNCMODE_ONLINE = 'O',
|
||||
SYNCMODE_OFFLINE = 'S',
|
||||
};
|
||||
|
||||
enum SyncModeDownload {
|
||||
SYNCMODE_DOWNLOADED_NONE = '\0',
|
||||
SYNCMODE_DOWNLOADED_NO = 'N',
|
||||
SYNCMODE_DOWNLOADED_YES = 'Y',
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the sync mode of the provided path
|
||||
*
|
||||
* @path File path
|
||||
* @return integer representing if file has been donwloaded.
|
||||
*/
|
||||
SyncModeDownload getSyncModeDownload(QString const & path);
|
||||
|
||||
/**
|
||||
* Sets or registers the sync downloaded boolean
|
||||
*
|
||||
* @path File path
|
||||
* @N, Y : download option
|
||||
* @return Number of modified records on success. -1 on failure.
|
||||
*/
|
||||
int setSyncModeDownload(QString const & path, SyncModeDownload down);
|
||||
|
||||
/**
|
||||
* Retrieves the sync mode of the provided path
|
||||
*
|
||||
* @path File path
|
||||
* @return integer representing the download mode in case of success, -1 on failure. If multiple paths match the @path parameter, the first result will be returned
|
||||
*/
|
||||
SyncMode getSyncMode(QString const & path);
|
||||
|
||||
/**
|
||||
* Retrieves the sync mode of the provided path
|
||||
*
|
||||
* @return the list of paths in the
|
||||
*/
|
||||
QList<QString> getSyncModePaths();
|
||||
|
||||
/**
|
||||
* Sets or registers the sync mode
|
||||
*
|
||||
* @path File path
|
||||
* @mode New download mode
|
||||
* @return Number of modified records on success. -1 on failure.
|
||||
*/
|
||||
int setSyncMode(QString const & path, SyncMode mode);
|
||||
|
||||
/**
|
||||
* Deletes syncmode register matching the provided path
|
||||
*
|
||||
* @path File path
|
||||
* @return Number of affected records on success. -1 on failure.
|
||||
*/
|
||||
int deleteSyncMode(QString const & path);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the last access local datetime of the given path
|
||||
* @path File path
|
||||
* @return On success, QDateTime representing the last access time. On failure, an invalid QDateTime object with null date and time.
|
||||
*/
|
||||
QDateTime getLastAccess(QString const & path);
|
||||
|
||||
/**
|
||||
* @brief Sets the last access time to the current local time in format yyyy-MM-dd HH:mm:ss
|
||||
* @path File path
|
||||
* @return Number of modified records on success. -1 on failure.
|
||||
*/
|
||||
int updateLastAccess(QString const & path);
|
||||
|
||||
/**
|
||||
* @brief Returns the difference in seconds between the current datetime and the record lastAccessDateTime
|
||||
* @path File path
|
||||
* @return Seconds since last accesson success. -1 on failure.
|
||||
*/
|
||||
qint64 secondsSinceLastAccess(QString const & path);
|
||||
|
||||
void emitSyncStatusChanged();
|
||||
|
||||
private:
|
||||
int getFileRecordCount();
|
||||
bool updateDatabaseStructure();
|
||||
@@ -265,13 +350,13 @@ private:
|
||||
int _transaction;
|
||||
bool _metadataTableIsEmpty;
|
||||
|
||||
SqlQuery _getFileRecordQuery;
|
||||
SqlQuery _getFileRecordQuery;
|
||||
SqlQuery _setFileRecordQuery;
|
||||
SqlQuery _getFileRecordQueryByMangledName;
|
||||
SqlQuery _getFileRecordQueryByInode;
|
||||
SqlQuery _getFileRecordQueryByFileId;
|
||||
SqlQuery _getFilesBelowPathQuery;
|
||||
SqlQuery _getAllFilesQuery;
|
||||
SqlQuery _setFileRecordQuery;
|
||||
SqlQuery _setFileRecordChecksumQuery;
|
||||
SqlQuery _setFileRecordLocalMetadataQuery;
|
||||
SqlQuery _getDownloadInfoQuery;
|
||||
@@ -291,10 +376,19 @@ private:
|
||||
SqlQuery _getDataFingerprintQuery;
|
||||
SqlQuery _setDataFingerprintQuery1;
|
||||
SqlQuery _setDataFingerprintQuery2;
|
||||
SqlQuery _getSyncModeDownloadQuery;
|
||||
SqlQuery _setSyncModeDownloadQuery;
|
||||
SqlQuery _getConflictRecordQuery;
|
||||
SqlQuery _setConflictRecordQuery;
|
||||
SqlQuery _deleteConflictRecordQuery;
|
||||
|
||||
SqlQuery _getSyncModeQuery;
|
||||
SqlQuery _setSyncModeQuery;
|
||||
SqlQuery _deleteSyncModeQuery;
|
||||
SqlQuery _getSyncModePathsQuery;
|
||||
SqlQuery _getLastAccessQuery;
|
||||
SqlQuery _setLastAccessQuery;
|
||||
|
||||
/* This is the list of paths we called avoidReadFromDbOnNextSync on.
|
||||
* It means that they should not be written to the DB in any case since doing
|
||||
* that would write the etag and would void the purpose of avoidReadFromDbOnNextSync
|
||||
@@ -303,12 +397,17 @@ private:
|
||||
*/
|
||||
QList<QByteArray> _avoidReadFromDbOnNextSyncFilter;
|
||||
|
||||
static SyncJournalDb *_instance;
|
||||
|
||||
/** The journal mode to use for the db.
|
||||
*
|
||||
* Typically WAL initially, but may be set to other modes via environment
|
||||
* variable, for specific filesystems, or when WAL fails in a particular way.
|
||||
*/
|
||||
QByteArray _journalMode;
|
||||
|
||||
signals:
|
||||
void syncStatusChanged();
|
||||
};
|
||||
|
||||
bool OCSYNC_EXPORT
|
||||
|
||||
@@ -48,10 +48,10 @@
|
||||
#include "csync_rename.h"
|
||||
#include "common/c_jhash.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
#include <common/asserts.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(lcCSync, "sync.csync.csync", QtInfoMsg)
|
||||
|
||||
|
||||
csync_s::csync_s(const char *localUri, OCC::SyncJournalDb *statedb)
|
||||
: statedb(statedb)
|
||||
{
|
||||
@@ -67,6 +67,10 @@ csync_s::csync_s(const char *localUri, OCC::SyncJournalDb *statedb)
|
||||
int csync_update(CSYNC *ctx) {
|
||||
int rc = -1;
|
||||
|
||||
if(ctx->fuseEnabled){
|
||||
qCInfo(lcCSync, "Running FUSE!");
|
||||
}
|
||||
|
||||
if (ctx == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
@@ -83,29 +87,42 @@ int csync_update(CSYNC *ctx) {
|
||||
|
||||
/* update detection for local replica */
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
ctx->current = LOCAL_REPLICA;
|
||||
|
||||
qCInfo(lcCSync, "## Starting local discovery ##");
|
||||
/* we don't do local discovery if fuse is in use */
|
||||
if(!ctx->fuseEnabled){
|
||||
timer.start();
|
||||
ctx->current = LOCAL_REPLICA;
|
||||
|
||||
rc = csync_ftw(ctx, ctx->local.uri, csync_walker, MAX_DEPTH);
|
||||
if (rc < 0) {
|
||||
if(ctx->status_code == CSYNC_STATUS_OK) {
|
||||
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
|
||||
}
|
||||
return rc;
|
||||
qCInfo(lcCSync, "## Starting local discovery ##");
|
||||
|
||||
rc = csync_ftw(ctx, ctx->local.uri, csync_walker, MAX_DEPTH);
|
||||
if (rc < 0) {
|
||||
if(ctx->status_code == CSYNC_STATUS_OK) {
|
||||
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
qCInfo(lcCSync) << "Update detection for local replica took" << timer.elapsed() / 1000.
|
||||
<< "seconds walking" << ctx->local.files.size() << "files";
|
||||
csync_memstat_check();
|
||||
}
|
||||
|
||||
qCInfo(lcCSync) << "Update detection for local replica took" << timer.elapsed() / 1000.
|
||||
<< "seconds walking" << ctx->local.files.size() << "files";
|
||||
csync_memstat_check();
|
||||
|
||||
/* update detection for remote replica */
|
||||
timer.restart();
|
||||
ctx->current = REMOTE_REPLICA;
|
||||
|
||||
std::unordered_map<ByteArrayRef, std::unique_ptr<csync_file_stat_t>, ByteArrayRefHash>::iterator it = ctx->local.files.begin();
|
||||
qDebug() << "LOCAL ######################################################";
|
||||
while (it != ctx->local.files.end()) {
|
||||
qDebug() << "localFile->file_id " << it->second->file_id;
|
||||
qDebug() << "localFile->path " << it->second->path;
|
||||
qDebug() << "localFile->instruction " << it->second->instruction;
|
||||
it++;
|
||||
}
|
||||
qDebug() << "######################################################";
|
||||
|
||||
qCInfo(lcCSync, "## Starting remote discovery ##");
|
||||
|
||||
ctx->current = REMOTE_REPLICA;
|
||||
rc = csync_ftw(ctx, "", csync_walker, MAX_DEPTH);
|
||||
if (rc < 0) {
|
||||
if(ctx->status_code == CSYNC_STATUS_OK) {
|
||||
@@ -114,6 +131,16 @@ int csync_update(CSYNC *ctx) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::unordered_map<ByteArrayRef, std::unique_ptr<csync_file_stat_t>, ByteArrayRefHash>::iterator it2 = ctx->remote.files.begin();
|
||||
qDebug() << "REMOTE ######################################################";
|
||||
while (it2 != ctx->remote.files.end()) {
|
||||
qDebug() << "remote->file_id " << it2->second->file_id;
|
||||
qDebug() << "remote->path " << it2->second->path;
|
||||
qDebug() << "remote->instruction " << it2->second->instruction;
|
||||
it2++;
|
||||
}
|
||||
qDebug() << "######################################################";
|
||||
|
||||
|
||||
qCInfo(lcCSync) << "Update detection for remote replica took" << timer.elapsed() / 1000.
|
||||
<< "seconds walking" << ctx->remote.files.size() << "files";
|
||||
@@ -237,7 +264,8 @@ int csync_s::reinitialize() {
|
||||
remote.read_from_db = 0;
|
||||
read_remote_from_db = true;
|
||||
|
||||
local.files.clear();
|
||||
//FUSE
|
||||
//local.files.clear();
|
||||
remote.files.clear();
|
||||
|
||||
renames.folder_renamed_from.clear();
|
||||
@@ -326,6 +354,195 @@ int csync_abort_requested(CSYNC *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
bool cysnc_update_file(CSYNC *ctx, const char *absolutePath, const QByteArray &relativePath, const QByteArray &fileName, csync_instructions_e instruction)
|
||||
{
|
||||
if (ctx->local.files.findFile(relativePath)) {
|
||||
ctx->local.files.findFile(relativePath)->instruction = instruction;
|
||||
|
||||
//std::unordered_map<ByteArrayRef, std::unique_ptr<csync_file_stat_t>, ByteArrayRefHash>::iterator it = ctx->local.files.begin();
|
||||
qDebug() << "## FOUND FILE IN TREE ######################################################" << relativePath << ctx->local.files.findFile(relativePath)->instruction;
|
||||
// while (it != ctx->local.files.end()) {
|
||||
// qDebug() << "localFile->file_id " << it->second->file_id;
|
||||
// qDebug() << "localFile->path " << it->second->path;
|
||||
// qDebug() << "localFile->original_path " << it->second->original_path;
|
||||
// qDebug() << "localFile->instruction " << it->second->instruction;
|
||||
//qDebug() << "localFile->is_fuse_created_file " << it->second->is_fuse_created_file;
|
||||
// it++;
|
||||
// }
|
||||
// qDebug() << "######################################################";
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
//qDebug() << "ADDING FILE TO TREE!! ######################################################" << absolutePath << relativePath << fileName;
|
||||
// do the whole process for a single file
|
||||
std::unique_ptr<csync_file_stat_t> newfile;
|
||||
csync_file_stat_t *previous_fs = NULL;
|
||||
csync_vio_handle_t *dh = NULL;
|
||||
unsigned int depth = MAX_DEPTH;
|
||||
QByteArray filenameBuffer;
|
||||
QByteArray fullpath;
|
||||
int read_from_db = 0;
|
||||
|
||||
ctx->current = LOCAL_REPLICA;
|
||||
dh = csync_vio_opendir(ctx, absolutePath);
|
||||
if(dh){
|
||||
newfile = csync_vio_readfile(dh, absolutePath, fileName);
|
||||
|
||||
if (newfile) {
|
||||
newfile->instruction = instruction;
|
||||
|
||||
// csync_walker job
|
||||
|
||||
/* Conversion error */
|
||||
if (newfile->path.isEmpty() && !newfile->original_path.isEmpty()) {
|
||||
ctx->status_code = CSYNC_STATUS_INVALID_CHARACTERS;
|
||||
ctx->error_string = c_strdup(newfile->original_path);
|
||||
newfile->original_path.clear();
|
||||
if (dh != nullptr) {
|
||||
csync_vio_closedir(ctx, dh);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point dirent->path only contains the file name.
|
||||
filenameBuffer = newfile->path;
|
||||
if (filenameBuffer.isEmpty()) {
|
||||
ctx->status_code = CSYNC_STATUS_READDIR_ERROR;
|
||||
if (dh != nullptr) {
|
||||
csync_vio_closedir(ctx, dh);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (absolutePath[0] == '\0') {
|
||||
fullpath = filenameBuffer;
|
||||
} else {
|
||||
fullpath = QByteArray() % absolutePath % '/' % filenameBuffer;
|
||||
}
|
||||
|
||||
/* if the filename starts with a . we consider it a hidden file
|
||||
* For windows, the hidden state is also discovered within the vio
|
||||
* local stat function.
|
||||
*/
|
||||
if (filenameBuffer[0] == '.') {
|
||||
if (filenameBuffer != ".sys.admin#recall#") { /* recall file shall not be ignored (#4420) */
|
||||
newfile->is_hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Now process to have a relative path to the sync root for the local replica, or to the data root on the remote.
|
||||
newfile->path = fullpath;
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
ASSERT(newfile->path.startsWith(ctx->local.uri)); // path is relative to uri
|
||||
// "len + 1" to include the slash in-between.
|
||||
newfile->path = newfile->path.mid(strlen(ctx->local.uri) + 1);
|
||||
}
|
||||
|
||||
//previous_fs = ctx->current_fs;
|
||||
//bool recurse = newfile->type == ItemTypeDirectory;
|
||||
|
||||
/* Call walker for the file */
|
||||
int rc = csync_walker(ctx, std::move(newfile));
|
||||
/* this function may update ctx->current and ctx->read_from_db */
|
||||
|
||||
if (rc < 0) {
|
||||
if (CSYNC_STATUS_IS_OK(ctx->status_code)) {
|
||||
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
|
||||
return false;
|
||||
}
|
||||
if (dh != nullptr) {
|
||||
csync_vio_closedir(ctx, dh);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// if it is a folder look into the files
|
||||
// CSYNC *ctx, const char *uri, const QByteArray &key, csync_instructions_e instruction
|
||||
//if (recurse && rc == 0) {
|
||||
// qDebug() << "FOUND DIRECTORY ######################################################" << absolutePath << relativePath << fileName;
|
||||
// rc = cysnc_update_file(ctx, absolutePath, relativePath, fileName, instruction);
|
||||
// if (!rc) {
|
||||
// ctx->current_fs = previous_fs;
|
||||
// if (dh != nullptr) {
|
||||
// csync_vio_closedir(ctx, dh);
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if (ctx->current_fs && !ctx->current_fs->child_modified
|
||||
// && ctx->current_fs->instruction == CSYNC_INSTRUCTION_EVAL) {
|
||||
// if (ctx->current == REMOTE_REPLICA) {
|
||||
// ctx->current_fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
// } else {
|
||||
// ctx->current_fs->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (ctx->current_fs && previous_fs && ctx->current_fs->has_ignored_files) {
|
||||
// /* If a directory has ignored files, put the flag on the parent directory as well */
|
||||
// previous_fs->has_ignored_files = ctx->current_fs->has_ignored_files;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if (ctx->current_fs && previous_fs && ctx->current_fs->child_modified) {
|
||||
// /* If a directory has modified files, put the flag on the parent directory as well */
|
||||
// previous_fs->child_modified = ctx->current_fs->child_modified;
|
||||
//}
|
||||
|
||||
//ctx->current_fs = previous_fs;
|
||||
ctx->remote.read_from_db = read_from_db;
|
||||
|
||||
csync_vio_closedir(ctx, dh);
|
||||
qCDebug(lcCSync, " <= Closing walk for %s with read_from_db %d", absolutePath, read_from_db);
|
||||
|
||||
ctx->current = REMOTE_REPLICA;
|
||||
|
||||
//std::unordered_map<ByteArrayRef, std::unique_ptr<csync_file_stat_t>, ByteArrayRefHash>::iterator it = ctx->local.files.begin();
|
||||
qDebug() << "## AFTER ADDING TO LOCAL ######################################################" << fullpath;
|
||||
// while (it != ctx->local.files.end()) {
|
||||
// qDebug() << "localFile->file_id " << it->second->file_id;
|
||||
// qDebug() << "localFile->path " << it->second->path;
|
||||
// qDebug() << "localFile->original_path " << it->second->original_path;
|
||||
// qDebug() << "localFile->instruction " << it->second->instruction;
|
||||
// it++;
|
||||
// }
|
||||
// qDebug() << "######################################################";
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cysnc_update_is_fuse_created_file(CSYNC *ctx, const QByteArray &relativePath, bool is_fuse_created_file)
|
||||
{
|
||||
if (ctx->local.files.findFile(relativePath)) {
|
||||
ctx->local.files.findFile(relativePath)->is_fuse_created_file = is_fuse_created_file;
|
||||
|
||||
//std::unordered_map<ByteArrayRef, std::unique_ptr<csync_file_stat_t>, ByteArrayRefHash>::iterator it = ctx->local.files.begin();
|
||||
qDebug() << "cysnc_update_is_fuse_created_file ######################################################" << relativePath << ctx->local.files.findFile(relativePath)->is_fuse_created_file;
|
||||
//while (it != ctx->local.files.end()) {
|
||||
// qDebug() << "localFile->file_id " << it->second->file_id;
|
||||
// qDebug() << "localFile->path " << it->second->path;
|
||||
// qDebug() << "localFile->original_path " << it->second->original_path;
|
||||
// qDebug() << "localFile->instruction " << it->second->instruction;
|
||||
// qDebug() << "localFile->is_fuse_created_file " << it->second->is_fuse_created_file;
|
||||
// it++;
|
||||
//}
|
||||
//qDebug() << "######################################################";
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> csync_file_stat_s::fromSyncJournalFileRecord(const OCC::SyncJournalFileRecord &rec)
|
||||
{
|
||||
std::unique_ptr<csync_file_stat_t> st(new csync_file_stat_t);
|
||||
@@ -342,3 +559,4 @@ std::unique_ptr<csync_file_stat_t> csync_file_stat_s::fromSyncJournalFileRecord(
|
||||
st->e2eMangledName = rec._e2eMangledName;
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ struct OCSYNC_EXPORT csync_file_stat_s {
|
||||
bool child_modified BITFIELD(1);
|
||||
bool has_ignored_files BITFIELD(1); // Specify that a directory, or child directory contains ignored files.
|
||||
bool is_hidden BITFIELD(1); // Not saved in the DB, only used during discovery for local files.
|
||||
bool is_fuse_created_file BITFIELD(1); // File initially created by fuse,not touched by the user yet
|
||||
|
||||
QByteArray path;
|
||||
QByteArray rename_path;
|
||||
@@ -186,6 +187,7 @@ struct OCSYNC_EXPORT csync_file_stat_s {
|
||||
, child_modified(false)
|
||||
, has_ignored_files(false)
|
||||
, is_hidden(false)
|
||||
, is_fuse_created_file(false)
|
||||
, error_status(CSYNC_STATUS_OK)
|
||||
, instruction(CSYNC_INSTRUCTION_NONE)
|
||||
{ }
|
||||
@@ -336,6 +338,19 @@ void OCSYNC_EXPORT csync_resume(CSYNC *ctx);
|
||||
*/
|
||||
int OCSYNC_EXPORT csync_abort_requested(CSYNC *ctx);
|
||||
|
||||
/**
|
||||
* @brief Add or update file to the local tree
|
||||
*
|
||||
* @param ctx The csync context.
|
||||
* @param absolutePath The local folder.
|
||||
* @param relativePath Path relative to the local folder..
|
||||
* @param fileName The file name.
|
||||
* @param csync_instructions_e instructio for the file.
|
||||
*/
|
||||
bool OCSYNC_EXPORT cysnc_update_file(CSYNC *ctx, const char *absolutePath, const QByteArray &relativePath, const QByteArray &fileName, csync_instructions_e instruction);
|
||||
|
||||
bool OCSYNC_EXPORT cysnc_update_is_fuse_created_file(CSYNC *ctx, const QByteArray &relativePath, bool is_fuse_created_file);
|
||||
|
||||
time_t OCSYNC_EXPORT oc_httpdate_parse( const char *date );
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "csync_exclude.h"
|
||||
#include "csync_macros.h"
|
||||
|
||||
|
||||
/**
|
||||
* How deep to scan directories.
|
||||
*/
|
||||
@@ -72,6 +73,7 @@ enum csync_replica_e {
|
||||
enum class LocalDiscoveryStyle {
|
||||
FilesystemOnly, //< read all local data from the filesystem
|
||||
DatabaseAndFilesystem, //< read from the db, except for listed paths
|
||||
FUSEFilesystem, //< read from the db, except for listed paths
|
||||
};
|
||||
|
||||
|
||||
@@ -217,6 +219,9 @@ struct OCSYNC_EXPORT csync_s {
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/e39ab33d-1aaf-4125-b6de-50410d9ced1d
|
||||
csync_s(const csync_s &) = delete;
|
||||
csync_s &operator=(const csync_s &) = delete;
|
||||
|
||||
/* fuse selective sync */
|
||||
bool fuseEnabled = false;
|
||||
};
|
||||
|
||||
void set_errno_from_http_errcode( int err );
|
||||
|
||||
@@ -150,7 +150,12 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
break;
|
||||
}
|
||||
cur->instruction = CSYNC_INSTRUCTION_REMOVE;
|
||||
if (ctx->statedb->getSyncMode(cur->path) == OCC::SyncJournalDb::SYNCMODE_NONE)
|
||||
{
|
||||
/* Do not remove files in a directory that was not open yet */
|
||||
break;
|
||||
}
|
||||
cur->instruction = CSYNC_INSTRUCTION_REMOVE;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_EVAL_RENAME: {
|
||||
// By default, the EVAL_RENAME decays into a NEW
|
||||
@@ -302,7 +307,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
is_conflict = false;
|
||||
} else {
|
||||
// If the size or mtime is different, it's definitely a conflict.
|
||||
is_conflict = ((other->size != cur->size) || (other->modtime != cur->modtime));
|
||||
is_conflict = ((other->size != cur->size) || (other->modtime != cur->modtime)) && !(cur->is_fuse_created_file);
|
||||
|
||||
// It could be a conflict even if size and mtime match!
|
||||
//
|
||||
@@ -355,13 +360,22 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
// sizes and mtimes pops up when the local database is lost for
|
||||
// whatever reason.
|
||||
}
|
||||
|
||||
if (ctx->current == REMOTE_REPLICA) {
|
||||
// If the files are considered equal, only update the DB with the etag from remote
|
||||
cur->instruction = is_conflict ? CSYNC_INSTRUCTION_CONFLICT : CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
other->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
} else {
|
||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
other->instruction = is_conflict ? CSYNC_INSTRUCTION_CONFLICT : CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
if (is_conflict)
|
||||
other->instruction = CSYNC_INSTRUCTION_CONFLICT;
|
||||
else {
|
||||
if(other->type == ItemTypeDirectory && cur->type == ItemTypeDirectory)
|
||||
other->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
else
|
||||
other->instruction = (!cur->is_fuse_created_file) ? CSYNC_INSTRUCTION_UPDATE_METADATA : CSYNC_INSTRUCTION_SYNC;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -296,8 +296,9 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Default to NEW unless we're sure it's a rename.
|
||||
fs->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
// Default to NEW unless we're sure it's a rename or fuse is telling otherwise
|
||||
// if (fs->instruction != CSYNC_INSTRUCTION_SYNC)
|
||||
//fs->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
|
||||
bool isRename =
|
||||
base.isValid() && base._type == fs->type
|
||||
@@ -441,8 +442,7 @@ out:
|
||||
|
||||
ctx->current_fs = fs.get();
|
||||
|
||||
qCInfo(lcUpdate, "file: %s, instruction: %s <<=", fs->path.constData(),
|
||||
csync_instruction_str(fs->instruction));
|
||||
qCInfo(lcUpdate) << "file:" << fs->path << "instruction: " << csync_instruction_str(fs->instruction);
|
||||
|
||||
QByteArray path = fs->path;
|
||||
switch (ctx->current) {
|
||||
@@ -546,6 +546,11 @@ static bool fill_tree_from_db(CSYNC *ctx, const char *uri)
|
||||
st->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
}
|
||||
|
||||
/* check if it is offline */
|
||||
//if(ctx->statedb->getSyncMode(rec._path) != OCC::SyncJournalDb::SyncMode::SYNCMODE_OFFLINE)
|
||||
// st->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
qDebug() << "UPDATE #######################" << rec._path << st->instruction;
|
||||
|
||||
/* store into result list. */
|
||||
files[rec._path] = std::move(st);
|
||||
++count;
|
||||
@@ -663,6 +668,12 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ctx->current == REMOTE_REPLICA) {
|
||||
qDebug() << "ALL GOOD!";
|
||||
} else {
|
||||
qDebug() << "IT WILL BREAK!";
|
||||
}
|
||||
|
||||
while ((dirent = csync_vio_readdir(ctx, dh))) {
|
||||
/* Conversion error */
|
||||
if (dirent->path.isEmpty() && !dirent->original_path.isEmpty()) {
|
||||
|
||||
@@ -90,6 +90,10 @@ std::unique_ptr<csync_file_stat_t> csync_vio_readdir(CSYNC *ctx, csync_vio_handl
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_readfile(csync_vio_handle_t *dhandle, const char *uri, const QByteArray &key) {
|
||||
return csync_vio_local_readfile(dhandle, uri, key);
|
||||
}
|
||||
|
||||
char *csync_vio_get_status_string(CSYNC *ctx) {
|
||||
if(ctx->error_string) {
|
||||
return ctx->error_string;
|
||||
|
||||
@@ -35,7 +35,7 @@ typedef struct fhandle_s {
|
||||
csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name);
|
||||
int csync_vio_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle);
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle);
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_readfile(csync_vio_handle_t *dhandle, const char *uri, const QByteArray &key);
|
||||
char *csync_vio_get_status_string(CSYNC *ctx);
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
csync_vio_handle_t OCSYNC_EXPORT *csync_vio_local_opendir(const char *name);
|
||||
int OCSYNC_EXPORT csync_vio_local_closedir(csync_vio_handle_t *dhandle);
|
||||
std::unique_ptr<csync_file_stat_t> OCSYNC_EXPORT csync_vio_local_readdir(csync_vio_handle_t *dhandle);
|
||||
std::unique_ptr<csync_file_stat_t> OCSYNC_EXPORT csync_vio_local_readfile(csync_vio_handle_t *dhandle, const char *uri, const QByteArray &key);
|
||||
|
||||
int OCSYNC_EXPORT csync_vio_local_stat(const char *uri, csync_file_stat_t *buf);
|
||||
|
||||
|
||||
@@ -140,6 +140,62 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
||||
return file_stat;
|
||||
}
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_local_readfile(csync_vio_handle_t *dhandle, const char *uri,
|
||||
const QByteArray &key) {
|
||||
|
||||
dhandle_t *handle = NULL;
|
||||
|
||||
handle = (dhandle_t *) dhandle;
|
||||
struct _tdirent *dirent = NULL;
|
||||
std::unique_ptr<csync_file_stat_t> file_stat;
|
||||
|
||||
do {
|
||||
dirent = _treaddir(handle->dh);
|
||||
if (dirent == NULL)
|
||||
return {};
|
||||
} while (qstrcmp(dirent->d_name, ".") == 0 || qstrcmp(dirent->d_name, "..") == 0 || qstrcmp(dirent->d_name, key) != 0);
|
||||
|
||||
file_stat.reset(new csync_file_stat_t);
|
||||
file_stat->path = c_utf8_from_locale(dirent->d_name);
|
||||
QByteArray fullPath = QByteArray() % const_cast<const char *>(handle->path) % '/' % QByteArray() % const_cast<const char *>(dirent->d_name);
|
||||
if (file_stat->path.isNull()) {
|
||||
file_stat->original_path = fullPath;
|
||||
qCWarning(lcCSyncVIOLocal) << "Invalid characters in file/directory name, please rename:" << dirent->d_name << handle->path;
|
||||
}
|
||||
|
||||
/* Check for availability of d_type, see manpage. */
|
||||
#if defined(_DIRENT_HAVE_D_TYPE) || defined(__APPLE__)
|
||||
switch (dirent->d_type) {
|
||||
case DT_FIFO:
|
||||
case DT_SOCK:
|
||||
case DT_CHR:
|
||||
case DT_BLK:
|
||||
break;
|
||||
case DT_DIR:
|
||||
case DT_REG:
|
||||
if (dirent->d_type == DT_DIR) {
|
||||
file_stat->type = ItemTypeDirectory;
|
||||
} else {
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// TODO handle directories
|
||||
//file_stat->type = ItemTypeFile;
|
||||
#endif
|
||||
|
||||
if (file_stat->path.isNull())
|
||||
return file_stat;
|
||||
|
||||
if (_csync_vio_local_stat_mb(fullPath.constData(), file_stat.get()) < 0) {
|
||||
// Will get excluded by _csync_detect_update.
|
||||
file_stat->type = ItemTypeSkip;
|
||||
}
|
||||
return file_stat;
|
||||
}
|
||||
|
||||
|
||||
int csync_vio_local_stat(const char *uri, csync_file_stat_t *buf)
|
||||
{
|
||||
|
||||
@@ -35,87 +35,92 @@
|
||||
|
||||
#include "vio/csync_vio_local.h"
|
||||
|
||||
#include "atlstr.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(lcCSyncVIOLocal, "sync.csync.vio_local", QtInfoMsg)
|
||||
|
||||
/*
|
||||
* directory functions
|
||||
*/
|
||||
|
||||
typedef struct dhandle_s {
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind;
|
||||
int firstFind;
|
||||
mbchar_t *path; // Always ends with '\'
|
||||
typedef struct dhandle_s
|
||||
{
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind;
|
||||
int firstFind;
|
||||
mbchar_t *path; // Always ends with '\'
|
||||
} dhandle_t;
|
||||
|
||||
static int _csync_vio_local_stat_mb(const mbchar_t *uri, csync_file_stat_t *buf);
|
||||
|
||||
csync_vio_handle_t *csync_vio_local_opendir(const char *name) {
|
||||
dhandle_t *handle = NULL;
|
||||
mbchar_t *dirname = NULL;
|
||||
csync_vio_handle_t *csync_vio_local_opendir(const char *name)
|
||||
{
|
||||
dhandle_t *handle = NULL;
|
||||
mbchar_t *dirname = NULL;
|
||||
|
||||
handle = (dhandle_t*)c_malloc(sizeof(dhandle_t));
|
||||
handle = (dhandle_t *)c_malloc(sizeof(dhandle_t));
|
||||
|
||||
// the file wildcard has to be attached
|
||||
int len_name = strlen(name);
|
||||
if( len_name ) {
|
||||
char *h = NULL;
|
||||
// the file wildcard has to be attached
|
||||
int len_name = strlen(name);
|
||||
if (len_name) {
|
||||
char *h = NULL;
|
||||
|
||||
// alloc an enough large buffer to take the name + '/*' + the closing zero.
|
||||
h = (char*)c_malloc(len_name+3);
|
||||
strncpy( h, name, 1+len_name);
|
||||
strncat(h, "/*", 2);
|
||||
// alloc an enough large buffer to take the name + '/*' + the closing zero.
|
||||
h = (char *)c_malloc(len_name + 3);
|
||||
strncpy(h, name, 1 + len_name);
|
||||
strncat(h, "/*", 2);
|
||||
|
||||
dirname = c_utf8_path_to_locale(h);
|
||||
SAFE_FREE(h);
|
||||
}
|
||||
dirname = c_utf8_path_to_locale(h);
|
||||
SAFE_FREE(h);
|
||||
}
|
||||
|
||||
if( dirname ) {
|
||||
handle->hFind = FindFirstFile(dirname, &(handle->ffd));
|
||||
}
|
||||
if (dirname) {
|
||||
handle->hFind = FindFirstFile(dirname, &(handle->ffd));
|
||||
}
|
||||
|
||||
if (!dirname || handle->hFind == INVALID_HANDLE_VALUE) {
|
||||
c_free_locale_string(dirname);
|
||||
int retcode = GetLastError();
|
||||
if( retcode == ERROR_FILE_NOT_FOUND ) {
|
||||
errno = ENOENT;
|
||||
} else {
|
||||
errno = EACCES;
|
||||
}
|
||||
SAFE_FREE(handle);
|
||||
return NULL;
|
||||
}
|
||||
if (!dirname || handle->hFind == INVALID_HANDLE_VALUE) {
|
||||
c_free_locale_string(dirname);
|
||||
int retcode = GetLastError();
|
||||
if (retcode == ERROR_FILE_NOT_FOUND) {
|
||||
errno = ENOENT;
|
||||
} else {
|
||||
errno = EACCES;
|
||||
}
|
||||
SAFE_FREE(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
handle->firstFind = 1; // Set a flag that there first fileinfo is available.
|
||||
handle->firstFind = 1; // Set a flag that there first fileinfo is available.
|
||||
|
||||
dirname[std::wcslen(dirname) - 1] = L'\0'; // remove the *
|
||||
handle->path = dirname;
|
||||
dirname[std::wcslen(dirname) - 1] = L'\0'; // remove the *
|
||||
handle->path = dirname;
|
||||
|
||||
return (csync_vio_handle_t *) handle;
|
||||
return (csync_vio_handle_t *)handle;
|
||||
}
|
||||
|
||||
int csync_vio_local_closedir(csync_vio_handle_t *dhandle) {
|
||||
dhandle_t *handle = NULL;
|
||||
int rc = -1;
|
||||
int csync_vio_local_closedir(csync_vio_handle_t *dhandle)
|
||||
{
|
||||
dhandle_t *handle = NULL;
|
||||
int rc = -1;
|
||||
|
||||
if (dhandle == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (dhandle == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
handle = (dhandle_t *) dhandle;
|
||||
// FindClose returns non-zero on success
|
||||
if( FindClose(handle->hFind) != 0 ) {
|
||||
rc = 0;
|
||||
} else {
|
||||
// error case, set errno
|
||||
errno = EBADF;
|
||||
}
|
||||
handle = (dhandle_t *)dhandle;
|
||||
// FindClose returns non-zero on success
|
||||
if (FindClose(handle->hFind) != 0) {
|
||||
rc = 0;
|
||||
} else {
|
||||
// error case, set errno
|
||||
errno = EBADF;
|
||||
}
|
||||
|
||||
c_free_locale_string(handle->path);
|
||||
SAFE_FREE(handle);
|
||||
c_free_locale_string(handle->path);
|
||||
SAFE_FREE(handle);
|
||||
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,63 +130,62 @@ static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder)
|
||||
t <<= 32;
|
||||
t += (UINT32)filetime->dwLowDateTime;
|
||||
t -= 116444736000000000LL;
|
||||
if (t < 0)
|
||||
{
|
||||
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
|
||||
if (t < 0) {
|
||||
if (remainder)
|
||||
*remainder = 9999999 - (-t - 1) % 10000000;
|
||||
return -1 - ((-t - 1) / 10000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (remainder) *remainder = t % 10000000;
|
||||
} else {
|
||||
if (remainder)
|
||||
*remainder = t % 10000000;
|
||||
return t / 10000000;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *dhandle)
|
||||
{
|
||||
dhandle_t *handle = NULL;
|
||||
std::unique_ptr<csync_file_stat_t> file_stat;
|
||||
DWORD rem;
|
||||
|
||||
dhandle_t *handle = NULL;
|
||||
std::unique_ptr<csync_file_stat_t> file_stat;
|
||||
DWORD rem;
|
||||
handle = (dhandle_t *)dhandle;
|
||||
|
||||
handle = (dhandle_t *) dhandle;
|
||||
errno = 0;
|
||||
|
||||
errno = 0;
|
||||
// the win32 functions get the first valid entry with the opendir
|
||||
// thus we must not jump to next entry if it was the first find.
|
||||
if (handle->firstFind) {
|
||||
handle->firstFind = 0;
|
||||
} else {
|
||||
if (FindNextFile(handle->hFind, &(handle->ffd)) == 0) {
|
||||
// might be error, check!
|
||||
int dwError = GetLastError();
|
||||
if (dwError != ERROR_NO_MORE_FILES) {
|
||||
errno = EACCES; // no more files is fine. Otherwise EACCESS
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto path = c_utf8_from_locale(handle->ffd.cFileName);
|
||||
if (path == "." || path == "..")
|
||||
return csync_vio_local_readdir(dhandle);
|
||||
|
||||
// the win32 functions get the first valid entry with the opendir
|
||||
// thus we must not jump to next entry if it was the first find.
|
||||
if( handle->firstFind ) {
|
||||
handle->firstFind = 0;
|
||||
} else {
|
||||
if( FindNextFile(handle->hFind, &(handle->ffd)) == 0 ) {
|
||||
// might be error, check!
|
||||
int dwError = GetLastError();
|
||||
if (dwError != ERROR_NO_MORE_FILES) {
|
||||
errno = EACCES; // no more files is fine. Otherwise EACCESS
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto path = c_utf8_from_locale(handle->ffd.cFileName);
|
||||
if (path == "." || path == "..")
|
||||
return csync_vio_local_readdir(dhandle);
|
||||
file_stat.reset(new csync_file_stat_t);
|
||||
file_stat->path = path;
|
||||
|
||||
file_stat.reset(new csync_file_stat_t);
|
||||
file_stat->path = path;
|
||||
|
||||
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
// Detect symlinks, and treat junctions as symlinks too.
|
||||
if (handle->ffd.dwReserved0 == IO_REPARSE_TAG_SYMLINK
|
||||
|| handle->ffd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) {
|
||||
file_stat->type = ItemTypeSoftLink;
|
||||
} else {
|
||||
// The SIS and DEDUP reparse points should be treated as
|
||||
// regular files. We don't know about the other ones yet,
|
||||
// but will also treat them normally for now.
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
// Detect symlinks, and treat junctions as symlinks too.
|
||||
if (handle->ffd.dwReserved0 == IO_REPARSE_TAG_SYMLINK
|
||||
|| handle->ffd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) {
|
||||
file_stat->type = ItemTypeSoftLink;
|
||||
} else {
|
||||
// The SIS and DEDUP reparse points should be treated as
|
||||
// regular files. We don't know about the other ones yet,
|
||||
// but will also treat them normally for now.
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
|
||||
file_stat->type = ItemTypeSkip;
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
file_stat->type = ItemTypeDirectory;
|
||||
@@ -190,11 +194,11 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
||||
}
|
||||
|
||||
/* Check for the hidden flag */
|
||||
if( handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) {
|
||||
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
|
||||
file_stat->is_hidden = true;
|
||||
}
|
||||
|
||||
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow;
|
||||
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD) + 1)) + handle->ffd.nFileSizeLow;
|
||||
file_stat->modtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem);
|
||||
|
||||
std::wstring fullPath;
|
||||
@@ -210,6 +214,136 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
||||
return file_stat;
|
||||
}
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_local_readfile(csync_vio_handle_t *dhandle, const char *uri,
|
||||
const QByteArray &key)
|
||||
{
|
||||
//dhandle_t *handle = NULL;
|
||||
//handle = (dhandle_t *)dhandle;
|
||||
//DWORD rem;
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> file_stat;
|
||||
|
||||
// the win32 functions get the first valid entry with the opendir
|
||||
// thus we must not jump to next entry if it was the first find.
|
||||
//if (handle->firstFind) {
|
||||
// handle->firstFind = 0;
|
||||
//} else {
|
||||
// if (FindNextFile(handle->hFind, &(handle->ffd)) == 0) {
|
||||
// // might be error, check!
|
||||
// int dwError = GetLastError();
|
||||
// if (dwError != ERROR_NO_MORE_FILES) {
|
||||
// errno = EACCES; // no more files is fine. Otherwise EACCESS
|
||||
// }
|
||||
// return nullptr;
|
||||
// }
|
||||
//}
|
||||
|
||||
//WIN32_FIND_DATA ffd;
|
||||
// DWORD rem;
|
||||
// HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
// DWORD dwError = 0;
|
||||
//TCHAR szName[MAX_PATH];
|
||||
// QByteArray fullPath(uri + "/" + key);
|
||||
|
||||
//_tcscpy(szName, CA2T(fullPath.constData()));
|
||||
// qDebug() << "** looking for " << szName;
|
||||
|
||||
DWORD rem;
|
||||
dhandle_t *handle = NULL;
|
||||
mbchar_t *dirnameAndFile = NULL;
|
||||
|
||||
handle = (dhandle_t *)c_malloc(sizeof(dhandle_t));
|
||||
|
||||
//const char *fullPath = std::string(uri + std::string("/") + key.constData()).c_str();
|
||||
int len_name = strlen(uri);
|
||||
if (len_name) {
|
||||
char *buffer = NULL;
|
||||
|
||||
// alloc an enough large buffer to take the name + '/*' + the closing zero.
|
||||
buffer = (char *)c_malloc(len_name + 2 + key.size());
|
||||
strncpy(buffer, uri, 1 + len_name);
|
||||
strncat(buffer, "/", 1);
|
||||
strncat(buffer, key.constData(), key.size());
|
||||
|
||||
dirnameAndFile = c_utf8_path_to_locale(buffer);
|
||||
SAFE_FREE(buffer);
|
||||
}
|
||||
|
||||
if (dirnameAndFile) {
|
||||
handle->hFind = FindFirstFile(dirnameAndFile, &(handle->ffd));
|
||||
}
|
||||
|
||||
if (!dirnameAndFile || handle->hFind == INVALID_HANDLE_VALUE) {
|
||||
c_free_locale_string(dirnameAndFile);
|
||||
int retcode = GetLastError();
|
||||
if (retcode == ERROR_FILE_NOT_FOUND) {
|
||||
errno = ENOENT;
|
||||
} else {
|
||||
errno = EACCES;
|
||||
}
|
||||
SAFE_FREE(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto path = c_utf8_from_locale(handle->ffd.cFileName);
|
||||
|
||||
// if (path != key) {
|
||||
// qDebug() << "** looking for " << key;
|
||||
// qDebug() << " ** but found " << path.data();
|
||||
//return csync_vio_local_readfile(dhandle, uri, key);
|
||||
// } else {
|
||||
// qDebug() << "** looking for " << key;
|
||||
// qDebug() << " ** and found it!!!!! " << path.data();
|
||||
file_stat.reset(new csync_file_stat_t);
|
||||
file_stat->path = path;
|
||||
|
||||
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
// Detect symlinks, and treat junctions as symlinks too.
|
||||
if (handle->ffd.dwReserved0 == IO_REPARSE_TAG_SYMLINK
|
||||
|| handle->ffd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) {
|
||||
file_stat->type = ItemTypeSoftLink;
|
||||
} else {
|
||||
// The SIS and DEDUP reparse points should be treated as
|
||||
// regular files. We don't know about the other ones yet,
|
||||
// but will also treat them normally for now.
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
|
||||
file_stat->type = ItemTypeSkip;
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
file_stat->type = ItemTypeDirectory;
|
||||
} else {
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
|
||||
/* Check for the hidden flag */
|
||||
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
|
||||
file_stat->is_hidden = true;
|
||||
}
|
||||
|
||||
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD) + 1)) + handle->ffd.nFileSizeLow;
|
||||
file_stat->modtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem);
|
||||
|
||||
//std::string fullpath(uri);
|
||||
//fullpath.append("/");
|
||||
//fullpath.append(path);
|
||||
|
||||
//int wstrsize = MultiByteToWideChar(CP_UTF8, 0, &fullpath[0], (int)fullpath.size(), NULL, 0);
|
||||
// std::wstring wstrPath(wstrsize, 0);
|
||||
// MultiByteToWideChar(CP_UTF8, 0, &fullpath[0], (int)fullpath.size(), &wstrPath[0], wstrsize);
|
||||
|
||||
// if (_csync_vio_local_stat_mb(wstrPath.data(), file_stat.get()) < 0) {
|
||||
// // will get excluded by _csync_detect_update.
|
||||
// file_stat->type = ItemTypeSkip;
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
return file_stat;
|
||||
}
|
||||
|
||||
|
||||
int csync_vio_local_stat(const char *uri, csync_file_stat_t *buf)
|
||||
{
|
||||
@@ -230,17 +364,17 @@ static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf
|
||||
BY_HANDLE_FILE_INFORMATION fileInfo;
|
||||
ULARGE_INTEGER FileIndex;
|
||||
|
||||
h = CreateFileW( wuri, 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL );
|
||||
if( h == INVALID_HANDLE_VALUE ) {
|
||||
h = CreateFileW(wuri, 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
qCCritical(lcCSyncVIOLocal, "CreateFileW failed on %ls", wuri);
|
||||
errno = GetLastError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!GetFileInformationByHandle( h, &fileInfo ) ) {
|
||||
if (!GetFileInformationByHandle(h, &fileInfo)) {
|
||||
qCCritical(lcCSyncVIOLocal, "GetFileInformationByHandle failed on %ls", wuri);
|
||||
errno = GetLastError();
|
||||
CloseHandle(h);
|
||||
@@ -253,7 +387,7 @@ static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf
|
||||
FileIndex.QuadPart &= 0x0000FFFFFFFFFFFF;
|
||||
/* printf("Index: %I64i\n", FileIndex.QuadPart); */
|
||||
buf->inode = FileIndex.QuadPart;
|
||||
buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + fileInfo.nFileSizeLow;
|
||||
buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD) + 1)) + fileInfo.nFileSizeLow;
|
||||
|
||||
DWORD rem;
|
||||
buf->modtime = FileTimeToUnixTime(&fileInfo.ftLastWriteTime, &rem);
|
||||
|
||||
@@ -33,7 +33,6 @@ set(client_UI_SRCS
|
||||
sslerrordialog.ui
|
||||
addcertificatedialog.ui
|
||||
proxyauthdialog.ui
|
||||
notificationwidget.ui
|
||||
mnemonicdialog.ui
|
||||
wizard/owncloudadvancedsetuppage.ui
|
||||
wizard/owncloudconnectionmethoddialog.ui
|
||||
@@ -95,7 +94,6 @@ set(client_SRCS
|
||||
proxyauthdialog.cpp
|
||||
synclogdialog.cpp
|
||||
tooltipupdater.cpp
|
||||
notificationwidget.cpp
|
||||
notificationconfirmjob.cpp
|
||||
servernotificationhandler.cpp
|
||||
guiutility.cpp
|
||||
@@ -141,14 +139,29 @@ IF( APPLE )
|
||||
list(REMOVE_ITEM client_SRCS settingsdialog.cpp)
|
||||
list(APPEND client_SRCS socketapisocket_mac.mm)
|
||||
list(APPEND client_SRCS systray.mm)
|
||||
list(APPEND client_SRCS vfs_mac.cpp)
|
||||
list(APPEND client_SRCS fileManager.mm)
|
||||
list(APPEND client_SRCS vfs_maccontroller.cpp)
|
||||
|
||||
if(SPARKLE_FOUND)
|
||||
# Define this, we need to check in updater.cpp
|
||||
add_definitions( -DHAVE_SPARKLE )
|
||||
list(APPEND updater_SRCS updater/sparkleupdater_mac.mm)
|
||||
endif()
|
||||
|
||||
add_definitions( -D_DARWIN_USE_64_BIT_INODE )
|
||||
add_definitions( -D_FILE_OFFSET_BITS=64 )
|
||||
ENDIF()
|
||||
|
||||
IF( WIN32 )
|
||||
list(APPEND client_SRCS vfs_windows.cpp)
|
||||
ENDIF()
|
||||
|
||||
IF( WIN32 OR APPLE )
|
||||
list(APPEND client_SRCS syncwrapper.cpp)
|
||||
ENDIF()
|
||||
|
||||
|
||||
IF( NOT WIN32 AND NOT APPLE )
|
||||
set(client_SRCS ${client_SRCS} folderwatcher_linux.cpp)
|
||||
ENDIF()
|
||||
@@ -274,7 +287,7 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 main.cpp ${final_src})
|
||||
else()
|
||||
# set(CMAKE_INSTALL_PREFIX ".") # Examples use /Applications. hurmpf.
|
||||
set(MACOSX_BUNDLE_ICON_FILE "ownCloud.icns")
|
||||
set(MACOSX_BUNDLE_ICON_FILE "${APPLICATION_ICON_NAME}.icns")
|
||||
|
||||
# we must add MACOSX_BUNDLE only if building a bundle
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src})
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "filesystem.h"
|
||||
#include "clientsideencryptionjobs.h"
|
||||
#include "syncresult.h"
|
||||
#include "vfs_windows.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -606,9 +607,12 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
|
||||
{
|
||||
if (indx.data(FolderStatusDelegate::AddButton).toBool()) {
|
||||
// "Add Folder Sync Connection"
|
||||
if (indx.flags() & Qt::ItemIsEnabled) {
|
||||
slotAddFolder();
|
||||
} else {
|
||||
// TODO: FUSE - only one folder is allowed
|
||||
// if (indx.flags() & Qt::ItemIsEnabled) {
|
||||
// slotAddFolder();
|
||||
// } else {
|
||||
|
||||
if (!indx.flags() & !Qt::ItemIsEnabled){
|
||||
QToolTip::showText(
|
||||
QCursor::pos(),
|
||||
_model->data(indx, Qt::ToolTipRole).toString(),
|
||||
@@ -637,70 +641,71 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: FUSE - only one folder is allowed
|
||||
void AccountSettings::slotAddFolder()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
folderMan->setSyncEnabled(false); // do not start more syncs.
|
||||
// FolderMan *folderMan = FolderMan::instance();
|
||||
// folderMan->setSyncEnabled(false); // do not start more syncs.
|
||||
|
||||
FolderWizard *folderWizard = new FolderWizard(_accountState->account(), this);
|
||||
// FolderWizard *folderWizard = new FolderWizard(_accountState->account(), this);
|
||||
|
||||
connect(folderWizard, &QDialog::accepted, this, &AccountSettings::slotFolderWizardAccepted);
|
||||
connect(folderWizard, &QDialog::rejected, this, &AccountSettings::slotFolderWizardRejected);
|
||||
folderWizard->open();
|
||||
// connect(folderWizard, &QDialog::accepted, this, &AccountSettings::slotFolderWizardAccepted);
|
||||
// connect(folderWizard, &QDialog::rejected, this, &AccountSettings::slotFolderWizardRejected);
|
||||
// folderWizard->open();
|
||||
}
|
||||
|
||||
|
||||
// TODO: FUSE - only one folder is allowed
|
||||
void AccountSettings::slotFolderWizardAccepted()
|
||||
{
|
||||
FolderWizard *folderWizard = qobject_cast<FolderWizard *>(sender());
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
// FolderWizard *folderWizard = qobject_cast<FolderWizard *>(sender());
|
||||
// FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
qCInfo(lcAccountSettings) << "Folder wizard completed";
|
||||
// qCInfo(lcAccountSettings) << "Folder wizard completed";
|
||||
|
||||
FolderDefinition definition;
|
||||
definition.localPath = FolderDefinition::prepareLocalPath(
|
||||
folderWizard->field(QLatin1String("sourceFolder")).toString());
|
||||
definition.targetPath = FolderDefinition::prepareTargetPath(
|
||||
folderWizard->property("targetPath").toString());
|
||||
// FolderDefinition definition;
|
||||
// definition.localPath = FolderDefinition::prepareLocalPath(
|
||||
// folderWizard->field(QLatin1String("sourceFolder")).toString());
|
||||
// definition.targetPath = FolderDefinition::prepareTargetPath(
|
||||
// folderWizard->property("targetPath").toString());
|
||||
|
||||
{
|
||||
QDir dir(definition.localPath);
|
||||
if (!dir.exists()) {
|
||||
qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath;
|
||||
if (!dir.mkpath(".")) {
|
||||
QMessageBox::warning(this, tr("Folder creation failed"),
|
||||
tr("<p>Could not create local folder <i>%1</i>.")
|
||||
.arg(QDir::toNativeSeparators(definition.localPath)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
FileSystem::setFolderMinimumPermissions(definition.localPath);
|
||||
Utility::setupFavLink(definition.localPath);
|
||||
}
|
||||
// {
|
||||
// QDir dir(definition.localPath);
|
||||
// if (!dir.exists()) {
|
||||
// qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath;
|
||||
// if (!dir.mkpath(".")) {
|
||||
// QMessageBox::warning(this, tr("Folder creation failed"),
|
||||
// tr("<p>Could not create local folder <i>%1</i>.")
|
||||
// .arg(QDir::toNativeSeparators(definition.localPath)));
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// FileSystem::setFolderMinimumPermissions(definition.localPath);
|
||||
// Utility::setupFavLink(definition.localPath);
|
||||
// }
|
||||
|
||||
/* take the value from the definition of already existing folders. All folders have
|
||||
* the same setting so far.
|
||||
* The default is to not sync hidden files
|
||||
*/
|
||||
definition.ignoreHiddenFiles = folderMan->ignoreHiddenFiles();
|
||||
// /* take the value from the definition of already existing folders. All folders have
|
||||
// * the same setting so far.
|
||||
// * The default is to not sync hidden files
|
||||
// */
|
||||
// definition.ignoreHiddenFiles = folderMan->ignoreHiddenFiles();
|
||||
|
||||
if (folderMan->navigationPaneHelper().showInExplorerNavigationPane())
|
||||
definition.navigationPaneClsid = QUuid::createUuid();
|
||||
// if (folderMan->navigationPaneHelper().showInExplorerNavigationPane())
|
||||
// definition.navigationPaneClsid = QUuid::createUuid();
|
||||
|
||||
auto selectiveSyncBlackList = folderWizard->property("selectiveSyncBlackList").toStringList();
|
||||
// auto selectiveSyncBlackList = folderWizard->property("selectiveSyncBlackList").toStringList();
|
||||
|
||||
folderMan->setSyncEnabled(true);
|
||||
// folderMan->setSyncEnabled(true);
|
||||
|
||||
Folder *f = folderMan->addFolder(_accountState, definition);
|
||||
if (f) {
|
||||
f->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, selectiveSyncBlackList);
|
||||
// Folder *f = folderMan->addFolder(_accountState, definition);
|
||||
// if (f) {
|
||||
// f->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, selectiveSyncBlackList);
|
||||
|
||||
// The user already accepted the selective sync dialog. everything is in the white list
|
||||
f->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList,
|
||||
QStringList() << QLatin1String("/"));
|
||||
folderMan->scheduleAllFolders();
|
||||
emit folderChanged();
|
||||
}
|
||||
// // The user already accepted the selective sync dialog. everything is in the white list
|
||||
// f->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList,
|
||||
// QStringList() << QLatin1String("/"));
|
||||
// folderMan->scheduleAllFolders();
|
||||
// emit folderChanged();
|
||||
// }
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderWizardRejected()
|
||||
@@ -737,7 +742,7 @@ void AccountSettings::slotRemoveCurrentFolder()
|
||||
return;
|
||||
}
|
||||
|
||||
folderMan->removeFolder(folder);
|
||||
folderMan->removeFolder();
|
||||
_model->removeRow(row);
|
||||
|
||||
// single folder fix to show add-button and hide remove-button
|
||||
@@ -834,7 +839,7 @@ void AccountSettings::slotEnableCurrentFolder()
|
||||
if (currentlyPaused)
|
||||
_wasDisabledBefore = true;
|
||||
|
||||
_model->slotUpdateFolderState(f);
|
||||
_model->slotUpdateFolderState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,7 +847,7 @@ void AccountSettings::slotScheduleCurrentFolder()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
if (auto folder = folderMan->folder(selectedFolderAlias())) {
|
||||
folderMan->scheduleFolder(folder);
|
||||
folderMan->scheduleFolder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,7 +856,7 @@ void AccountSettings::slotScheduleCurrentFolderForceRemoteDiscovery()
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
if (auto folder = folderMan->folder(selectedFolderAlias())) {
|
||||
folder->journalDb()->forceRemoteDiscoveryNextSync();
|
||||
folderMan->scheduleFolder(folder);
|
||||
folderMan->scheduleFolder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,11 +867,11 @@ void AccountSettings::slotForceSyncCurrentFolder()
|
||||
// Terminate and reschedule any running sync
|
||||
if (Folder *current = folderMan->currentSyncFolder()) {
|
||||
folderMan->terminateSyncProcess();
|
||||
folderMan->scheduleFolder(current);
|
||||
folderMan->scheduleFolder();
|
||||
}
|
||||
|
||||
// Insert the selected folder at the front of the queue
|
||||
folderMan->scheduleFolderNext(selectedFolder);
|
||||
folderMan->scheduleFolderNext();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -916,7 +921,7 @@ void AccountSettings::slotAccountStateChanged()
|
||||
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
foreach (Folder *folder, folderMan->map().values()) {
|
||||
_model->slotUpdateFolderState(folder);
|
||||
_model->slotUpdateFolderState();
|
||||
}
|
||||
|
||||
QString server = QString::fromLatin1("<a href=\"%1\">%2</a>")
|
||||
@@ -1019,12 +1024,12 @@ void AccountSettings::slotLinkActivated(const QString &link)
|
||||
|
||||
// Make sure the folder itself is expanded
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
QModelIndex folderIndx = _model->indexForPath(f, QString());
|
||||
QModelIndex folderIndx = _model->indexForPath(QString());
|
||||
if (!ui->_folderList->isExpanded(folderIndx)) {
|
||||
ui->_folderList->setExpanded(folderIndx, true);
|
||||
}
|
||||
|
||||
QModelIndex indx = _model->indexForPath(f, myFolder);
|
||||
QModelIndex indx = _model->indexForPath(myFolder);
|
||||
if (indx.isValid()) {
|
||||
// make sure all the parents are expanded
|
||||
for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
|
||||
@@ -1070,7 +1075,7 @@ void AccountSettings::refreshSelectiveSyncStatus()
|
||||
if (myFolder.endsWith('/')) {
|
||||
myFolder.chop(1);
|
||||
}
|
||||
QModelIndex theIndx = _model->indexForPath(folder, myFolder);
|
||||
QModelIndex theIndx = _model->indexForPath(myFolder);
|
||||
if (theIndx.isValid()) {
|
||||
msg += QString::fromLatin1("<a href=\"%1?folder=%2\">%1</a>")
|
||||
.arg(Utility::escape(myFolder), Utility::escape(folder->alias()));
|
||||
@@ -1160,6 +1165,10 @@ void AccountSettings::slotDeleteAccount()
|
||||
|
||||
// IMPORTANT: "this" is deleted from this point on. We should probably remove this synchronous
|
||||
// .exec() QMessageBox magic above as it recurses into the event loop.
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
VfsWindows::instance()->unmount();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AccountSettings::event(QEvent *e)
|
||||
|
||||
@@ -60,6 +60,10 @@ int ActivityItemDelegate::rowHeight()
|
||||
QFontMetrics fm(f);
|
||||
|
||||
_margin = fm.height() / 2;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
_margin += 5;
|
||||
#endif
|
||||
}
|
||||
return iconHeight() + 5 * _margin;
|
||||
}
|
||||
@@ -228,16 +232,11 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
|
||||
|
||||
// change pen color if the line is selected
|
||||
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
|
||||
? QPalette::Normal
|
||||
: QPalette::Disabled;
|
||||
if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
|
||||
cg = QPalette::Inactive;
|
||||
QPalette::ColorGroup cg = option.state & (QStyle::State_Enabled | QStyle::State_Active)
|
||||
? QPalette::Normal
|
||||
: QPalette::Inactive;
|
||||
|
||||
if (option.state & QStyle::State_Selected)
|
||||
painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
|
||||
else
|
||||
painter->setPen(option.palette.color(cg, QPalette::Text));
|
||||
painter->setPen(option.palette.color(cg, QPalette::Text));
|
||||
|
||||
// calculate space for text - use the max possible before using the elipses
|
||||
int spaceLeftForText = option.rect.width() - (actionIconRect.width() + margin + rightMargin + leftMargin) -
|
||||
@@ -258,12 +257,6 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
// draw the message
|
||||
// change pen color for the message
|
||||
if(!messageText.isEmpty()){
|
||||
painter->setPen(p.color(QPalette::Inactive, QPalette::Text));
|
||||
|
||||
// check if line is selected
|
||||
if (option.state & QStyle::State_Selected)
|
||||
painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
|
||||
|
||||
const QString elidedMessage = fm.elidedText(messageText, Qt::ElideRight, spaceLeftForText);
|
||||
painter->drawText(messageTextBox, elidedMessage);
|
||||
}
|
||||
|
||||
@@ -108,9 +108,9 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
||||
|| a._status == SyncFileItem::Restoration){
|
||||
return QIcon(QLatin1String(":/client/resources/state-warning.svg"));
|
||||
}
|
||||
return QIcon(QLatin1String(":/client/resources/activity.png"));
|
||||
} else return QIcon(QLatin1String(":/client/resources/activity.png"));
|
||||
return QVariant();
|
||||
return QIcon(QLatin1String(":/client/resources/state-sync.svg"));
|
||||
}
|
||||
return QIcon(QLatin1String(":/client/resources/activity.png"));
|
||||
break;
|
||||
case ActivityItemDelegate::ObjectTypeRole:
|
||||
return a._objectType;
|
||||
@@ -121,7 +121,6 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
||||
return type;
|
||||
break;
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
case ActivityItemDelegate::ActionTextRole:
|
||||
return a._subject;
|
||||
break;
|
||||
@@ -237,6 +236,12 @@ void ActivityListModel::removeActivityFromActivityList(int row) {
|
||||
removeActivityFromActivityList(activity);
|
||||
}
|
||||
|
||||
void ActivityListModel::addSyncFileItemToActivityList(Activity activity) {
|
||||
qCInfo(lcActivity) << "Successfully added to the activity list: " << activity._subject;
|
||||
_syncFileItemLists.prepend(activity);
|
||||
combineActivityLists();
|
||||
}
|
||||
|
||||
void ActivityListModel::removeActivityFromActivityList(Activity activity) {
|
||||
qCInfo(lcActivity) << "Activity/Notification/Error successfully dismissed: " << activity._subject;
|
||||
qCInfo(lcActivity) << "Trying to remove Activity/Notification/Error from view... ";
|
||||
@@ -271,6 +276,9 @@ void ActivityListModel::combineActivityLists()
|
||||
std::sort(_notificationLists.begin(), _notificationLists.end());
|
||||
resultList.append(_notificationLists);
|
||||
|
||||
std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
|
||||
resultList.append(_syncFileItemLists);
|
||||
|
||||
std::sort(_activityLists.begin(), _activityLists.end());
|
||||
resultList.append(_activityLists);
|
||||
|
||||
@@ -283,18 +291,29 @@ void ActivityListModel::combineActivityLists()
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
bool ActivityListModel::canFetchActivities() const {
|
||||
return _accountState->isConnected() && _accountState->account()->capabilities().hasActivities();
|
||||
}
|
||||
|
||||
void ActivityListModel::fetchMore(const QModelIndex &)
|
||||
{
|
||||
if (_accountState->isConnected()) {
|
||||
_activityLists = ActivityList();
|
||||
_activityLists = ActivityList();
|
||||
if (canFetchActivities()) {
|
||||
startFetchJob();
|
||||
} else {
|
||||
combineActivityLists();
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityListModel::slotRefreshActivity()
|
||||
{
|
||||
_activityLists.clear();
|
||||
startFetchJob();
|
||||
|
||||
if (canFetchActivities()) {
|
||||
startFetchJob();
|
||||
} else {
|
||||
combineActivityLists();
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityListModel::slotRemoveAccount()
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
ActivityList errorsList() { return _notificationErrorsLists; }
|
||||
void addNotificationToActivityList(Activity activity);
|
||||
void addErrorToActivityList(Activity activity);
|
||||
void addSyncFileItemToActivityList(Activity activity);
|
||||
void removeActivityFromActivityList(int row);
|
||||
void removeActivityFromActivityList(Activity activity);
|
||||
|
||||
@@ -66,8 +67,10 @@ signals:
|
||||
private:
|
||||
void startFetchJob();
|
||||
void combineActivityLists();
|
||||
bool canFetchActivities() const;
|
||||
|
||||
ActivityList _activityLists;
|
||||
ActivityList _syncFileItemLists;
|
||||
ActivityList _notificationLists;
|
||||
ActivityList _notificationErrorsLists;
|
||||
ActivityList _finalList;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "accountmanager.h"
|
||||
#include "activityitemdelegate.h"
|
||||
#include "QProgressIndicator.h"
|
||||
#include "notificationwidget.h"
|
||||
#include "notificationconfirmjob.h"
|
||||
#include "servernotificationhandler.h"
|
||||
#include "theme.h"
|
||||
@@ -68,7 +67,6 @@ ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
|
||||
ActivityItemDelegate *delegate = new ActivityItemDelegate;
|
||||
delegate->setParent(this);
|
||||
_ui->_activityList->setItemDelegate(delegate);
|
||||
_ui->_activityList->setBackgroundRole(QPalette::Background);
|
||||
_ui->_activityList->setAlternatingRowColors(true);
|
||||
_ui->_activityList->setModel(_model);
|
||||
|
||||
@@ -77,15 +75,11 @@ ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
|
||||
connect(_model, &ActivityListModel::activityJobStatusCode,
|
||||
this, &ActivityWidget::slotAccountActivityStatus);
|
||||
|
||||
_ui->_copyButton->setToolTip(tr("Copy the activity list to the clipboard."));
|
||||
connect(_ui->_copyButton, &QPushButton::click, this, &ActivityWidget::copyToClipboard);
|
||||
|
||||
connect(_model, &QAbstractItemModel::rowsInserted, this, &ActivityWidget::rowsInserted);
|
||||
|
||||
connect(delegate, &ActivityItemDelegate::primaryButtonClickedOnItemView, this, &ActivityWidget::slotPrimaryButtonClickedOnListView);
|
||||
connect(delegate, &ActivityItemDelegate::secondaryButtonClickedOnItemView, this, &ActivityWidget::slotSecondaryButtonClickedOnListView);
|
||||
connect(_ui->_activityList, &QListView::activated, this, &ActivityWidget::slotOpenFile);
|
||||
connect(&_removeTimer, &QTimer::timeout, this, &ActivityWidget::slotCheckToCleanWidgets);
|
||||
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::progressInfo,
|
||||
this, &ActivityWidget::slotProgressInfo);
|
||||
@@ -104,52 +98,49 @@ ActivityWidget::~ActivityWidget()
|
||||
|
||||
void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
|
||||
{
|
||||
if (progress.status() == ProgressInfo::Reconcile) {
|
||||
// Wipe all non-persistent entries - as well as the persistent ones
|
||||
// in cases where a local discovery was done.
|
||||
auto f = FolderMan::instance()->folder(folder);
|
||||
if (!f)
|
||||
return;
|
||||
const auto &engine = f->syncEngine();
|
||||
const auto style = engine.lastLocalDiscoveryStyle();
|
||||
foreach (Activity activity, _model->errorsList()) {
|
||||
if (activity._folder != folder){
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: this is really not working
|
||||
// if (progress.status() == ProgressInfo::Done
|
||||
// || progress.status() == ProgressInfo::Reconcile) {
|
||||
// // Wipe all non-persistent entries - as well as the persistent ones
|
||||
// // in cases where a local discovery was done.
|
||||
// auto f = FolderMan::instance()->folder(folder);
|
||||
// if (!f)
|
||||
// return;
|
||||
// const auto &engine = f->syncEngine();
|
||||
// const auto style = engine.lastLocalDiscoveryStyle();
|
||||
// foreach (Activity activity, _model->errorsList()) {
|
||||
// if (activity._folder != folder){
|
||||
// continue;
|
||||
// }
|
||||
if (style == LocalDiscoveryStyle::FilesystemOnly){
|
||||
_model->removeActivityFromActivityList(activity);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (style == LocalDiscoveryStyle::FilesystemOnly){
|
||||
// _model->removeActivityFromActivityList(activity);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if(activity._status == SyncFileItem::Conflict && !QFileInfo(f->path() + activity._file).exists()){
|
||||
// _model->removeActivityFromActivityList(activity);
|
||||
// continue;
|
||||
// }
|
||||
if(activity._status == SyncFileItem::Conflict && !QFileInfo(f->path() + activity._file).exists()){
|
||||
_model->removeActivityFromActivityList(activity);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()){
|
||||
// _model->removeActivityFromActivityList(activity);
|
||||
// continue;
|
||||
// }
|
||||
if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()){
|
||||
_model->removeActivityFromActivityList(activity);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if(!QFileInfo(f->path() + activity._file).exists()){
|
||||
// _model->removeActivityFromActivityList(activity);
|
||||
// continue;
|
||||
// }
|
||||
if(!QFileInfo(f->path() + activity._file).exists()){
|
||||
_model->removeActivityFromActivityList(activity);
|
||||
continue;
|
||||
}
|
||||
|
||||
// auto path = QFileInfo(activity._file).dir().path().toUtf8();
|
||||
// if (path == ".")
|
||||
// path.clear();
|
||||
auto path = QFileInfo(activity._file).dir().path().toUtf8();
|
||||
if (path == ".")
|
||||
path.clear();
|
||||
|
||||
// if(engine.shouldDiscoverLocally(path))
|
||||
// _model->removeActivityFromActivityList(activity);
|
||||
// }
|
||||
if(engine.shouldDiscoverLocally(path))
|
||||
_model->removeActivityFromActivityList(activity);
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
if (progress.status() == ProgressInfo::Done) {
|
||||
// We keep track very well of pending conflicts.
|
||||
@@ -177,18 +168,26 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
|
||||
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in " << item->_errorString;
|
||||
|
||||
Activity activity;
|
||||
activity._type = Activity::SyncFileItemType;
|
||||
activity._type = Activity::SyncFileItemType; //client activity
|
||||
activity._status = item->_status;
|
||||
activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
|
||||
activity._subject = item->_errorString;
|
||||
activity._message = item->_originalFile;
|
||||
activity._link = folderInstance->accountState()->account()->url();
|
||||
activity._accName = folderInstance->accountState()->account()->displayName();
|
||||
activity._file = item->_file;
|
||||
activity._folder = folder;
|
||||
|
||||
// add 'protocol error' to activity list
|
||||
_model->addErrorToActivityList(activity);
|
||||
if(item->_status == SyncFileItem::NoStatus || item->_status == SyncFileItem::Success){
|
||||
qCWarning(lcActivity) << "Item " << item->_file << " retrieved successfully.";
|
||||
activity._message.prepend(tr("Synced "));
|
||||
_model->addSyncFileItemToActivityList(activity);
|
||||
} else {
|
||||
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString;
|
||||
activity._subject = item->_errorString;
|
||||
|
||||
// add 'protocol error' to activity list
|
||||
_model->addErrorToActivityList(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,14 +325,17 @@ void ActivityWidget::slotRemoveAccount()
|
||||
|
||||
void ActivityWidget::showLabels()
|
||||
{
|
||||
QString t = tr("Server Activities");
|
||||
t.clear();
|
||||
_ui->_bottomLabel->hide(); // hide whatever was there before
|
||||
QString t("");
|
||||
QSetIterator<QString> i(_accountsWithoutActivities);
|
||||
while (i.hasNext()) {
|
||||
t.append(tr("<br/>Account %1 does not have activities enabled.").arg(i.next()));
|
||||
}
|
||||
_ui->_bottomLabel->setTextFormat(Qt::RichText);
|
||||
_ui->_bottomLabel->setText(t);
|
||||
if(!t.isEmpty()){
|
||||
_ui->_bottomLabel->setTextFormat(Qt::RichText);
|
||||
_ui->_bottomLabel->setText(t);
|
||||
_ui->_bottomLabel->show();
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityWidget::slotAccountActivityStatus(int statusCode)
|
||||
@@ -347,7 +349,7 @@ void ActivityWidget::slotAccountActivityStatus(int statusCode)
|
||||
_accountsWithoutActivities.remove(_accountState->account()->displayName());
|
||||
}
|
||||
|
||||
checkActivityTabVisibility();
|
||||
checkActivityWidgetVisibility();
|
||||
showLabels();
|
||||
}
|
||||
|
||||
@@ -398,16 +400,15 @@ void ActivityWidget::storeActivityList(QTextStream &ts)
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityWidget::checkActivityTabVisibility()
|
||||
void ActivityWidget::checkActivityWidgetVisibility()
|
||||
{
|
||||
int accountCount = AccountManager::instance()->accounts().count();
|
||||
bool hasAccountsWithActivity =
|
||||
_accountsWithoutActivities.count() != accountCount;
|
||||
bool hasNotifications = !_widgetForNotifId.isEmpty();
|
||||
|
||||
_ui->_activityList->setVisible(hasAccountsWithActivity);
|
||||
|
||||
emit hideActivityTab(!hasAccountsWithActivity && !hasNotifications);
|
||||
emit hideActivityTab(!hasAccountsWithActivity);
|
||||
}
|
||||
|
||||
void ActivityWidget::slotOpenFile(QModelIndex indx)
|
||||
@@ -508,7 +509,7 @@ void ActivityWidget::slotSendNotificationRequest(const QString &accountName, con
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityWidget::endNotificationRequest(NotificationWidget *widget, int replyCode)
|
||||
void ActivityWidget::endNotificationRequest(int replyCode)
|
||||
{
|
||||
_notificationRequestsRunning--;
|
||||
slotNotificationRequestFinished(replyCode);
|
||||
@@ -523,7 +524,7 @@ void ActivityWidget::slotNotifyNetworkError(QNetworkReply *reply)
|
||||
|
||||
int resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
endNotificationRequest(job->widget(), resultCode);
|
||||
endNotificationRequest(resultCode);
|
||||
qCWarning(lcActivity) << "Server notify job failed with code " << resultCode;
|
||||
}
|
||||
|
||||
@@ -534,72 +535,10 @@ void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCod
|
||||
return;
|
||||
}
|
||||
|
||||
endNotificationRequest(job->widget(), replyCode);
|
||||
endNotificationRequest(replyCode);
|
||||
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
|
||||
|
||||
// if the notification was successful start a timer that triggers
|
||||
// removal of the done widgets in a few seconds
|
||||
// Add 200 millisecs to the predefined value to make sure that the timer in
|
||||
// widget's method readyToClose() has elapsed.
|
||||
if (replyCode == OCS_SUCCESS_STATUS_CODE || replyCode == OCS_SUCCESS_STATUS_CODE_V2) {
|
||||
//scheduleWidgetToRemove(job->widget());
|
||||
}
|
||||
}
|
||||
|
||||
// blacklist the activity coming in here.
|
||||
void ActivityWidget::slotRequestCleanupAndBlacklist(const Activity &blacklistActivity)
|
||||
{
|
||||
if (!_blacklistedNotifications.contains(blacklistActivity)) {
|
||||
_blacklistedNotifications.append(blacklistActivity);
|
||||
}
|
||||
|
||||
NotificationWidget *widget = _widgetForNotifId[blacklistActivity.ident()];
|
||||
scheduleWidgetToRemove(widget);
|
||||
}
|
||||
|
||||
void ActivityWidget::scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds)
|
||||
{
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
// in five seconds from now, remove the widget.
|
||||
QDateTime removeTime = QDateTime::currentDateTimeUtc().addMSecs(milliseconds);
|
||||
QDateTime &it = _widgetsToRemove[widget];
|
||||
if (!it.isValid() || it > removeTime) {
|
||||
it = removeTime;
|
||||
}
|
||||
if (!_removeTimer.isActive()) {
|
||||
_removeTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Called every second to see if widgets need to be removed.
|
||||
void ActivityWidget::slotCheckToCleanWidgets()
|
||||
{
|
||||
auto currentTime = QDateTime::currentDateTimeUtc();
|
||||
auto it = _widgetsToRemove.begin();
|
||||
while (it != _widgetsToRemove.end()) {
|
||||
// loop over all widgets in the to-remove queue
|
||||
QDateTime t = it.value();
|
||||
NotificationWidget *widget = it.key();
|
||||
|
||||
if (currentTime > t) {
|
||||
// found one to remove!
|
||||
Activity::Identifier id = widget->activity().ident();
|
||||
_widgetForNotifId.remove(id);
|
||||
widget->deleteLater();
|
||||
it = _widgetsToRemove.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (_widgetsToRemove.isEmpty()) {
|
||||
_removeTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ==================================================================== */
|
||||
|
||||
ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
|
||||
@@ -611,14 +550,7 @@ ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
|
||||
|
||||
_activityWidget = new ActivityWidget(_accountState, this);
|
||||
|
||||
// set background white
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::Background, Qt::white);
|
||||
_activityWidget->setAutoFillBackground(true);
|
||||
_activityWidget->setPalette(palette);
|
||||
|
||||
_vbox->insertWidget(1, _activityWidget);
|
||||
connect(_activityWidget, &ActivityWidget::copyToClipboard, this, &ActivitySettings::slotCopyToClipboard);
|
||||
connect(_activityWidget, &ActivityWidget::guiLog, this, &ActivitySettings::guiLog);
|
||||
connect(&_notificationCheckTimer, &QTimer::timeout,
|
||||
this, &ActivitySettings::slotRegularNotificationCheck);
|
||||
@@ -641,21 +573,6 @@ void ActivitySettings::setNotificationRefreshInterval(std::chrono::milliseconds
|
||||
_notificationCheckTimer.start(interval.count());
|
||||
}
|
||||
|
||||
void ActivitySettings::slotCopyToClipboard()
|
||||
{
|
||||
QString text;
|
||||
QTextStream ts(&text);
|
||||
|
||||
QString message;
|
||||
|
||||
_activityWidget->storeActivityList(ts);
|
||||
message = tr("The server activity and notifications list has been copied to the clipboard.");
|
||||
|
||||
QApplication::clipboard()->setText(text);
|
||||
|
||||
emit guiLog(tr("Copied to clipboard"), message);
|
||||
}
|
||||
|
||||
void ActivitySettings::slotRemoveAccount()
|
||||
{
|
||||
_activityWidget->slotRemoveAccount();
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace OCC {
|
||||
class Account;
|
||||
class AccountStatusPtr;
|
||||
class JsonApiJob;
|
||||
class NotificationWidget;
|
||||
class ActivityListModel;
|
||||
|
||||
namespace Ui {
|
||||
@@ -68,7 +67,7 @@ public:
|
||||
* Based on whether activities are enabled and whether notifications are
|
||||
* available.
|
||||
*/
|
||||
void checkActivityTabVisibility();
|
||||
void checkActivityWidgetVisibility();
|
||||
|
||||
public slots:
|
||||
void slotOpenFile(QModelIndex indx);
|
||||
@@ -76,14 +75,12 @@ public slots:
|
||||
void slotRefreshNotifications();
|
||||
void slotRemoveAccount();
|
||||
void slotAccountActivityStatus(int statusCode);
|
||||
void slotRequestCleanupAndBlacklist(const Activity &blacklistActivity);
|
||||
void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
|
||||
void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
|
||||
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
||||
|
||||
signals:
|
||||
void guiLog(const QString &, const QString &);
|
||||
void copyToClipboard();
|
||||
void rowsInserted();
|
||||
void hideActivityTab(bool);
|
||||
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
|
||||
@@ -93,9 +90,7 @@ private slots:
|
||||
void slotSendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
|
||||
void slotNotifyNetworkError(QNetworkReply *);
|
||||
void slotNotifyServerFinished(const QString &reply, int replyCode);
|
||||
void endNotificationRequest(NotificationWidget *widget, int replyCode);
|
||||
void scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds = 100);
|
||||
void slotCheckToCleanWidgets();
|
||||
void endNotificationRequest(int replyCode);
|
||||
void slotNotificationRequestFinished(int statusCode);
|
||||
void slotPrimaryButtonClickedOnListView(const QModelIndex &index);
|
||||
void slotSecondaryButtonClickedOnListView(const QModelIndex &index);
|
||||
@@ -105,12 +100,10 @@ private:
|
||||
QString timeString(QDateTime dt, QLocale::FormatType format) const;
|
||||
Ui::ActivityWidget *_ui;
|
||||
QSet<QString> _accountsWithoutActivities;
|
||||
QMap<Activity::Identifier, NotificationWidget *> _widgetForNotifId;
|
||||
QElapsedTimer _guiLogTimer;
|
||||
QSet<int> _guiLoggedNotifications;
|
||||
ActivityList _blacklistedNotifications;
|
||||
|
||||
QHash<NotificationWidget *, QDateTime> _widgetsToRemove;
|
||||
QTimer _removeTimer;
|
||||
|
||||
// number of currently running notification requests. If non zero,
|
||||
@@ -146,7 +139,6 @@ public slots:
|
||||
void setNotificationRefreshInterval(std::chrono::milliseconds interval);
|
||||
|
||||
private slots:
|
||||
void slotCopyToClipboard();
|
||||
void slotRegularNotificationCheck();
|
||||
void slotDisplayActivities();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>693</width>
|
||||
<width>652</width>
|
||||
<height>556</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -77,8 +77,11 @@
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="_activityList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
@@ -92,18 +95,18 @@
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
@@ -112,18 +115,18 @@
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
@@ -132,18 +135,18 @@
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
@@ -151,13 +154,13 @@
|
||||
</palette>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
@@ -185,7 +188,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item>
|
||||
<widget class="QLabel" name="_bottomLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
@@ -201,28 +204,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QPushButton" name="_copyButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-left:40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <windows.h>
|
||||
#include "vfs_windows.h"
|
||||
#endif
|
||||
|
||||
#if defined(WITH_CRASHREPORTER)
|
||||
@@ -51,8 +52,11 @@
|
||||
#include <QTranslator>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
class QSocket;
|
||||
|
||||
namespace OCC {
|
||||
@@ -259,16 +263,35 @@ Application::~Application()
|
||||
|
||||
// Remove the account from the account manager so it can be deleted.
|
||||
AccountManager::instance()->shutdown();
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
VfsWindows::instance()->unmount();
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
VfsMacController::instance()->unmount();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::slotAccountStateRemoved(AccountState *accountState)
|
||||
{
|
||||
/*
|
||||
if (_cronDeleteOnlineFiles)
|
||||
{
|
||||
disconnect(_cronDeleteOnlineFiles, SIGNAL(timeout()), this, SLOT(slotDeleteOnlineFiles()));
|
||||
_cronDeleteOnlineFiles->stop();
|
||||
delete _cronDeleteOnlineFiles;
|
||||
_cronDeleteOnlineFiles = NULL;
|
||||
}
|
||||
*/
|
||||
|
||||
if (_gui) {
|
||||
disconnect(accountState, &AccountState::stateChanged,
|
||||
_gui.data(), &ownCloudGui::slotAccountStateChanged);
|
||||
disconnect(accountState->account().data(), &Account::serverVersionChanged,
|
||||
_gui.data(), &ownCloudGui::slotTrayMessageIfServerUnsupported);
|
||||
}
|
||||
|
||||
if (_folderManager) {
|
||||
disconnect(accountState, &AccountState::stateChanged,
|
||||
_folderManager.data(), &FolderMan::slotAccountStateChanged);
|
||||
@@ -296,6 +319,38 @@ void Application::slotAccountStateAdded(AccountState *accountState)
|
||||
_folderManager.data(), &FolderMan::slotServerVersionChanged);
|
||||
|
||||
_gui->slotTrayMessageIfServerUnsupported(accountState->account().data());
|
||||
|
||||
// Mount the virtual FileSystem.
|
||||
#if defined(Q_OS_MAC)
|
||||
QString rootPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/.cachedFiles";
|
||||
QString mountPath = "/Volumes/" + _theme->appName() + "fs";
|
||||
VfsMacController::instance()->initialize(rootPath, mountPath, accountState);
|
||||
VfsMacController::instance()->mount();
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QString rootPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cachedFiles/";
|
||||
WCHAR mountLetter = L'X';
|
||||
VfsWindows::instance()->initialize(rootPath, mountLetter, accountState);
|
||||
VfsWindows::instance()->mount();
|
||||
#endif
|
||||
|
||||
//< For cron delete dir/files online. Execute each 60000 msec
|
||||
|
||||
//< Uncomment for test "Clean local folder" case.
|
||||
/*_cronDeleteOnlineFiles = new QTimer(this);
|
||||
connect(_cronDeleteOnlineFiles, SIGNAL(timeout()), this, SLOT(slotDeleteOnlineFiles()));
|
||||
_cronDeleteOnlineFiles->start(60000);
|
||||
*/
|
||||
|
||||
/* See SocketApi::command_SET_DOWNLOAD_MODE
|
||||
//< Dummy example; Not uncomment
|
||||
SyncJournalDb::instance()->setSyncMode(QString("C:/Users/poncianoj/zd"), SyncJournalDb::SYNCMODE_OFFLINE);
|
||||
SyncJournalDb::instance()->setSyncMode(QString("C:/Users/poncianoj/zf.txt"), SyncJournalDb::SYNCMODE_ONLINE);
|
||||
|
||||
SyncJournalDb::instance()->updateLastAccess(QString("C:/Users/poncianoj/zd"));
|
||||
SyncJournalDb::instance()->updateLastAccess(QString("C:/Users/poncianoj/zf.txt"));
|
||||
*/
|
||||
}
|
||||
|
||||
void Application::slotCleanup()
|
||||
@@ -615,6 +670,119 @@ void Application::setupTranslations()
|
||||
}
|
||||
}
|
||||
|
||||
bool removeDirs(const QString & dirName)
|
||||
{
|
||||
bool result = true;
|
||||
QDir dir(dirName);
|
||||
|
||||
if (dir.exists(dirName)) {
|
||||
Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) {
|
||||
if (info.isDir()) {
|
||||
result = removeDirs(info.absoluteFilePath());
|
||||
}
|
||||
else {
|
||||
result = QFile::remove(info.absoluteFilePath());
|
||||
if (!result) {
|
||||
const QFile::Permissions permissions = QFile::permissions(info.absoluteFilePath());
|
||||
if (!(permissions & QFile::WriteUser)) {
|
||||
result = QFile::setPermissions(info.absoluteFilePath(), permissions | QFile::WriteUser) && QFile::remove(info.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Application::slotDeleteOnlineFiles()
|
||||
{
|
||||
|
||||
qDebug() << Q_FUNC_INFO << " clfCase 00:";
|
||||
|
||||
qDebug() << Q_FUNC_INFO << " 01: " << SyncJournalDb::instance()->databaseFilePath();
|
||||
|
||||
|
||||
//< Get paths SyncMode table.
|
||||
QList<QString> list = SyncJournalDb::instance()->getSyncModePaths();
|
||||
|
||||
if (!list.empty())
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " clfCase 02";
|
||||
|
||||
QString item;
|
||||
foreach(item, list)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " 03";
|
||||
qint64 m_secondsSinceLastAccess = SyncJournalDb::instance()->secondsSinceLastAccess(item);
|
||||
SyncJournalDb::SyncMode mode = SyncJournalDb::instance()->getSyncMode(item);
|
||||
|
||||
qDebug() << Q_FUNC_INFO << " 04";
|
||||
|
||||
SyncJournalDb::SyncModeDownload down = SyncJournalDb::instance()->getSyncModeDownload(item);
|
||||
|
||||
qDebug() << Q_FUNC_INFO << " 05";
|
||||
|
||||
if (mode == SyncJournalDb::SyncMode::SYNCMODE_ONLINE && down == SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_NONE)
|
||||
qDebug() << " clfCase item: " << item << " SYNCMODE_ONLINE - SYNCMODE_DOWNLOADED_NONE" << " secondsSinceLastAccess: " << m_secondsSinceLastAccess;
|
||||
else if (mode == SyncJournalDb::SyncMode::SYNCMODE_OFFLINE && down == SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_NONE)
|
||||
qDebug() << " clfCase item: " << item << " SYNCMODE_OFFLINE - SYNCMODE_DOWNLOADED_NONE" << " secondsSinceLastAccess: " << m_secondsSinceLastAccess;
|
||||
else if (mode == SyncJournalDb::SyncMode::SYNCMODE_ONLINE && down == SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_NO)
|
||||
qDebug() << " clfCase item: " << item << " SYNCMODE_ONLINE - SYNCMODE_DOWNLOADED_NO" << " secondsSinceLastAccess: " << m_secondsSinceLastAccess;
|
||||
else if (mode == SyncJournalDb::SyncMode::SYNCMODE_ONLINE && down == SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_YES)
|
||||
qDebug() << " clfCase item: " << item << " SYNCMODE_ONLINE - SYNCMODE_DOWNLOADED_YES" << " secondsSinceLastAccess: " << m_secondsSinceLastAccess;
|
||||
else if (mode == SyncJournalDb::SyncMode::SYNCMODE_OFFLINE && down == SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_NO)
|
||||
qDebug() << " clfCase item: " << item << " SYNCMODE_OFFLINE - SYNCMODE_DOWNLOADED_NO" << " secondsSinceLastAccess: " << m_secondsSinceLastAccess;
|
||||
else if (mode == SyncJournalDb::SyncMode::SYNCMODE_OFFLINE && down == SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_YES)
|
||||
qDebug() << " clfCase item: " << item << " SYNCMODE_OFFLINE - SYNCMODE_DOWNLOADED_YES" << " secondsSinceLastAccess: " << m_secondsSinceLastAccess;
|
||||
|
||||
//< After 10' and assumption SYNCMODE_ONLINE = Online, SYNCMODE_ALWAYS = Offline.
|
||||
if (m_secondsSinceLastAccess > 65 &&
|
||||
(mode == SyncJournalDb::SyncMode::SYNCMODE_ONLINE)
|
||||
)
|
||||
{
|
||||
QString relative_prefix;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
relative_prefix = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cachedFiles/";
|
||||
#elif defined(Q_OS_MAC)
|
||||
relative_prefix = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/cachedFiles/";
|
||||
#endif
|
||||
|
||||
QString realPathItem = relative_prefix.append(item);
|
||||
qDebug() << " clfCase Prepare to delete file or dir ..." << item;
|
||||
|
||||
QDir dir(realPathItem);
|
||||
//< if is dir
|
||||
if (dir.exists())
|
||||
{
|
||||
qDebug() << " clfCase remove dir ..." << realPathItem;
|
||||
removeDirs(realPathItem); //< Auxiliary function to remove folder contents
|
||||
SyncJournalDb::instance()->deleteFileRecord(item, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << " clfCase remove file ..." << realPathItem;
|
||||
|
||||
//< if is file
|
||||
QFile file(realPathItem);
|
||||
while (file.exists()) {
|
||||
QFile::remove(realPathItem); //< Remove
|
||||
QThread::msleep(100);
|
||||
}
|
||||
SyncJournalDb::instance()->deleteFileRecord(item, false);
|
||||
}
|
||||
SyncJournalDb::instance()->deleteSyncMode(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Application::giveHelp()
|
||||
{
|
||||
return _helpOnly;
|
||||
@@ -630,5 +798,4 @@ void Application::showSettingsDialog()
|
||||
_gui->slotShowSettings();
|
||||
}
|
||||
|
||||
|
||||
} // namespace OCC
|
||||
} // namespace OCC
|
||||
@@ -31,6 +31,9 @@
|
||||
#include "progressdispatcher.h"
|
||||
#include "clientproxy.h"
|
||||
#include "folderman.h"
|
||||
#if defined(Q_OS_MAC)
|
||||
#include "vfs_maccontroller.h"
|
||||
#endif
|
||||
|
||||
class QMessageBox;
|
||||
class QSystemTrayIcon;
|
||||
@@ -71,6 +74,7 @@ public:
|
||||
public slots:
|
||||
// TODO: this should not be public
|
||||
void slotownCloudWizardDone(int);
|
||||
void slotDeleteOnlineFiles();
|
||||
void slotCrash();
|
||||
|
||||
protected:
|
||||
@@ -97,6 +101,9 @@ private:
|
||||
QPointer<ownCloudGui> _gui;
|
||||
|
||||
Theme *_theme;
|
||||
#if defined(Q_OS_MAC)
|
||||
VfsMacController *cont;
|
||||
#endif
|
||||
|
||||
bool _helpOnly;
|
||||
bool _versionOnly;
|
||||
@@ -117,6 +124,7 @@ private:
|
||||
|
||||
QNetworkConfigurationManager _networkConfigurationManager;
|
||||
QTimer _checkConnectionTimer;
|
||||
QTimer *_cronDeleteOnlineFiles; //< Variable for cron delete online files task
|
||||
|
||||
#if defined(WITH_CRASHREPORTER)
|
||||
QScopedPointer<CrashReporter::Handler> _crashHandler;
|
||||
|
||||
@@ -24,6 +24,35 @@ namespace OCC {
|
||||
|
||||
Q_LOGGING_CATEGORY(lcWebFlowCredentials, "sync.credentials.webflow", QtInfoMsg)
|
||||
|
||||
class WebFlowCredentialsAccessManager : public AccessManager
|
||||
{
|
||||
public:
|
||||
WebFlowCredentialsAccessManager(const WebFlowCredentials *cred, QObject *parent = nullptr)
|
||||
: AccessManager(parent)
|
||||
, _cred(cred)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) Q_DECL_OVERRIDE
|
||||
{
|
||||
QNetworkRequest req(request);
|
||||
if (!req.attribute(HttpCredentials::DontAddCredentialsAttribute).toBool()) {
|
||||
if (_cred && !_cred->password().isEmpty()) {
|
||||
QByteArray credHash = QByteArray(_cred->user().toUtf8() + ":" + _cred->password().toUtf8()).toBase64();
|
||||
req.setRawHeader("Authorization", "Basic " + credHash);
|
||||
}
|
||||
}
|
||||
|
||||
return AccessManager::createRequest(op, req, outgoingData);
|
||||
}
|
||||
|
||||
private:
|
||||
// The credentials object dies along with the account, while the QNAM might
|
||||
// outlive both.
|
||||
QPointer<const WebFlowCredentials> _cred;
|
||||
};
|
||||
|
||||
WebFlowCredentials::WebFlowCredentials()
|
||||
: _ready(false),
|
||||
_credentialsValid(false)
|
||||
@@ -56,7 +85,7 @@ QString WebFlowCredentials::password() const {
|
||||
|
||||
QNetworkAccessManager *WebFlowCredentials::createQNAM() const {
|
||||
qCInfo(lcWebFlowCredentials()) << "Get QNAM";
|
||||
AccessManager *qnam = new AccessManager();
|
||||
AccessManager *qnam = new WebFlowCredentialsAccessManager(this);
|
||||
|
||||
connect(qnam, &AccessManager::authenticationRequired, this, &WebFlowCredentials::slotAuthentication);
|
||||
connect(qnam, &AccessManager::finished, this, &WebFlowCredentials::slotFinished);
|
||||
|
||||
61
src/gui/fileManager.h
Normal file
61
src/gui/fileManager.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef FILEMANAGER_H
|
||||
#define FILEMANAGER_H
|
||||
#include <QtCore>
|
||||
|
||||
class FileManager
|
||||
{
|
||||
public:
|
||||
static const QString FMFileType;
|
||||
static const QString FMFileTypeDirectory;
|
||||
static const QString FMFileTypeRegular;
|
||||
static const QString FMFileTypeSymbolicLink;
|
||||
static const QString FMFileTypeSocket;
|
||||
static const QString FMFileTypeCharacterSpecial;
|
||||
static const QString FMFileTypeBlockSpecial;
|
||||
static const QString FMFileTypeUnknown;
|
||||
static const QString FMFileSize;
|
||||
static const QString FMFileModificationDate;
|
||||
static const QString FMFileReferenceCount;
|
||||
static const QString FMFileDeviceIdentifier;
|
||||
static const QString FMFileOwnerAccountName;
|
||||
static const QString FMFileGroupOwnerAccountName;
|
||||
static const QString FMFilePosixPermissions;
|
||||
static const QString FMFileSystemNumber;
|
||||
static const QString FMFileSystemFileNumber;
|
||||
static const QString FMFileExtensionHidden;
|
||||
static const QString FMFileHFSCreatorCode;
|
||||
static const QString FMFileHFSTypeCode;
|
||||
static const QString FMFileImmutable;
|
||||
static const QString FMFileAppendOnly;
|
||||
static const QString FMFileCreationDate;
|
||||
static const QString FMFileOwnerAccountID;
|
||||
static const QString FMFileGroupOwnerAccountID;
|
||||
static const QString FMFileBusy;
|
||||
static const QString FMFileProtectionKey;
|
||||
static const QString FMFileProtectionNone;
|
||||
static const QString FMFileProtectionComplete;
|
||||
static const QString FMFileProtectionCompleteUnlessOpen;
|
||||
static const QString FMFileProtectionCompleteUntilFirstUserAuthentication;
|
||||
|
||||
static const QString FMFileSystemSize;
|
||||
static const QString FMFileSystemFreeSize;
|
||||
static const QString FMFileSystemNodes;
|
||||
static const QString FMFileSystemFreeNodes;
|
||||
|
||||
static const QString FMPOSIXErrorDomain;
|
||||
|
||||
FileManager(){}
|
||||
QVariantMap* attributesOfItemAtPath(QString path, QVariantMap &error);
|
||||
bool createDirectory(QString path, QVariantMap attributes, QVariantMap &errorcode);
|
||||
bool createFileAtPath(QString path, QVariantMap attributes, int flags, QVariant &userData, QVariantMap &error);
|
||||
bool createFileAtPath(QString path, QVariantMap attributes, QVariant &userData, QVariantMap &error);
|
||||
bool removeItemAtPath(QString path, QVariantMap &error);
|
||||
bool createSymbolicLinkAtPath(QString path, QString otherPath, QVariantMap &error);
|
||||
QString destinationOfSymbolicLinkAtPath(QString path, QVariantMap &error);
|
||||
QVariantMap attributesOfFileSystemForPath(QString path, QVariantMap &error);
|
||||
bool setAttributes(QVariantMap attributes, QString path, QVariantMap &error);
|
||||
QStringList contentsOfDirectoryAtPath (QString path, QVariantMap &error);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
465
src/gui/fileManager.mm
Normal file
465
src/gui/fileManager.mm
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Copyright (C) 2018 by AMCO
|
||||
* Copyright (C) 2018 by Jesús Deloya <jdeloya_ext@amco.mx>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import "fileManager.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
@interface ShavHelperManager : NSObject
|
||||
+ (NSDictionary*)variantToNSDictionary:(QVariantMap)settings;
|
||||
//+ (NSArray*)varianListToNSArray:(QVariantList)list;
|
||||
+ (id)variantToNSObject:(QVariant)item;
|
||||
+ (QVariant)NSObjectToVariant:(id)item;
|
||||
+ (QVariantMap)NSDictionaryToVariantMap:(NSDictionary*)obj;
|
||||
+ (QVariantList)NSArrayToVariantList:(NSArray*)array;
|
||||
//+ (QString)applicationBundle;
|
||||
+ (QVariantMap)NSErrorToVariantMap:(NSError*)error;
|
||||
/*+ (QSize)CGSizeToQSize:(CGSize)size;
|
||||
+ (CGSize)QSizeToCGSize:(QSize)size;
|
||||
+ (QPoint)CGPointToQPoint:(CGPoint)point;
|
||||
+ (CGPoint)QPointToCGPoint:(QPoint)point;
|
||||
+ (QRect)CGRectToQRect:(CGRect)frame;
|
||||
+ (CGRect)QRectToCGRect:(QRect)frame;*/
|
||||
@end
|
||||
|
||||
@implementation ShavHelperManager
|
||||
+ (NSDictionary*)variantToNSDictionary:(QVariantMap)settings {
|
||||
NSMutableDictionary* userInfoDic = nil;
|
||||
if(!settings.isEmpty()) {
|
||||
userInfoDic = [NSMutableDictionary dictionary];
|
||||
foreach(QString key, settings.keys()){
|
||||
QVariant item = settings[key];
|
||||
if(!item.isNull()) {
|
||||
if(item.type() == QVariant::String) {
|
||||
NSString* value = item.toString().toNSString();
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
} else if(item.type() == QVariant::Bool || item.type() == QVariant::Int || item.type() == QVariant::Double ||
|
||||
item.type() == QVariant::LongLong || item.type() == QVariant::UInt || item.type() == QVariant::ULongLong) {
|
||||
if(item.type() == QVariant::Int) {
|
||||
NSNumber* value = [NSNumber numberWithInt:item.toInt()];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
} else if(item.type() == QVariant::UInt) {
|
||||
NSNumber* value = [NSNumber numberWithUnsignedInt:item.toUInt()];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
} else if(item.type() == QVariant::LongLong) {
|
||||
NSNumber* value = [NSNumber numberWithLongLong:item.toLongLong()];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
} else if(item.type() == QVariant::ULongLong) {
|
||||
NSNumber* value = [NSNumber numberWithUnsignedLongLong:item.toULongLong()];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
} else if(item.type() == QVariant::Double) {
|
||||
NSNumber* value = [NSNumber numberWithDouble:item.toDouble()];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
} else if(item.type() == QVariant::Bool) {
|
||||
BOOL flag = ((item.toBool() == true) ? YES : NO);
|
||||
NSNumber* value = [NSNumber numberWithBool:flag];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
}
|
||||
} else if(item.type() == QVariant::Url) {
|
||||
QUrl url = item.toUrl();
|
||||
NSURL* urlPath = [NSURL URLWithString: (url.toString().toNSString())];
|
||||
[userInfoDic setObject: urlPath forKey: key.toNSString()];
|
||||
} else if(item.canConvert<QVariantMap>()) {
|
||||
NSDictionary* value = [ShavHelperManager variantToNSDictionary: (QVariantMap)item.toMap()];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
} else if(item.type() == QVariant::DateTime) {
|
||||
NSDate *date = item.toDateTime().toNSDate();
|
||||
if(date) {
|
||||
[userInfoDic setObject:date forKey:key.toNSString()];
|
||||
}
|
||||
}
|
||||
else if(item.canConvert<QVariantList>()) {
|
||||
NSArray* value = [ShavHelperManager varianListToNSArray: (QVariantList)item.toList()];
|
||||
if(value) {
|
||||
[userInfoDic setObject: value forKey: key.toNSString()];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return userInfoDic;
|
||||
}
|
||||
|
||||
+ (id)variantToNSObject:(QVariant)item {
|
||||
id res = nil;
|
||||
if(!item.isNull()) {
|
||||
if(item.type() == QVariant::String) {
|
||||
res = item.toString().toNSString();
|
||||
} else if(item.type() == QVariant::Bool || item.type() == QVariant::Int || item.type() == QVariant::Double ||
|
||||
item.type() == QVariant::LongLong || item.type() == QVariant::UInt || item.type() == QVariant::ULongLong) {
|
||||
if(item.type() == QVariant::Int) {
|
||||
res = [NSNumber numberWithInt:item.toInt()];
|
||||
} else if(item.type() == QVariant::UInt) {
|
||||
res = [NSNumber numberWithUnsignedInt:item.toUInt()];
|
||||
} else if(item.type() == QVariant::LongLong) {
|
||||
res = [NSNumber numberWithLongLong:item.toLongLong()];
|
||||
} else if(item.type() == QVariant::ULongLong) {
|
||||
res = [NSNumber numberWithUnsignedLongLong:item.toULongLong()];
|
||||
} else if(item.type() == QVariant::Double) {
|
||||
res = [NSNumber numberWithDouble:item.toDouble()];
|
||||
} else if(item.type() == QVariant::Bool) {
|
||||
BOOL flag = ((item.toBool() == true) ? YES : NO);
|
||||
res = [NSNumber numberWithBool:flag];
|
||||
}
|
||||
} else if(item.type() == QVariant::Url) {
|
||||
QUrl url = item.toUrl();
|
||||
res = [NSURL URLWithString: (url.toString().toNSString())];
|
||||
} else if(item.canConvert<QVariantMap>()) {
|
||||
res = [ShavHelperManager variantToNSDictionary: (QVariantMap)item.toMap()];
|
||||
} /*else if(item.canConvert<QVariantList>()) {
|
||||
res = [ShavHelperManager varianListToNSArray: (QVariantList)item.toList()];
|
||||
}*/
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
+ (QVariant)NSObjectToVariant:(id)item {
|
||||
QVariant res;
|
||||
res.clear();
|
||||
if(item) {
|
||||
if([item isKindOfClass: [NSDictionary class]]) {
|
||||
res = QVariant([ShavHelperManager NSDictionaryToVariantMap: (NSDictionary*)item]);
|
||||
} /*else if([item isKindOfClass: [NSArray class]]) {
|
||||
res = QVariant([ShavHelperManager NSArrayToVariantList: (NSArray*)item]);
|
||||
}*/ else if([item isKindOfClass: [NSNumber class]]) {
|
||||
if((NSNumber*)item == (void*)kCFBooleanFalse || (NSNumber*)item == (void*)kCFBooleanTrue) {
|
||||
BOOL value = [(NSNumber*)item boolValue];
|
||||
res = QVariant(((value == YES) ? true : false));
|
||||
} else if(strcmp([item objCType], @encode(int)) == 0) {
|
||||
int value = [(NSNumber*)item intValue];
|
||||
res = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(double)) == 0) {
|
||||
double value = [(NSNumber*)item doubleValue];
|
||||
res = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(float)) == 0) {
|
||||
float value = [(NSNumber*)item floatValue];
|
||||
res = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(uint)) == 0) {
|
||||
uint value = [(NSNumber*)item unsignedIntValue];
|
||||
res = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(long long)) == 0) {
|
||||
long long value = [(NSNumber*)item longLongValue];
|
||||
res = QVariant(value);
|
||||
}
|
||||
} else if([item isKindOfClass: [NSString class]]) {
|
||||
res = QVariant(QString::fromNSString((NSString*)item));
|
||||
} else if([item isKindOfClass:[NSURL class]]) {
|
||||
NSURL* url = (NSURL*)item;
|
||||
res = QVariant(QUrl(QString::fromNSString(url.absoluteString)));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
+ (QVariantMap)NSDictionaryToVariantMap:(NSDictionary*)obj {
|
||||
QVariantMap res;
|
||||
res.clear();
|
||||
if(obj && [obj isKindOfClass: [NSDictionary class]]) {
|
||||
for(NSEnumerator* key in [obj keyEnumerator]) {
|
||||
id item = [obj objectForKey: key];
|
||||
QString keyString = QString::fromNSString((NSString*)key);
|
||||
if([item isKindOfClass: [NSDictionary class]]) {
|
||||
res[keyString] = [ShavHelperManager NSDictionaryToVariantMap: (NSDictionary*)item];
|
||||
} /*else if([item isKindOfClass: [NSArray class]]) {
|
||||
res[keyString] = [ShavHelperManager NSArrayToVariantList: (NSArray*)item];
|
||||
}*/ else if([item isKindOfClass: [NSNumber class]]) {
|
||||
if((NSNumber*)item == (void*)kCFBooleanFalse || (NSNumber*)item == (void*)kCFBooleanTrue) {
|
||||
BOOL value = [(NSNumber*)item boolValue];
|
||||
res[keyString] = QVariant(((value == YES) ? true : false));
|
||||
} else if(strcmp([item objCType], @encode(int)) == 0) {
|
||||
int value = [(NSNumber*)item intValue];
|
||||
res[keyString] = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(double)) == 0) {
|
||||
double value = [(NSNumber*)item doubleValue];
|
||||
res[keyString] = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(float)) == 0) {
|
||||
float value = [(NSNumber*)item floatValue];
|
||||
res[keyString] = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(uint)) == 0) {
|
||||
uint value = [(NSNumber*)item unsignedIntValue];
|
||||
res[keyString] = QVariant(value);
|
||||
} else if(strcmp([item objCType], @encode(long long)) == 0) {
|
||||
long long value = [(NSNumber*)item longLongValue];
|
||||
res[keyString] = QVariant(value);
|
||||
}
|
||||
} else if([item isKindOfClass: [NSString class]]) {
|
||||
res[keyString] = QString::fromNSString((NSString*)item);
|
||||
} else if([item isKindOfClass:[NSURL class]]) {
|
||||
NSURL* url = (NSURL*)item;
|
||||
res[keyString] = QVariant(QUrl(QString::fromNSString(url.absoluteString)));
|
||||
} else if([item isKindOfClass:[NSDate class]]) {
|
||||
QDateTime date = QDateTime::fromNSDate((NSDate *)item);
|
||||
res[keyString] = date;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
+ (QVariantMap)NSErrorToVariantMap:(NSError*)error {
|
||||
QVariantMap map;
|
||||
map.clear();
|
||||
if(error) {
|
||||
map["code"] = (int)(error.code);
|
||||
map["domain"] = QString::fromNSString(error.domain);
|
||||
map["localizedDescription"] = QString::fromNSString(error.localizedDescription);
|
||||
map["localizedFailureReason"] = QString::fromNSString(error.localizedFailureReason);
|
||||
map["localizedRecoverySuggestion"] = QString::fromNSString(error.localizedRecoverySuggestion);
|
||||
if(error.userInfo != nil) {
|
||||
map["userInfo"] = [ShavHelperManager NSDictionaryToVariantMap: error.userInfo];
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
+ (QVariantList)NSArrayToVariantList:(NSArray*)array {
|
||||
QVariantList list;
|
||||
list.clear();
|
||||
if(array) {
|
||||
for(id item in array) {
|
||||
NSLog(@"%@",item);
|
||||
if([item isKindOfClass: [NSDictionary class]]) {
|
||||
list.append([ShavHelperManager NSDictionaryToVariantMap: (NSDictionary*)item]);
|
||||
} else if([item isKindOfClass: [NSArray class]]) {
|
||||
list.append([ShavHelperManager NSArrayToVariantList: (NSArray*)item]);
|
||||
} else if([item isKindOfClass: [NSNumber class]]) {
|
||||
if((NSNumber*)item == (void*)kCFBooleanFalse || (NSNumber*)item == (void*)kCFBooleanTrue) {
|
||||
BOOL value = [(NSNumber*)item boolValue];
|
||||
list.append(QVariant(((value == YES) ? true : false)));
|
||||
} else if(strcmp([item objCType], @encode(int)) == 0) {
|
||||
int value = [(NSNumber*)item intValue];
|
||||
list.append(QVariant(value));
|
||||
} else if(strcmp([item objCType], @encode(double)) == 0) {
|
||||
double value = [(NSNumber*)item doubleValue];
|
||||
list.append(QVariant(value));
|
||||
} else if(strcmp([item objCType], @encode(float)) == 0) {
|
||||
float value = [(NSNumber*)item floatValue];
|
||||
list.append(QVariant(value));
|
||||
} else if(strcmp([item objCType], @encode(uint)) == 0) {
|
||||
uint value = [(NSNumber*)item unsignedIntValue];
|
||||
list.append(QVariant(value));
|
||||
} else if(strcmp([item objCType], @encode(long long)) == 0) {
|
||||
long long value = [(NSNumber*)item longLongValue];
|
||||
list.append(QVariant(value));
|
||||
}
|
||||
} else if([item isKindOfClass: [NSString class]]) {
|
||||
list.append(QString::fromNSString((NSString*)item));
|
||||
} else if([item isKindOfClass:[NSURL class]]) {
|
||||
NSURL* url = (NSURL*)item;
|
||||
list.append(QVariant(QUrl(QString::fromNSString(url.absoluteString))));
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
const QString FileManager::FMFileType = QString::fromNSString(NSFileType);
|
||||
const QString FileManager::FMFileTypeDirectory = QString::fromNSString(NSFileTypeDirectory);
|
||||
const QString FileManager::FMFileTypeRegular =QString::fromNSString(NSFileTypeRegular);
|
||||
const QString FileManager::FMFileTypeSymbolicLink =QString::fromNSString(NSFileTypeSymbolicLink);
|
||||
const QString FileManager::FMFileTypeSocket =QString::fromNSString(NSFileTypeSocket);
|
||||
const QString FileManager::FMFileTypeCharacterSpecial =QString::fromNSString(NSFileTypeCharacterSpecial);
|
||||
const QString FileManager::FMFileTypeBlockSpecial =QString::fromNSString(NSFileTypeBlockSpecial);
|
||||
const QString FileManager::FMFileTypeUnknown =QString::fromNSString(NSFileTypeUnknown);
|
||||
const QString FileManager::FMFileSize =QString::fromNSString(NSFileSize);
|
||||
const QString FileManager::FMFileModificationDate =QString::fromNSString(NSFileModificationDate);
|
||||
const QString FileManager::FMFileReferenceCount =QString::fromNSString(NSFileReferenceCount);
|
||||
const QString FileManager::FMFileDeviceIdentifier =QString::fromNSString(NSFileDeviceIdentifier);
|
||||
const QString FileManager::FMFileOwnerAccountName =QString::fromNSString(NSFileOwnerAccountName);
|
||||
const QString FileManager::FMFileGroupOwnerAccountName =QString::fromNSString(NSFileGroupOwnerAccountName);
|
||||
const QString FileManager::FMFilePosixPermissions =QString::fromNSString(NSFilePosixPermissions);
|
||||
const QString FileManager::FMFileSystemNumber =QString::fromNSString(NSFileSystemNumber);
|
||||
const QString FileManager::FMFileSystemFileNumber =QString::fromNSString(NSFileSystemFileNumber);
|
||||
const QString FileManager::FMFileExtensionHidden =QString::fromNSString(NSFileExtensionHidden);
|
||||
const QString FileManager::FMFileHFSCreatorCode =QString::fromNSString(NSFileHFSCreatorCode);
|
||||
const QString FileManager::FMFileHFSTypeCode =QString::fromNSString(NSFileHFSTypeCode);
|
||||
const QString FileManager::FMFileImmutable =QString::fromNSString(NSFileImmutable);
|
||||
const QString FileManager::FMFileAppendOnly =QString::fromNSString(NSFileAppendOnly);
|
||||
const QString FileManager::FMFileCreationDate =QString::fromNSString(NSFileCreationDate);
|
||||
const QString FileManager::FMFileOwnerAccountID =QString::fromNSString(NSFileOwnerAccountID);
|
||||
const QString FileManager::FMFileGroupOwnerAccountID =QString::fromNSString(NSFileGroupOwnerAccountID);
|
||||
const QString FileManager::FMFileBusy =QString::fromNSString(NSFileBusy);
|
||||
|
||||
const QString FileManager::FMFileSystemSize =QString::fromNSString(NSFileSystemSize);
|
||||
const QString FileManager::FMFileSystemFreeSize =QString::fromNSString(NSFileSystemFreeSize);
|
||||
const QString FileManager::FMFileSystemNodes =QString::fromNSString(NSFileSystemNodes);
|
||||
const QString FileManager::FMFileSystemFreeNodes =QString::fromNSString(NSFileSystemFreeNodes);
|
||||
|
||||
const QString FileManager::FMPOSIXErrorDomain = QString::fromNSString(NSPOSIXErrorDomain);
|
||||
|
||||
QVariantMap* FileManager::attributesOfItemAtPath(QString path, QVariantMap &error)
|
||||
{
|
||||
NSString* p = path.toNSString();
|
||||
NSError *nsError = nil;
|
||||
QVariantMap* qAttribs = nullptr;
|
||||
NSDictionary* attribs =
|
||||
[[NSFileManager defaultManager] attributesOfItemAtPath:p error:&nsError];
|
||||
if(nsError)
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nsError];
|
||||
else
|
||||
qAttribs = new QVariantMap([ShavHelperManager NSDictionaryToVariantMap:attribs]);
|
||||
return qAttribs;
|
||||
}
|
||||
|
||||
bool FileManager::createDirectory(QString path, QVariantMap attributes, QVariantMap &error)
|
||||
{
|
||||
NSDictionary *attrib = [ShavHelperManager variantToNSDictionary:attributes];
|
||||
NSError *nsError = nil;
|
||||
bool retval = [[NSFileManager defaultManager] createDirectoryAtPath:path.toNSString()
|
||||
withIntermediateDirectories:NO
|
||||
attributes:attrib
|
||||
error:&nsError]?true:false;
|
||||
if(nsError)
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nsError];
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool FileManager::createFileAtPath(QString path, QVariantMap attributes, int flags, QVariant &userData, QVariantMap &error)
|
||||
{
|
||||
mode_t mode = attributes.value(FMFilePosixPermissions).toLongLong();
|
||||
int fd = open(path.toLatin1().data(), flags, mode);
|
||||
if ( fd < 0 ) {
|
||||
NSError *nserror = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
|
||||
if(nserror)
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nserror];
|
||||
return false;
|
||||
}
|
||||
|
||||
userData = fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileManager::createFileAtPath(QString path, QVariantMap attributes, QVariant &userData, QVariantMap &error)
|
||||
{
|
||||
return this->createFileAtPath(path, attributes, (O_RDWR | O_CREAT | O_EXCL), userData, error);
|
||||
}
|
||||
|
||||
bool FileManager::removeItemAtPath(QString path, QVariantMap &error)
|
||||
{
|
||||
// NOTE: If removeDirectoryAtPath is commented out, then this may be called
|
||||
// with a directory, in which case NSFileManager will recursively remove all
|
||||
// subdirectories. So be careful!
|
||||
NSError *nserror=nil;
|
||||
bool retval = [[NSFileManager defaultManager] removeItemAtPath:path.toNSString() error:&nserror]?true:false;
|
||||
if(nserror)
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nserror];
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool FileManager::createSymbolicLinkAtPath(QString path, QString otherPath, QVariantMap &error)
|
||||
{
|
||||
NSError *nserror=nil;
|
||||
bool retval = [[NSFileManager defaultManager] createSymbolicLinkAtPath:path.toNSString()
|
||||
withDestinationPath:otherPath.toNSString()
|
||||
error:&nserror];
|
||||
if(nserror)
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nserror];
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
QString FileManager::destinationOfSymbolicLinkAtPath(QString path, QVariantMap &error)
|
||||
{
|
||||
NSError *nserror=nil;
|
||||
NSString *retval = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:path.toNSString()
|
||||
error:&nserror];
|
||||
if(nserror)
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nserror];
|
||||
|
||||
return QString::fromNSString(retval);
|
||||
}
|
||||
|
||||
QVariantMap FileManager::attributesOfFileSystemForPath(QString path, QVariantMap &error)
|
||||
{
|
||||
NSError *nsError;
|
||||
QVariantMap qAttribs;
|
||||
NSDictionary* d =
|
||||
[[NSFileManager defaultManager] attributesOfFileSystemForPath:path.toNSString() error:&nsError];
|
||||
if (d) {
|
||||
NSMutableDictionary* attribs = [NSMutableDictionary dictionaryWithDictionary:d];
|
||||
[attribs setObject:[NSNumber numberWithBool:YES]
|
||||
forKey:@"kGMUserFileSystemVolumeSupportsExtendedDatesKey"];
|
||||
|
||||
NSURL *URL = [NSURL fileURLWithPath:path.toNSString() isDirectory:YES];
|
||||
NSNumber *supportsCaseSensitiveNames = nil;
|
||||
[URL getResourceValue:&supportsCaseSensitiveNames
|
||||
forKey:NSURLVolumeSupportsCaseSensitiveNamesKey
|
||||
error:NULL];
|
||||
if (supportsCaseSensitiveNames == nil) {
|
||||
supportsCaseSensitiveNames = [NSNumber numberWithBool:YES];
|
||||
}
|
||||
[attribs setObject:supportsCaseSensitiveNames
|
||||
forKey:@"kGMUserFileSystemVolumeSupportsCaseSensitiveNamesKey"];
|
||||
|
||||
qAttribs = [ShavHelperManager NSDictionaryToVariantMap:attribs];
|
||||
return qAttribs;
|
||||
}
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nsError];
|
||||
return qAttribs;
|
||||
}
|
||||
|
||||
bool FileManager::setAttributes(QVariantMap attributes, QString path, QVariantMap &error)
|
||||
{
|
||||
NSError *nserror = nil;
|
||||
NSDictionary *dict = [ShavHelperManager variantToNSDictionary:attributes];
|
||||
bool retval = [[NSFileManager defaultManager] setAttributes:dict
|
||||
ofItemAtPath:path.toNSString()
|
||||
error:&nserror]?true:false;
|
||||
if(nserror)
|
||||
{
|
||||
error = [ShavHelperManager NSErrorToVariantMap:nserror];
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
QStringList FileManager::contentsOfDirectoryAtPath (QString path, QVariantMap &error)
|
||||
{
|
||||
NSError *errorns = nil;
|
||||
NSString* p = path.toNSString();
|
||||
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:p error:&errorns];
|
||||
QVariantList retlist = [ShavHelperManager NSArrayToVariantList:contents];
|
||||
QStringList retval;
|
||||
foreach(QVariant value, retlist)
|
||||
retval.append(value.toString());
|
||||
if(errorns)
|
||||
error = [ShavHelperManager NSErrorToVariantMap:errorns];
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -93,7 +93,7 @@ Folder::Folder(const FolderDefinition &definition,
|
||||
this, &Folder::slotAboutToRemoveAllFiles);
|
||||
connect(_engine.data(), &SyncEngine::aboutToRestoreBackup,
|
||||
this, &Folder::slotAboutToRestoreBackup);
|
||||
connect(_engine.data(), &SyncEngine::transmissionProgress, this, &Folder::slotTransmissionProgress);
|
||||
connect(_engine.data(), &SyncEngine::transmissionProgress, this, &Folder::slotTransmissionProgress, Qt::DirectConnection);
|
||||
connect(_engine.data(), &SyncEngine::itemCompleted,
|
||||
this, &Folder::slotItemCompleted);
|
||||
connect(_engine.data(), &SyncEngine::newBigFolder,
|
||||
@@ -453,55 +453,55 @@ int Folder::slotWipeErrorBlacklist()
|
||||
return _journal.wipeErrorBlacklist();
|
||||
}
|
||||
|
||||
void Folder::slotWatchedPathChanged(const QString &path)
|
||||
{
|
||||
if (!path.startsWith(this->path())) {
|
||||
qCDebug(lcFolder) << "Changed path is not contained in folder, ignoring:" << path;
|
||||
return;
|
||||
}
|
||||
//void Folder::slotWatchedPathChanged(const QString &path)
|
||||
//{
|
||||
// if (!path.startsWith(this->path())) {
|
||||
// qCDebug(lcFolder) << "Changed path is not contained in folder, ignoring:" << path;
|
||||
// return;
|
||||
// }
|
||||
|
||||
auto relativePath = path.midRef(this->path().size());
|
||||
// auto relativePath = path.midRef(this->path().size());
|
||||
|
||||
// Add to list of locally modified paths
|
||||
//
|
||||
// We do this before checking for our own sync-related changes to make
|
||||
// extra sure to not miss relevant changes.
|
||||
auto relativePathBytes = relativePath.toUtf8();
|
||||
_localDiscoveryPaths.insert(relativePathBytes);
|
||||
qCDebug(lcFolder) << "local discovery: inserted" << relativePath << "due to file watcher";
|
||||
// // Add to list of locally modified paths
|
||||
// //
|
||||
// // We do this before checking for our own sync-related changes to make
|
||||
// // extra sure to not miss relevant changes.
|
||||
// auto relativePathBytes = relativePath.toUtf8();
|
||||
// _localDiscoveryPaths.insert(relativePathBytes);
|
||||
// qCDebug(lcFolder) << "local discovery: inserted" << relativePath << "due to file watcher";
|
||||
|
||||
// The folder watcher fires a lot of bogus notifications during
|
||||
// a sync operation, both for actual user files and the database
|
||||
// and log. Therefore we check notifications against operations
|
||||
// the sync is doing to filter out our own changes.
|
||||
#ifdef Q_OS_MAC
|
||||
// On OSX the folder watcher does not report changes done by our
|
||||
// own process. Therefore nothing needs to be done here!
|
||||
#else
|
||||
// Use the path to figure out whether it was our own change
|
||||
if (_engine->wasFileTouched(path)) {
|
||||
qCDebug(lcFolder) << "Changed path was touched by SyncEngine, ignoring:" << path;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
//// The folder watcher fires a lot of bogus notifications during
|
||||
//// a sync operation, both for actual user files and the database
|
||||
//// and log. Therefore we check notifications against operations
|
||||
//// the sync is doing to filter out our own changes.
|
||||
//#ifdef Q_OS_MAC
|
||||
//// On OSX the folder watcher does not report changes done by our
|
||||
//// own process. Therefore nothing needs to be done here!
|
||||
//#else
|
||||
// // Use the path to figure out whether it was our own change
|
||||
// if (_engine->wasFileTouched(path)) {
|
||||
// qCDebug(lcFolder) << "Changed path was touched by SyncEngine, ignoring:" << path;
|
||||
// return;
|
||||
// }
|
||||
//#endif
|
||||
|
||||
// Check that the mtime actually changed.
|
||||
SyncJournalFileRecord record;
|
||||
if (_journal.getFileRecord(relativePathBytes, &record)
|
||||
&& record.isValid()
|
||||
&& !FileSystem::fileChanged(path, record._fileSize, record._modtime)) {
|
||||
qCInfo(lcFolder) << "Ignoring spurious notification for file" << relativePath;
|
||||
return; // probably a spurious notification
|
||||
}
|
||||
// // Check that the mtime actually changed.
|
||||
// SyncJournalFileRecord record;
|
||||
// if (_journal.getFileRecord(relativePathBytes, &record)
|
||||
// && record.isValid()
|
||||
// && !FileSystem::fileChanged(path, record._fileSize, record._modtime)) {
|
||||
// qCInfo(lcFolder) << "Ignoring spurious notification for file" << relativePath;
|
||||
// return; // probably a spurious notification
|
||||
// }
|
||||
|
||||
warnOnNewExcludedItem(record, relativePath);
|
||||
// warnOnNewExcludedItem(record, relativePath);
|
||||
|
||||
emit watchedFileChangedExternally(path);
|
||||
// emit watchedFileChangedExternally(path);
|
||||
|
||||
// Also schedule this folder for a sync, but only after some delay:
|
||||
// The sync will not upload files that were changed too recently.
|
||||
scheduleThisFolderSoon();
|
||||
}
|
||||
// // Also schedule this folder for a sync, but only after some delay:
|
||||
// // The sync will not upload files that were changed too recently.
|
||||
// scheduleThisFolderSoon();
|
||||
//}
|
||||
|
||||
void Folder::saveToSettings() const
|
||||
{
|
||||
@@ -609,14 +609,26 @@ bool Folder::reloadExcludes()
|
||||
return _engine->excludedFiles().reloadExcludeFiles();
|
||||
}
|
||||
|
||||
void Folder::startSync(const QStringList &pathList)
|
||||
{
|
||||
Q_UNUSED(pathList)
|
||||
void Folder::updateLocalFileTree(const QString &path, csync_instructions_e instruction){
|
||||
_engine->updateLocalFileTree(path, instruction);
|
||||
}
|
||||
|
||||
void Folder::updateFuseCreatedFile(const QString &path, bool is_fuse_created_file){
|
||||
_engine->updateFuseCreatedFile(path, is_fuse_created_file);
|
||||
}
|
||||
|
||||
void Folder::startSync()
|
||||
{
|
||||
if (isBusy()) {
|
||||
qCCritical(lcFolder) << "ERROR csync is still running and new sync requested.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (_engine->localTreeSize() == 0) {
|
||||
qCCritical(lcFolder) << "Nothing to do yet...";
|
||||
return;
|
||||
}
|
||||
|
||||
_csyncUnavail = false;
|
||||
|
||||
_timeSinceLastSyncStart.start();
|
||||
@@ -637,38 +649,48 @@ void Folder::startSync(const QStringList &pathList)
|
||||
setDirtyNetworkLimits();
|
||||
setSyncOptions();
|
||||
|
||||
static std::chrono::milliseconds fullLocalDiscoveryInterval = []() {
|
||||
auto interval = ConfigFile().fullLocalDiscoveryInterval();
|
||||
QByteArray env = qgetenv("OWNCLOUD_FULL_LOCAL_DISCOVERY_INTERVAL");
|
||||
if (!env.isEmpty()) {
|
||||
interval = std::chrono::milliseconds(env.toLongLong());
|
||||
}
|
||||
return interval;
|
||||
}();
|
||||
if (_folderWatcher && _folderWatcher->isReliable() && _timeSinceLastFullLocalDiscovery.isValid()
|
||||
&& (fullLocalDiscoveryInterval.count() < 0
|
||||
|| _timeSinceLastFullLocalDiscovery.hasExpired(fullLocalDiscoveryInterval.count()))) {
|
||||
qCInfo(lcFolder) << "Allowing local discovery to read from the database";
|
||||
_engine->setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, _localDiscoveryPaths);
|
||||
// static std::chrono::milliseconds fullLocalDiscoveryInterval = []() {
|
||||
// auto interval = ConfigFile().fullLocalDiscoveryInterval();
|
||||
// QByteArray env = qgetenv("OWNCLOUD_FULL_LOCAL_DISCOVERY_INTERVAL");
|
||||
// if (!env.isEmpty()) {
|
||||
// interval = std::chrono::milliseconds(env.toLongLong());
|
||||
// }
|
||||
// return interval;
|
||||
// }();
|
||||
|
||||
if (lcFolder().isDebugEnabled()) {
|
||||
QByteArrayList paths;
|
||||
for (auto &path : _localDiscoveryPaths)
|
||||
paths.append(path);
|
||||
qCDebug(lcFolder) << "local discovery paths: " << paths;
|
||||
}
|
||||
// if (_folderWatcher && _folderWatcher->isReliable() && _timeSinceLastFullLocalDiscovery.isValid()
|
||||
// && (fullLocalDiscoveryInterval.count() < 0
|
||||
// || _timeSinceLastFullLocalDiscovery.hasExpired(fullLocalDiscoveryInterval.count()))) {
|
||||
|
||||
_previousLocalDiscoveryPaths = std::move(_localDiscoveryPaths);
|
||||
// qCInfo(lcFolder) << "Allowing local discovery to read from the database";
|
||||
// _engine->setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem);
|
||||
|
||||
// if (lcFolder().isDebugEnabled()) {
|
||||
// QByteArrayList paths;
|
||||
// for (auto &path : _localDiscoveryPaths)
|
||||
// paths.append(path);
|
||||
// qCDebug(lcFolder) << "local discovery paths: " << paths;
|
||||
// }
|
||||
|
||||
// _previousLocalDiscoveryPaths = std::move(_localDiscoveryPaths);
|
||||
// } else {
|
||||
// qCInfo(lcFolder) << "Forbidding local discovery to read from the database";
|
||||
// _engine->setLocalDiscoveryOptions(LocalDiscoveryStyle::FilesystemOnly);
|
||||
// _previousLocalDiscoveryPaths.clear();
|
||||
// }
|
||||
|
||||
_engine->setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem);
|
||||
if (_engine->localTreeSize() > 0){
|
||||
_engine->setLocalDiscoveryOptions(LocalDiscoveryStyle::FUSEFilesystem);
|
||||
} else {
|
||||
qCInfo(lcFolder) << "Forbidding local discovery to read from the database";
|
||||
_engine->setLocalDiscoveryOptions(LocalDiscoveryStyle::FilesystemOnly);
|
||||
_previousLocalDiscoveryPaths.clear();
|
||||
}
|
||||
_localDiscoveryPaths.clear();
|
||||
|
||||
//_localDiscoveryPaths.clear();
|
||||
|
||||
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
|
||||
|
||||
QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::DirectConnection);
|
||||
|
||||
emit syncStarted();
|
||||
}
|
||||
@@ -809,19 +831,23 @@ void Folder::slotSyncFinished(bool success)
|
||||
if ((_syncResult.status() == SyncResult::Success
|
||||
|| _syncResult.status() == SyncResult::Problem)
|
||||
&& success) {
|
||||
if (_engine->lastLocalDiscoveryStyle() == LocalDiscoveryStyle::FilesystemOnly) {
|
||||
_timeSinceLastFullLocalDiscovery.start();
|
||||
}
|
||||
// if (_engine->lastLocalDiscoveryStyle() == LocalDiscoveryStyle::FilesystemOnly) {
|
||||
// _timeSinceLastFullLocalDiscovery.start();
|
||||
// }
|
||||
|
||||
qCDebug(lcFolder) << "Sync success, forgetting last sync's local discovery path list";
|
||||
} else {
|
||||
// On overall-failure we can't forget about last sync's local discovery
|
||||
// paths yet, reuse them for the next sync again.
|
||||
// C++17: Could use std::set::merge().
|
||||
_localDiscoveryPaths.insert(
|
||||
_previousLocalDiscoveryPaths.begin(), _previousLocalDiscoveryPaths.end());
|
||||
// _localDiscoveryPaths.insert(
|
||||
// _previousLocalDiscoveryPaths.begin(), _previousLocalDiscoveryPaths.end());
|
||||
|
||||
//_engine->updateLocalFileTree() ??
|
||||
|
||||
qCDebug(lcFolder) << "Sync failed, keeping last sync's local discovery path list";
|
||||
}
|
||||
_previousLocalDiscoveryPaths.clear();
|
||||
//_previousLocalDiscoveryPaths.clear();
|
||||
|
||||
emit syncStateChange();
|
||||
|
||||
@@ -888,12 +914,14 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item)
|
||||
|
||||
// add new directories or remove gone away dirs to the watcher
|
||||
if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_NEW) {
|
||||
if (_folderWatcher)
|
||||
_folderWatcher->addPath(path() + item->_file);
|
||||
// if (_folderWatcher)
|
||||
// _folderWatcher->addPath(path() + item->_file);
|
||||
//updateLocalFileTree(item->_file, item->_instruction);
|
||||
}
|
||||
if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
|
||||
if (_folderWatcher)
|
||||
_folderWatcher->removePath(path() + item->_file);
|
||||
// if (_folderWatcher)
|
||||
// _folderWatcher->removePath(path() + item->_file);
|
||||
//updateLocalFileTree(item->_file, item->_instruction);
|
||||
}
|
||||
|
||||
// Success and failure of sync items adjust what the next sync is
|
||||
@@ -908,14 +936,30 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item)
|
||||
|| item->_status == SyncFileItem::FileIgnored
|
||||
|| item->_status == SyncFileItem::Restoration
|
||||
|| item->_status == SyncFileItem::Conflict) {
|
||||
if (_previousLocalDiscoveryPaths.erase(item->_file.toUtf8()))
|
||||
qCDebug(lcFolder) << "local discovery: wiped" << item->_file;
|
||||
// if (_previousLocalDiscoveryPaths.erase(item->_file.toUtf8()))
|
||||
// qCDebug(lcFolder) << "local discovery: wiped" << item->_file;
|
||||
|
||||
if (item->_status != SyncFileItem::FileIgnored) {
|
||||
_journal.setSyncModeDownload(item->_file, SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_YES);
|
||||
_journal.updateLastAccess(item->_file);
|
||||
qCDebug(lcFolder) << "Sync successed for file: " << item->_file;
|
||||
} else {
|
||||
_journal.setSyncModeDownload(item->_file, SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_NO);
|
||||
qCDebug(lcFolder) << "file IGNORED because is ONLINE: " << item->_file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
_localDiscoveryPaths.insert(item->_file.toUtf8());
|
||||
qCDebug(lcFolder) << "local discovery: inserted" << item->_file << "due to sync failure";
|
||||
//_localDiscoveryPaths.insert(item->_file.toUtf8());
|
||||
//updateLocalFileTree(item->_file, item->_instruction);
|
||||
|
||||
_journal.setSyncModeDownload(item->_file, SyncJournalDb::SyncModeDownload::SYNCMODE_DOWNLOADED_NO);
|
||||
qCDebug(lcFolder) << "Sync failure for file: " << item->_file;
|
||||
}
|
||||
|
||||
_syncResult.processCompletedItem(item);
|
||||
// notify this file 'got' synced no matter the result
|
||||
_journal.emitSyncStatusChanged();
|
||||
|
||||
_fileLog->logItem(*item);
|
||||
emit ProgressDispatcher::instance()->itemCompleted(alias(), item);
|
||||
@@ -964,13 +1008,13 @@ void Folder::slotLogPropagationStart()
|
||||
|
||||
void Folder::slotScheduleThisFolder()
|
||||
{
|
||||
FolderMan::instance()->scheduleFolder(this);
|
||||
FolderMan::instance()->scheduleFolder();
|
||||
}
|
||||
|
||||
void Folder::slotNextSyncFullLocalDiscovery()
|
||||
{
|
||||
_timeSinceLastFullLocalDiscovery.invalidate();
|
||||
}
|
||||
//void Folder::slotNextSyncFullLocalDiscovery()
|
||||
//{
|
||||
// _timeSinceLastFullLocalDiscovery.invalidate();
|
||||
//}
|
||||
|
||||
void Folder::slotFolderConflicts(const QString &folder, const QStringList &conflictPaths)
|
||||
{
|
||||
@@ -1027,19 +1071,19 @@ void Folder::setSaveBackwardsCompatible(bool save)
|
||||
_saveBackwardsCompatible = save;
|
||||
}
|
||||
|
||||
void Folder::registerFolderWatcher()
|
||||
{
|
||||
if (_folderWatcher)
|
||||
return;
|
||||
if (!QDir(path()).exists())
|
||||
return;
|
||||
//void Folder::registerFolderWatcher()
|
||||
//{
|
||||
// if (_folderWatcher)
|
||||
// return;
|
||||
// if (!QDir(path()).exists())
|
||||
// return;
|
||||
|
||||
_folderWatcher.reset(new FolderWatcher(path(), this));
|
||||
connect(_folderWatcher.data(), &FolderWatcher::pathChanged,
|
||||
this, &Folder::slotWatchedPathChanged);
|
||||
connect(_folderWatcher.data(), &FolderWatcher::lostChanges,
|
||||
this, &Folder::slotNextSyncFullLocalDiscovery);
|
||||
}
|
||||
// _folderWatcher.reset(new FolderWatcher(path(), this));
|
||||
// connect(_folderWatcher.data(), &FolderWatcher::pathChanged,
|
||||
// this, &Folder::slotWatchedPathChanged);
|
||||
// connect(_folderWatcher.data(), &FolderWatcher::lostChanges,
|
||||
// this, &Folder::slotNextSyncFullLocalDiscovery);
|
||||
//}
|
||||
|
||||
void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, bool *cancel)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QUuid>
|
||||
#include <set>
|
||||
#include <chrono>
|
||||
#include <QThread>
|
||||
|
||||
class QThread;
|
||||
class QSettings;
|
||||
@@ -231,12 +232,12 @@ public:
|
||||
*/
|
||||
void setSaveBackwardsCompatible(bool save);
|
||||
|
||||
/**
|
||||
* Sets up this folder's folderWatcher if possible.
|
||||
*
|
||||
* May be called several times.
|
||||
*/
|
||||
void registerFolderWatcher();
|
||||
// /**
|
||||
// * Sets up this folder's folderWatcher if possible.
|
||||
// *
|
||||
// * May be called several times.
|
||||
// */
|
||||
// void registerFolderWatcher();
|
||||
|
||||
signals:
|
||||
void syncStateChange();
|
||||
@@ -266,11 +267,19 @@ public slots:
|
||||
|
||||
|
||||
/**
|
||||
* Starts a sync operation
|
||||
* Starts a sync operation using _localDiscoveryPaths
|
||||
*
|
||||
* If the list of changed files is known, it is passed.
|
||||
*/
|
||||
void startSync(const QStringList &pathList = QStringList());
|
||||
void startSync();
|
||||
|
||||
/**
|
||||
* Set local files and actions when it is known - triggered by FUSE operations
|
||||
*
|
||||
*/
|
||||
void updateLocalFileTree(const QString &path, csync_instructions_e instruction);
|
||||
|
||||
void updateFuseCreatedFile(const QString & path, bool is_fuse_created_files);
|
||||
|
||||
int slotDiscardDownloadProgress();
|
||||
int downloadInfoCount();
|
||||
@@ -282,7 +291,7 @@ public slots:
|
||||
* changes. Needs to check whether this change should trigger a new
|
||||
* sync run to be scheduled.
|
||||
*/
|
||||
void slotWatchedPathChanged(const QString &path);
|
||||
//void slotWatchedPathChanged(const QString &path);
|
||||
|
||||
private slots:
|
||||
void slotSyncStarted();
|
||||
@@ -313,7 +322,7 @@ private slots:
|
||||
void slotScheduleThisFolder();
|
||||
|
||||
/** Ensures that the next sync performs a full local discovery. */
|
||||
void slotNextSyncFullLocalDiscovery();
|
||||
//void slotNextSyncFullLocalDiscovery();
|
||||
|
||||
/** Adjust sync result based on conflict data from IssuesWidget.
|
||||
*
|
||||
@@ -358,7 +367,7 @@ private:
|
||||
QString _lastEtag;
|
||||
QElapsedTimer _timeSinceLastSyncDone;
|
||||
QElapsedTimer _timeSinceLastSyncStart;
|
||||
QElapsedTimer _timeSinceLastFullLocalDiscovery;
|
||||
//QElapsedTimer _timeSinceLastFullLocalDiscovery;
|
||||
std::chrono::milliseconds _lastSyncDuration;
|
||||
|
||||
/// The number of syncs that failed in a row.
|
||||
@@ -385,20 +394,25 @@ private:
|
||||
*/
|
||||
bool _saveBackwardsCompatible;
|
||||
|
||||
/**
|
||||
* Watches this folder's local directory for changes.
|
||||
*
|
||||
* Created by registerFolderWatcher(), triggers slotWatchedPathChanged()
|
||||
*/
|
||||
QScopedPointer<FolderWatcher> _folderWatcher;
|
||||
// /**
|
||||
// * Watches this folder's local directory for changes.
|
||||
// *
|
||||
// * Created by registerFolderWatcher(), triggers slotWatchedPathChanged()
|
||||
// */
|
||||
// QScopedPointer<FolderWatcher> _folderWatcher;
|
||||
|
||||
// /**
|
||||
// * The paths that should be checked by the next local discovery.
|
||||
// *
|
||||
// * Mostly a collection of files the filewatchers have reported as touched.
|
||||
// * Also includes files that have had errors in the last sync run.
|
||||
// */
|
||||
// std::set<QByteArray> _localDiscoveryPaths;
|
||||
|
||||
/**
|
||||
* The paths that should be checked by the next local discovery.
|
||||
*
|
||||
* Mostly a collection of files the filewatchers have reported as touched.
|
||||
* Also includes files that have had errors in the last sync run.
|
||||
* The known local paths and instructions that should be checked by the next local discovery.
|
||||
*/
|
||||
std::set<QByteArray> _localDiscoveryPaths;
|
||||
//std::map<QByteArray, csync_instructions_e> _fuseDiscoveryPaths;
|
||||
|
||||
/**
|
||||
* The paths that the current sync run used for local discovery.
|
||||
@@ -406,7 +420,7 @@ private:
|
||||
* For failing syncs, this list will be merged into _localDiscoveryPaths
|
||||
* again when the sync is done to make sure everything is retried.
|
||||
*/
|
||||
std::set<QByteArray> _previousLocalDiscoveryPaths;
|
||||
//std::set<QByteArray> _previousLocalDiscoveryPaths;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -98,28 +98,28 @@ OCC::Folder::Map FolderMan::map()
|
||||
return _folderMap;
|
||||
}
|
||||
|
||||
void FolderMan::unloadFolder(Folder *f)
|
||||
void FolderMan::unloadFolder()
|
||||
{
|
||||
if (!f) {
|
||||
if (!_currentSyncFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
_socketApi->slotUnregisterPath(f->alias());
|
||||
_socketApi->slotUnregisterPath(_currentSyncFolder->alias());
|
||||
|
||||
_folderMap.remove(f->alias());
|
||||
_folderMap.remove(_currentSyncFolder->alias());
|
||||
|
||||
disconnect(f, &Folder::syncStarted,
|
||||
disconnect(_currentSyncFolder, &Folder::syncStarted,
|
||||
this, &FolderMan::slotFolderSyncStarted);
|
||||
disconnect(f, &Folder::syncFinished,
|
||||
disconnect(_currentSyncFolder, &Folder::syncFinished,
|
||||
this, &FolderMan::slotFolderSyncFinished);
|
||||
disconnect(f, &Folder::syncStateChange,
|
||||
disconnect(_currentSyncFolder, &Folder::syncStateChange,
|
||||
this, &FolderMan::slotForwardFolderSyncStateChange);
|
||||
disconnect(f, &Folder::syncPausedChanged,
|
||||
disconnect(_currentSyncFolder, &Folder::syncPausedChanged,
|
||||
this, &FolderMan::slotFolderSyncPaused);
|
||||
disconnect(&f->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::fileStatusChanged,
|
||||
disconnect(&_currentSyncFolder->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::fileStatusChanged,
|
||||
_socketApi.data(), &SocketApi::broadcastStatusPushMessage);
|
||||
disconnect(f, &Folder::watchedFileChangedExternally,
|
||||
&f->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::slotPathTouched);
|
||||
disconnect(_currentSyncFolder, &Folder::watchedFileChangedExternally,
|
||||
&_currentSyncFolder->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::slotPathTouched);
|
||||
}
|
||||
|
||||
int FolderMan::unloadAndDeleteAllFolders()
|
||||
@@ -131,31 +131,30 @@ int FolderMan::unloadAndDeleteAllFolders()
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
Folder *f = i.value();
|
||||
unloadFolder(f);
|
||||
unloadFolder();
|
||||
delete f;
|
||||
cnt++;
|
||||
}
|
||||
ASSERT(_folderMap.isEmpty());
|
||||
|
||||
_lastSyncFolder = 0;
|
||||
_currentSyncFolder = 0;
|
||||
_scheduledFolders.clear();
|
||||
emit folderListChanged(_folderMap);
|
||||
emit scheduleQueueChanged();
|
||||
|
||||
return cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FolderMan::registerFolderWithSocketApi(Folder *folder)
|
||||
void FolderMan::registerFolderWithSocketApi()
|
||||
{
|
||||
if (!folder)
|
||||
if (!_currentSyncFolder)
|
||||
return;
|
||||
if (!QDir(folder->path()).exists())
|
||||
if (!QDir(_currentSyncFolder->path()).exists())
|
||||
return;
|
||||
|
||||
// register the folder with the socket API
|
||||
if (folder->canSync())
|
||||
_socketApi->slotRegisterPath(folder->alias());
|
||||
if (_currentSyncFolder->canSync())
|
||||
_socketApi->slotRegisterPath(_currentSyncFolder->alias());
|
||||
}
|
||||
|
||||
int FolderMan::setupFolders()
|
||||
@@ -224,14 +223,14 @@ void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account,
|
||||
SyncJournalDb::maybeMigrateDb(folderDefinition.localPath, folderDefinition.absoluteJournalPath());
|
||||
}
|
||||
|
||||
Folder *f = addFolderInternal(std::move(folderDefinition), account.data());
|
||||
if (f) {
|
||||
_currentSyncFolder = addFolderInternal(std::move(folderDefinition), account.data());
|
||||
if (_currentSyncFolder) {
|
||||
// Migration: Mark folders that shall be saved in a backwards-compatible way
|
||||
if (backwardsCompatible) {
|
||||
f->setSaveBackwardsCompatible(true);
|
||||
_currentSyncFolder->setSaveBackwardsCompatible(true);
|
||||
}
|
||||
scheduleFolder(f);
|
||||
emit folderSyncStateChange(f);
|
||||
scheduleFolder();
|
||||
emit folderSyncStateChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,10 +252,10 @@ int FolderMan::setupFoldersMigration()
|
||||
// Normally there should be only one account when migrating.
|
||||
AccountState *accountState = AccountManager::instance()->accounts().value(0).data();
|
||||
foreach (const QString &alias, list) {
|
||||
Folder *f = setupFolderFromOldConfigFile(alias, accountState);
|
||||
if (f) {
|
||||
scheduleFolder(f);
|
||||
emit folderSyncStateChange(f);
|
||||
_currentSyncFolder = setupFolderFromOldConfigFile(alias, accountState);
|
||||
if (_currentSyncFolder) {
|
||||
scheduleFolder();
|
||||
emit folderSyncStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,8 +345,6 @@ QString FolderMan::unescapeAlias(const QString &alias)
|
||||
// WARNING: Do not remove this code, it is used for predefined/automated deployments (2016)
|
||||
Folder *FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountState *accountState)
|
||||
{
|
||||
Folder *folder = 0;
|
||||
|
||||
qCInfo(lcFolderMan) << " ` -> setting up:" << file;
|
||||
QString escapedAlias(file);
|
||||
// check the unescaped variant (for the case when the filename comes out
|
||||
@@ -362,7 +359,7 @@ Folder *FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
|
||||
}
|
||||
if (!cfgFile.isReadable()) {
|
||||
qCWarning(lcFolderMan) << "Cannot read folder definition for alias " << cfgFile.filePath();
|
||||
return folder;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QSettings settings(_folderConfigPath + QLatin1Char('/') + escapedAlias, QSettings::IniFormat);
|
||||
@@ -407,47 +404,45 @@ Folder *FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
|
||||
folderDefinition.paused = paused;
|
||||
folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles();
|
||||
|
||||
folder = addFolderInternal(folderDefinition, accountState);
|
||||
if (folder) {
|
||||
_currentSyncFolder = addFolderInternal(folderDefinition, accountState);
|
||||
if (_currentSyncFolder) {
|
||||
QStringList blackList = settings.value(QLatin1String("blackList")).toStringList();
|
||||
if (!blackList.empty()) {
|
||||
//migrate settings
|
||||
folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
|
||||
_currentSyncFolder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
|
||||
settings.remove(QLatin1String("blackList"));
|
||||
// FIXME: If you remove this codepath, you need to provide another way to do
|
||||
// this via theme.h or the normal FolderMan::setupFolders
|
||||
}
|
||||
|
||||
folder->saveToSettings();
|
||||
_currentSyncFolder->saveToSettings();
|
||||
}
|
||||
qCInfo(lcFolderMan) << "Migrated!" << folder;
|
||||
qCInfo(lcFolderMan) << "Migrated!" << _currentSyncFolder;
|
||||
settings.sync();
|
||||
return folder;
|
||||
return _currentSyncFolder;
|
||||
}
|
||||
|
||||
void FolderMan::slotFolderSyncPaused(Folder *f, bool paused)
|
||||
void FolderMan::slotFolderSyncPaused(bool paused)
|
||||
{
|
||||
if (!f) {
|
||||
if (!_currentSyncFolder) {
|
||||
qCCritical(lcFolderMan) << "slotFolderSyncPaused called with empty folder";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
_disabledFolders.remove(f);
|
||||
scheduleFolder(f);
|
||||
} else {
|
||||
_disabledFolders.insert(f);
|
||||
if (!_currentSyncFolder->syncPaused()) {
|
||||
scheduleFolder();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMan::slotFolderCanSyncChanged()
|
||||
{
|
||||
Folder *f = qobject_cast<Folder *>(sender());
|
||||
ASSERT(f);
|
||||
if (f->canSync()) {
|
||||
_socketApi->slotRegisterPath(f->alias());
|
||||
//TODO FUSE
|
||||
//Folder *f = qobject_cast<Folder *>(sender());
|
||||
ASSERT(_currentSyncFolder);
|
||||
if (_currentSyncFolder->canSync()) {
|
||||
_socketApi->slotRegisterPath(_currentSyncFolder->alias());
|
||||
} else {
|
||||
_socketApi->slotUnregisterPath(f->alias());
|
||||
_socketApi->slotUnregisterPath(_currentSyncFolder->alias());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,11 +451,10 @@ void FolderMan::slotFolderCanSyncChanged()
|
||||
// csync still remains in a stable state, regardless of that.
|
||||
void FolderMan::terminateSyncProcess()
|
||||
{
|
||||
Folder *f = _currentSyncFolder;
|
||||
if (f) {
|
||||
if (_currentSyncFolder) {
|
||||
// This will, indirectly and eventually, call slotFolderSyncFinished
|
||||
// and thereby clear _currentSyncFolder.
|
||||
f->slotTerminateSync();
|
||||
_currentSyncFolder->slotTerminateSync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,10 +470,8 @@ Folder *FolderMan::folder(const QString &alias)
|
||||
|
||||
void FolderMan::scheduleAllFolders()
|
||||
{
|
||||
foreach (Folder *f, _folderMap.values()) {
|
||||
if (f && f->canSync()) {
|
||||
scheduleFolder(f);
|
||||
}
|
||||
if (_currentSyncFolder && _currentSyncFolder->canSync()) {
|
||||
scheduleFolder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,25 +490,25 @@ void FolderMan::slotSyncOnceFileUnlocks(const QString &path)
|
||||
* if a folder wants to be synced, it calls this slot and is added
|
||||
* to the queue. The slot to actually start a sync is called afterwards.
|
||||
*/
|
||||
void FolderMan::scheduleFolder(Folder *f)
|
||||
void FolderMan::scheduleFolder()
|
||||
{
|
||||
if (!f) {
|
||||
if (!_currentSyncFolder) {
|
||||
qCCritical(lcFolderMan) << "slotScheduleSync called with null folder";
|
||||
return;
|
||||
}
|
||||
auto alias = f->alias();
|
||||
auto alias = _currentSyncFolder->alias();
|
||||
|
||||
qCInfo(lcFolderMan) << "Schedule folder " << alias << " to sync!";
|
||||
|
||||
if (!_scheduledFolders.contains(f)) {
|
||||
if (!f->canSync()) {
|
||||
if (!_scheduledFolders.contains(_currentSyncFolder)) {
|
||||
if (!_currentSyncFolder->canSync()) {
|
||||
qCInfo(lcFolderMan) << "Folder is not ready to sync, not scheduled!";
|
||||
_socketApi->slotUpdateFolderView(f);
|
||||
_socketApi->slotUpdateFolderView();
|
||||
return;
|
||||
}
|
||||
f->prepareToSync();
|
||||
emit folderSyncStateChange(f);
|
||||
_scheduledFolders.enqueue(f);
|
||||
_currentSyncFolder->prepareToSync();
|
||||
emit folderSyncStateChange();
|
||||
_scheduledFolders.enqueue(_currentSyncFolder);
|
||||
emit scheduleQueueChanged();
|
||||
} else {
|
||||
qCInfo(lcFolderMan) << "Sync for folder " << alias << " already scheduled, do not enqueue!";
|
||||
@@ -525,21 +517,21 @@ void FolderMan::scheduleFolder(Folder *f)
|
||||
startScheduledSyncSoon();
|
||||
}
|
||||
|
||||
void FolderMan::scheduleFolderNext(Folder *f)
|
||||
void FolderMan::scheduleFolderNext()
|
||||
{
|
||||
auto alias = f->alias();
|
||||
auto alias = _currentSyncFolder->alias();
|
||||
qCInfo(lcFolderMan) << "Schedule folder " << alias << " to sync! Front-of-queue.";
|
||||
|
||||
if (!f->canSync()) {
|
||||
if (!_currentSyncFolder->canSync()) {
|
||||
qCInfo(lcFolderMan) << "Folder is not ready to sync, not scheduled!";
|
||||
return;
|
||||
}
|
||||
|
||||
_scheduledFolders.removeAll(f);
|
||||
_scheduledFolders.clear();
|
||||
|
||||
f->prepareToSync();
|
||||
emit folderSyncStateChange(f);
|
||||
_scheduledFolders.prepend(f);
|
||||
_currentSyncFolder->prepareToSync();
|
||||
emit folderSyncStateChange();
|
||||
_scheduledFolders.prepend(_currentSyncFolder);
|
||||
emit scheduleQueueChanged();
|
||||
|
||||
startScheduledSyncSoon();
|
||||
@@ -562,14 +554,9 @@ void FolderMan::slotEtagJobDestroyed(QObject * /*o*/)
|
||||
void FolderMan::slotRunOneEtagJob()
|
||||
{
|
||||
if (_currentEtagJob.isNull()) {
|
||||
Folder *folder;
|
||||
foreach (Folder *f, _folderMap) {
|
||||
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.
|
||||
_currentEtagJob = f->etagJob();
|
||||
folder = f;
|
||||
break;
|
||||
}
|
||||
if (_currentSyncFolder->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.
|
||||
_currentEtagJob = _currentSyncFolder->etagJob();
|
||||
}
|
||||
if (_currentEtagJob.isNull()) {
|
||||
//qCDebug(lcFolderMan) << "No more remote ETag check jobs to schedule.";
|
||||
@@ -579,7 +566,7 @@ void FolderMan::slotRunOneEtagJob()
|
||||
restartApplication();
|
||||
}
|
||||
} else {
|
||||
qCDebug(lcFolderMan) << "Scheduling" << folder->remoteUrl().toString() << "to check remote ETag";
|
||||
qCDebug(lcFolderMan) << "Scheduling" << _currentSyncFolder->remoteUrl().toString() << "to check remote ETag";
|
||||
_currentEtagJob->start(); // on destroy/end it will continue the queue via slotEtagJobDestroyed
|
||||
}
|
||||
}
|
||||
@@ -596,12 +583,10 @@ void FolderMan::slotAccountStateChanged()
|
||||
if (accountState->isConnected()) {
|
||||
qCInfo(lcFolderMan) << "Account" << accountName << "connected, scheduling its folders";
|
||||
|
||||
foreach (Folder *f, _folderMap.values()) {
|
||||
if (f
|
||||
&& f->canSync()
|
||||
&& f->accountState() == accountState) {
|
||||
scheduleFolder(f);
|
||||
}
|
||||
if (_currentSyncFolder
|
||||
&& _currentSyncFolder->canSync()
|
||||
&& _currentSyncFolder->accountState() == accountState) {
|
||||
scheduleFolder();
|
||||
}
|
||||
} else {
|
||||
qCInfo(lcFolderMan) << "Account" << accountName << "disconnected or paused, "
|
||||
@@ -633,7 +618,7 @@ void FolderMan::setSyncEnabled(bool enabled)
|
||||
}
|
||||
_syncEnabled = enabled;
|
||||
// force a redraw in case the network connect status changed
|
||||
emit(folderSyncStateChange(0));
|
||||
emit(folderSyncStateChange());
|
||||
}
|
||||
|
||||
void FolderMan::startScheduledSyncSoon()
|
||||
@@ -641,10 +626,13 @@ void FolderMan::startScheduledSyncSoon()
|
||||
if (_startScheduledSyncTimer.isActive()) {
|
||||
return;
|
||||
}
|
||||
if (_scheduledFolders.empty()) {
|
||||
|
||||
if (_currentSyncFolder->isBusy()) {
|
||||
qCInfo(lcFolderMan) << "Currently folder " << _currentSyncFolder->remoteUrl().toString() << " is running, wait for finish!";
|
||||
return;
|
||||
}
|
||||
if (_currentSyncFolder) {
|
||||
|
||||
if (_scheduledFolders.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -652,14 +640,14 @@ void FolderMan::startScheduledSyncSoon()
|
||||
qint64 msSinceLastSync = 0;
|
||||
|
||||
// Require a pause based on the duration of the last sync run.
|
||||
if (Folder *lastFolder = _lastSyncFolder) {
|
||||
msSinceLastSync = lastFolder->msecSinceLastSync().count();
|
||||
if (_currentSyncFolder) {
|
||||
msSinceLastSync = _currentSyncFolder->msecSinceLastSync().count();
|
||||
|
||||
// 1s -> 1.5s pause
|
||||
// 10s -> 5s pause
|
||||
// 1min -> 12s pause
|
||||
// 1h -> 90s pause
|
||||
qint64 pause = qSqrt(lastFolder->msecLastSyncDuration().count()) / 20.0 * 1000.0;
|
||||
qint64 pause = qSqrt(_currentSyncFolder->msecLastSyncDuration().count()) / 20.0 * 1000.0;
|
||||
msDelay = qMax(msDelay, pause);
|
||||
}
|
||||
|
||||
@@ -672,7 +660,12 @@ void FolderMan::startScheduledSyncSoon()
|
||||
msDelay = qMax(1ll, msDelay - msSinceLastSync);
|
||||
|
||||
qCInfo(lcFolderMan) << "Starting the next scheduled sync in" << (msDelay / 1000) << "seconds";
|
||||
_startScheduledSyncTimer.start(msDelay);
|
||||
|
||||
// called by FUSE from a different thread
|
||||
if (thread() != QThread::currentThread())
|
||||
slotStartScheduledFolderSync();
|
||||
else
|
||||
_startScheduledSyncTimer.start(msDelay);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -682,7 +675,7 @@ void FolderMan::startScheduledSyncSoon()
|
||||
*/
|
||||
void FolderMan::slotStartScheduledFolderSync()
|
||||
{
|
||||
if (_currentSyncFolder) {
|
||||
if (_currentSyncFolder->isBusy()) {
|
||||
qCInfo(lcFolderMan) << "Currently folder " << _currentSyncFolder->remoteUrl().toString() << " is running, wait for finish!";
|
||||
return;
|
||||
}
|
||||
@@ -698,26 +691,17 @@ void FolderMan::slotStartScheduledFolderSync()
|
||||
}
|
||||
|
||||
// Find the first folder in the queue that can be synced.
|
||||
Folder *folder = 0;
|
||||
while (!_scheduledFolders.isEmpty()) {
|
||||
Folder *g = _scheduledFolders.dequeue();
|
||||
if (g->canSync()) {
|
||||
folder = g;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_scheduledFolders.clear();
|
||||
|
||||
emit scheduleQueueChanged();
|
||||
|
||||
// Start syncing this folder!
|
||||
if (folder) {
|
||||
if (_currentSyncFolder) {
|
||||
// Safe to call several times, and necessary to try again if
|
||||
// the folder path didn't exist previously.
|
||||
folder->registerFolderWatcher();
|
||||
registerFolderWithSocketApi(folder);
|
||||
|
||||
_currentSyncFolder = folder;
|
||||
folder->startSync(QStringList());
|
||||
//_currentSyncFolder->registerFolderWatcher();
|
||||
registerFolderWithSocketApi();
|
||||
_currentSyncFolder->startSync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -730,13 +714,14 @@ void FolderMan::slotEtagPollTimerTimeout()
|
||||
if (!f) {
|
||||
continue;
|
||||
}
|
||||
if (_currentSyncFolder == f) {
|
||||
continue;
|
||||
}
|
||||
// TODO FUSE
|
||||
// if (_currentSyncFolder == f) {
|
||||
// continue;
|
||||
// }
|
||||
if (_scheduledFolders.contains(f)) {
|
||||
continue;
|
||||
}
|
||||
if (_disabledFolders.contains(f)) {
|
||||
if (f->syncPaused()) {
|
||||
continue;
|
||||
}
|
||||
if (f->etagJob() || f->isBusy() || !f->canSync()) {
|
||||
@@ -760,9 +745,9 @@ void FolderMan::slotRemoveFoldersForAccount(AccountState *accountState)
|
||||
foldersToRemove.append(folder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (const auto &f, foldersToRemove) {
|
||||
removeFolder(f);
|
||||
removeFolder();
|
||||
}
|
||||
emit folderListChanged(_folderMap);
|
||||
}
|
||||
@@ -770,7 +755,7 @@ void FolderMan::slotRemoveFoldersForAccount(AccountState *accountState)
|
||||
void FolderMan::slotForwardFolderSyncStateChange()
|
||||
{
|
||||
if (Folder *f = qobject_cast<Folder *>(sender())) {
|
||||
emit folderSyncStateChange(f);
|
||||
emit folderSyncStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,18 +766,17 @@ void FolderMan::slotServerVersionChanged(Account *account)
|
||||
qCWarning(lcFolderMan) << "The server version is unsupported:" << account->serverVersion()
|
||||
<< "pausing all folders on the account";
|
||||
|
||||
foreach (auto &f, _folderMap) {
|
||||
if (f->accountState()->account().data() == account) {
|
||||
f->setSyncPaused(true);
|
||||
}
|
||||
if (_currentSyncFolder->accountState()->account().data() == account) {
|
||||
_currentSyncFolder->setSyncPaused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMan::slotWatchedFileUnlocked(const QString &path)
|
||||
{
|
||||
if (Folder *f = folderForPath(path)) {
|
||||
f->scheduleThisFolderSoon();
|
||||
Folder *folder = folderForPath(path);
|
||||
if (folder == _currentSyncFolder) {
|
||||
_currentSyncFolder->scheduleThisFolderSoon();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,7 +798,7 @@ void FolderMan::slotScheduleFolderByTime()
|
||||
<< "because it has been" << msecsSinceSync.count() << "ms "
|
||||
<< "since the last sync";
|
||||
|
||||
scheduleFolder(f);
|
||||
scheduleFolder();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -832,7 +816,7 @@ void FolderMan::slotScheduleFolderByTime()
|
||||
<< ", last status:" << f->syncResult().statusString()
|
||||
<< ", time since last sync:" << msecsSinceSync.count();
|
||||
|
||||
scheduleFolder(f);
|
||||
scheduleFolder();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -861,8 +845,8 @@ void FolderMan::slotFolderSyncFinished(const SyncResult &)
|
||||
qPrintable(_currentSyncFolder->accountState()->account()->displayName()),
|
||||
qPrintable(_currentSyncFolder->remoteUrl().toString()));
|
||||
|
||||
_lastSyncFolder = _currentSyncFolder;
|
||||
_currentSyncFolder = 0;
|
||||
//TODO FUSE
|
||||
//_currentSyncFolder = 0;
|
||||
|
||||
startScheduledSyncSoon();
|
||||
}
|
||||
@@ -877,28 +861,28 @@ Folder *FolderMan::addFolder(AccountState *accountState, const FolderDefinition
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto folder = addFolderInternal(definition, accountState);
|
||||
_currentSyncFolder = addFolderInternal(definition, accountState);
|
||||
|
||||
// Migration: The first account that's configured for a local folder shall
|
||||
// be saved in a backwards-compatible way.
|
||||
bool oneAccountOnly = true;
|
||||
foreach (Folder *other, FolderMan::instance()->map()) {
|
||||
if (other != folder && other->cleanPath() == folder->cleanPath()) {
|
||||
if (other != _currentSyncFolder && other->cleanPath() == _currentSyncFolder->cleanPath()) {
|
||||
oneAccountOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
folder->setSaveBackwardsCompatible(oneAccountOnly);
|
||||
_currentSyncFolder->setSaveBackwardsCompatible(oneAccountOnly);
|
||||
|
||||
if (folder) {
|
||||
folder->saveToSettings();
|
||||
emit folderSyncStateChange(folder);
|
||||
if (_currentSyncFolder) {
|
||||
_currentSyncFolder->saveToSettings();
|
||||
emit folderSyncStateChange();
|
||||
emit folderListChanged(_folderMap);
|
||||
}
|
||||
|
||||
_navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
|
||||
|
||||
return folder;
|
||||
return _currentSyncFolder;
|
||||
}
|
||||
|
||||
Folder *FolderMan::addFolderInternal(FolderDefinition folderDefinition,
|
||||
@@ -906,7 +890,7 @@ Folder *FolderMan::addFolderInternal(FolderDefinition folderDefinition,
|
||||
{
|
||||
auto alias = folderDefinition.alias;
|
||||
int count = 0;
|
||||
while (folderDefinition.alias.isEmpty() || _folderMap.contains(folderDefinition.alias)) {
|
||||
while (folderDefinition.alias.isEmpty()) {
|
||||
// There is already a folder configured with this name and folder names need to be unique
|
||||
folderDefinition.alias = alias + QString::number(++count);
|
||||
}
|
||||
@@ -914,10 +898,9 @@ Folder *FolderMan::addFolderInternal(FolderDefinition folderDefinition,
|
||||
auto folder = new Folder(folderDefinition, accountState, this);
|
||||
|
||||
qCInfo(lcFolderMan) << "Adding folder to Folder Map " << folder << folder->alias();
|
||||
|
||||
//TODO FUSE
|
||||
_folderMap[folder->alias()] = folder;
|
||||
if (folder->syncPaused()) {
|
||||
_disabledFolders.insert(folder);
|
||||
}
|
||||
|
||||
// See matching disconnects in unloadFolder().
|
||||
connect(folder, &Folder::syncStarted, this, &FolderMan::slotFolderSyncStarted);
|
||||
@@ -930,8 +913,8 @@ Folder *FolderMan::addFolderInternal(FolderDefinition folderDefinition,
|
||||
connect(folder, &Folder::watchedFileChangedExternally,
|
||||
&folder->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::slotPathTouched);
|
||||
|
||||
folder->registerFolderWatcher();
|
||||
registerFolderWithSocketApi(folder);
|
||||
//folder->registerFolderWatcher();
|
||||
registerFolderWithSocketApi();
|
||||
return folder;
|
||||
}
|
||||
|
||||
@@ -971,40 +954,40 @@ QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const Acco
|
||||
return re;
|
||||
}
|
||||
|
||||
void FolderMan::removeFolder(Folder *f)
|
||||
void FolderMan::removeFolder()
|
||||
{
|
||||
if (!f) {
|
||||
if (!_currentSyncFolder) {
|
||||
qCCritical(lcFolderMan) << "Can not remove null folder";
|
||||
return;
|
||||
}
|
||||
|
||||
qCInfo(lcFolderMan) << "Removing " << f->alias();
|
||||
qCInfo(lcFolderMan) << "Removing " << _currentSyncFolder->alias();
|
||||
|
||||
const bool currentlyRunning = (_currentSyncFolder == f);
|
||||
const bool currentlyRunning = _currentSyncFolder;
|
||||
if (currentlyRunning) {
|
||||
// abort the sync now
|
||||
terminateSyncProcess();
|
||||
}
|
||||
|
||||
if (_scheduledFolders.removeAll(f) > 0) {
|
||||
if (_scheduledFolders.removeAll(_currentSyncFolder)>0){
|
||||
emit scheduleQueueChanged();
|
||||
}
|
||||
|
||||
f->wipe();
|
||||
f->setSyncPaused(true);
|
||||
_currentSyncFolder->wipe();
|
||||
_currentSyncFolder->setSyncPaused(true);
|
||||
|
||||
// remove the folder configuration
|
||||
f->removeFromSettings();
|
||||
_currentSyncFolder->removeFromSettings();
|
||||
|
||||
unloadFolder(f);
|
||||
unloadFolder();
|
||||
if (currentlyRunning) {
|
||||
// We want to schedule the next folder once this is done
|
||||
connect(f, &Folder::syncFinished,
|
||||
connect(_currentSyncFolder, &Folder::syncFinished,
|
||||
this, &FolderMan::slotFolderSyncFinished);
|
||||
// Let the folder delete itself when done.
|
||||
connect(f, &Folder::syncFinished, f, &QObject::deleteLater);
|
||||
connect(_currentSyncFolder, &Folder::syncFinished, _currentSyncFolder, &QObject::deleteLater);
|
||||
} else {
|
||||
delete f;
|
||||
delete _currentSyncFolder;
|
||||
}
|
||||
|
||||
_navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
|
||||
@@ -1086,35 +1069,30 @@ bool FolderMan::startFromScratch(const QString &localFolder)
|
||||
|
||||
void FolderMan::setDirtyProxy()
|
||||
{
|
||||
foreach (Folder *f, _folderMap.values()) {
|
||||
if (f) {
|
||||
if (f->accountState() && f->accountState()->account()
|
||||
&& f->accountState()->account()->networkAccessManager()) {
|
||||
// Need to do this so we do not use the old determined system proxy
|
||||
f->accountState()->account()->networkAccessManager()->setProxy(
|
||||
QNetworkProxy(QNetworkProxy::DefaultProxy));
|
||||
}
|
||||
if (_currentSyncFolder) {
|
||||
if (_currentSyncFolder->accountState() && _currentSyncFolder->accountState()->account()
|
||||
&& _currentSyncFolder->accountState()->account()->networkAccessManager()) {
|
||||
// Need to do this so we do not use the old determined system proxy
|
||||
_currentSyncFolder->accountState()->account()->networkAccessManager()->setProxy(
|
||||
QNetworkProxy(QNetworkProxy::DefaultProxy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMan::setDirtyNetworkLimits()
|
||||
{
|
||||
foreach (Folder *f, _folderMap.values()) {
|
||||
// set only in busy folders. Otherwise they read the config anyway.
|
||||
if (f && f->isBusy()) {
|
||||
f->setDirtyNetworkLimits();
|
||||
}
|
||||
// set only in busy folders. Otherwise they read the config anyway.
|
||||
if (_currentSyncFolder && _currentSyncFolder->isBusy()) {
|
||||
_currentSyncFolder->setDirtyNetworkLimits();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMan::trayOverallStatus(const QList<Folder *> &folders,
|
||||
SyncResult::Status *status, bool *unresolvedConflicts)
|
||||
void FolderMan::trayOverallStatus(SyncResult::Status *status, bool *unresolvedConflicts)
|
||||
{
|
||||
*status = SyncResult::Undefined;
|
||||
*unresolvedConflicts = false;
|
||||
|
||||
int cnt = folders.count();
|
||||
int cnt = 1;
|
||||
|
||||
// if one folder: show the state of the one folder.
|
||||
// if more folders:
|
||||
@@ -1123,27 +1101,24 @@ void FolderMan::trayOverallStatus(const QList<Folder *> &folders,
|
||||
// do not show "problem" in the tray
|
||||
//
|
||||
if (cnt == 1) {
|
||||
Folder *folder = folders.at(0);
|
||||
if (folder) {
|
||||
auto syncResult = folder->syncResult();
|
||||
if (folder->syncPaused()) {
|
||||
*status = SyncResult::Paused;
|
||||
} else {
|
||||
SyncResult::Status syncStatus = syncResult.status();
|
||||
switch (syncStatus) {
|
||||
case SyncResult::Undefined:
|
||||
*status = SyncResult::Error;
|
||||
break;
|
||||
case SyncResult::Problem: // don't show the problem icon in tray.
|
||||
*status = SyncResult::Success;
|
||||
break;
|
||||
default:
|
||||
*status = syncStatus;
|
||||
break;
|
||||
}
|
||||
auto syncResult = FolderMan::instance()->currentSyncFolder()->syncResult();
|
||||
if (FolderMan::instance()->currentSyncFolder()->syncPaused()) {
|
||||
*status = SyncResult::Paused;
|
||||
} else {
|
||||
SyncResult::Status syncStatus = syncResult.status();
|
||||
switch (syncStatus) {
|
||||
case SyncResult::Undefined:
|
||||
*status = SyncResult::Error;
|
||||
break;
|
||||
case SyncResult::Problem: // don't show the problem icon in tray.
|
||||
*status = SyncResult::Success;
|
||||
break;
|
||||
default:
|
||||
*status = syncStatus;
|
||||
break;
|
||||
}
|
||||
*unresolvedConflicts = syncResult.hasUnresolvedConflicts();
|
||||
}
|
||||
*unresolvedConflicts = syncResult.hasUnresolvedConflicts();
|
||||
} else {
|
||||
int errorsSeen = 0;
|
||||
int goodSeen = 0;
|
||||
@@ -1151,39 +1126,37 @@ void FolderMan::trayOverallStatus(const QList<Folder *> &folders,
|
||||
int runSeen = 0;
|
||||
int various = 0;
|
||||
|
||||
foreach (Folder *folder, folders) {
|
||||
SyncResult folderResult = folder->syncResult();
|
||||
if (folder->syncPaused()) {
|
||||
abortOrPausedSeen++;
|
||||
} else {
|
||||
SyncResult::Status syncStatus = folderResult.status();
|
||||
SyncResult folderResult = FolderMan::instance()->currentSyncFolder()->syncResult();
|
||||
if (FolderMan::instance()->currentSyncFolder()->syncPaused()) {
|
||||
abortOrPausedSeen++;
|
||||
} else {
|
||||
SyncResult::Status syncStatus = folderResult.status();
|
||||
|
||||
switch (syncStatus) {
|
||||
case SyncResult::Undefined:
|
||||
case SyncResult::NotYetStarted:
|
||||
various++;
|
||||
break;
|
||||
case SyncResult::SyncPrepare:
|
||||
case SyncResult::SyncRunning:
|
||||
runSeen++;
|
||||
break;
|
||||
case SyncResult::Problem: // don't show the problem icon in tray.
|
||||
case SyncResult::Success:
|
||||
goodSeen++;
|
||||
break;
|
||||
case SyncResult::Error:
|
||||
case SyncResult::SetupError:
|
||||
errorsSeen++;
|
||||
break;
|
||||
case SyncResult::SyncAbortRequested:
|
||||
case SyncResult::Paused:
|
||||
abortOrPausedSeen++;
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
switch (syncStatus) {
|
||||
case SyncResult::Undefined:
|
||||
case SyncResult::NotYetStarted:
|
||||
various++;
|
||||
break;
|
||||
case SyncResult::SyncPrepare:
|
||||
case SyncResult::SyncRunning:
|
||||
runSeen++;
|
||||
break;
|
||||
case SyncResult::Problem: // don't show the problem icon in tray.
|
||||
case SyncResult::Success:
|
||||
goodSeen++;
|
||||
break;
|
||||
case SyncResult::Error:
|
||||
case SyncResult::SetupError:
|
||||
errorsSeen++;
|
||||
break;
|
||||
case SyncResult::SyncAbortRequested:
|
||||
case SyncResult::Paused:
|
||||
abortOrPausedSeen++;
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
if (folderResult.hasUnresolvedConflicts())
|
||||
*unresolvedConflicts = true;
|
||||
}
|
||||
if (folderResult.hasUnresolvedConflicts())
|
||||
*unresolvedConflicts = true;
|
||||
if (errorsSeen > 0) {
|
||||
*status = SyncResult::Error;
|
||||
} else if (abortOrPausedSeen > 0 && abortOrPausedSeen == cnt) {
|
||||
@@ -1381,10 +1354,8 @@ void FolderMan::setIgnoreHiddenFiles(bool ignore)
|
||||
{
|
||||
// Note that the setting will revert to 'true' if all folders
|
||||
// are deleted...
|
||||
foreach (Folder *folder, _folderMap) {
|
||||
folder->setIgnoreHiddenFiles(ignore);
|
||||
folder->saveToSettings();
|
||||
}
|
||||
_currentSyncFolder->setIgnoreHiddenFiles(ignore);
|
||||
_currentSyncFolder->saveToSettings();
|
||||
}
|
||||
|
||||
QQueue<Folder *> FolderMan::scheduleQueue() const
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
Folder *addFolder(AccountState *accountState, const FolderDefinition &folderDefinition);
|
||||
|
||||
/** Removes a folder */
|
||||
void removeFolder(Folder *);
|
||||
void removeFolder();
|
||||
|
||||
/** Returns the folder which the file or directory stored in path is in */
|
||||
Folder *folderForPath(const QString &path);
|
||||
@@ -110,8 +110,7 @@ public:
|
||||
static QString trayTooltipStatusString(SyncResult::Status syncStatus, bool hasUnresolvedConflicts, bool paused);
|
||||
|
||||
/// Compute status summarizing multiple folders
|
||||
static void trayOverallStatus(const QList<Folder *> &folders,
|
||||
SyncResult::Status *status, bool *unresolvedConflicts);
|
||||
static void trayOverallStatus(SyncResult::Status *status, bool *unresolvedConflicts);
|
||||
|
||||
// Escaping of the alias which is used in QSettings AND the file
|
||||
// system, thus need to be escaped.
|
||||
@@ -173,10 +172,10 @@ public:
|
||||
void setSyncEnabled(bool);
|
||||
|
||||
/** Queues a folder for syncing. */
|
||||
void scheduleFolder(Folder *);
|
||||
void scheduleFolder();
|
||||
|
||||
/** Puts a folder in the very front of the queue. */
|
||||
void scheduleFolderNext(Folder *);
|
||||
void scheduleFolderNext();
|
||||
|
||||
/** Queues all folders for syncing. */
|
||||
void scheduleAllFolders();
|
||||
@@ -197,7 +196,7 @@ signals:
|
||||
*
|
||||
* Attention: The folder may be zero. Do a general update of the state then.
|
||||
*/
|
||||
void folderSyncStateChange(Folder *);
|
||||
void folderSyncStateChange();
|
||||
|
||||
/**
|
||||
* Indicates when the schedule queue changes.
|
||||
@@ -233,8 +232,11 @@ public slots:
|
||||
// slot to schedule an ETag job (from Folder only)
|
||||
void slotScheduleETagJob(const QString &alias, RequestEtagJob *job);
|
||||
|
||||
// slot to take the next folder from queue and start syncing.
|
||||
void slotStartScheduledFolderSync();
|
||||
|
||||
private slots:
|
||||
void slotFolderSyncPaused(Folder *, bool paused);
|
||||
void slotFolderSyncPaused(bool paused);
|
||||
void slotFolderCanSyncChanged();
|
||||
void slotFolderSyncStarted();
|
||||
void slotFolderSyncFinished(const SyncResult &);
|
||||
@@ -242,8 +244,6 @@ private slots:
|
||||
void slotRunOneEtagJob();
|
||||
void slotEtagJobDestroyed(QObject *);
|
||||
|
||||
// slot to take the next folder from queue and start syncing.
|
||||
void slotStartScheduledFolderSync();
|
||||
void slotEtagPollTimerTimeout();
|
||||
|
||||
void slotRemoveFoldersForAccount(AccountState *accountState);
|
||||
@@ -278,7 +278,7 @@ private:
|
||||
AccountState *accountState);
|
||||
|
||||
/* unloads a folder object, does not delete it */
|
||||
void unloadFolder(Folder *);
|
||||
void unloadFolder();
|
||||
|
||||
/** Will start a sync after a bit of delay. */
|
||||
void startScheduledSyncSoon();
|
||||
@@ -288,18 +288,21 @@ private:
|
||||
QString getBackupName(QString fullPathName) const;
|
||||
|
||||
// makes the folder known to the socket api
|
||||
void registerFolderWithSocketApi(Folder *folder);
|
||||
void registerFolderWithSocketApi();
|
||||
|
||||
// restarts the application (Linux only)
|
||||
void restartApplication();
|
||||
|
||||
void setupFoldersHelper(QSettings &settings, AccountStatePtr account, bool backwardsCompatible);
|
||||
|
||||
QSet<Folder *> _disabledFolders;
|
||||
Folder *_disabledFolder;
|
||||
|
||||
//TODO FUSE
|
||||
//Folder::Map _folderMap;
|
||||
Folder::Map _folderMap;
|
||||
|
||||
QString _folderConfigPath;
|
||||
Folder *_currentSyncFolder;
|
||||
QPointer<Folder> _lastSyncFolder;
|
||||
bool _syncEnabled;
|
||||
|
||||
/// Starts regular etag query jobs
|
||||
|
||||
@@ -57,17 +57,19 @@ QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||
QFontMetrics aliasFm(aliasFont);
|
||||
|
||||
auto classif = static_cast<const FolderStatusModel *>(index.model())->classify(index);
|
||||
if (classif == FolderStatusModel::AddButton) {
|
||||
const int margins = aliasFm.height(); // same as 2*aliasMargin of paint
|
||||
QFontMetrics fm(qApp->font("QPushButton"));
|
||||
QStyleOptionButton opt;
|
||||
static_cast<QStyleOption &>(opt) = option;
|
||||
opt.text = addFolderText();
|
||||
return QApplication::style()->sizeFromContents(
|
||||
QStyle::CT_PushButton, &opt, fm.size(Qt::TextSingleLine, opt.text))
|
||||
.expandedTo(QApplication::globalStrut())
|
||||
+ QSize(0, margins);
|
||||
}
|
||||
|
||||
// TODO: FUSE - only one foldr allowed now
|
||||
// if (classif == FolderStatusModel::AddButton) {
|
||||
// const int margins = aliasFm.height(); // same as 2*aliasMargin of paint
|
||||
// QFontMetrics fm(qApp->font("QPushButton"));
|
||||
// QStyleOptionButton opt;
|
||||
// static_cast<QStyleOption &>(opt) = option;
|
||||
// opt.text = addFolderText();
|
||||
// return QApplication::style()->sizeFromContents(
|
||||
// QStyle::CT_PushButton, &opt, fm.size(Qt::TextSingleLine, opt.text))
|
||||
// .expandedTo(QApplication::globalStrut())
|
||||
// + QSize(0, margins);
|
||||
// }
|
||||
|
||||
if (classif != FolderStatusModel::RootFolder) {
|
||||
return QStyledItemDelegate::sizeHint(option, index);
|
||||
@@ -128,22 +130,23 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
int aliasMargin = aliasFm.height() / 2;
|
||||
int margin = subFm.height() / 4;
|
||||
|
||||
if (index.data(AddButton).toBool()) {
|
||||
QSize hint = sizeHint(option, index);
|
||||
QStyleOptionButton opt;
|
||||
static_cast<QStyleOption &>(opt) = option;
|
||||
opt.state &= ~QStyle::State_Selected;
|
||||
opt.state |= QStyle::State_Raised;
|
||||
opt.text = addFolderText();
|
||||
opt.rect.setWidth(qMin(opt.rect.width(), hint.width()));
|
||||
opt.rect.adjust(0, aliasMargin, 0, -aliasMargin);
|
||||
opt.rect = QStyle::visualRect(option.direction, option.rect, opt.rect);
|
||||
painter->save();
|
||||
painter->setFont(qApp->font("QPushButton"));
|
||||
QApplication::style()->drawControl(QStyle::CE_PushButton, &opt, painter, option.widget);
|
||||
painter->restore();
|
||||
return;
|
||||
}
|
||||
// TODO: FUSE - only one foldr allowed now
|
||||
// if (index.data(AddButton).toBool()) {
|
||||
// QSize hint = sizeHint(option, index);
|
||||
// QStyleOptionButton opt;
|
||||
// static_cast<QStyleOption &>(opt) = option;
|
||||
// opt.state &= ~QStyle::State_Selected;
|
||||
// opt.state |= QStyle::State_Raised;
|
||||
// opt.text = addFolderText();
|
||||
// opt.rect.setWidth(qMin(opt.rect.width(), hint.width()));
|
||||
// opt.rect.adjust(0, aliasMargin, 0, -aliasMargin);
|
||||
// opt.rect = QStyle::visualRect(option.direction, option.rect, opt.rect);
|
||||
// painter->save();
|
||||
// painter->setFont(qApp->font("QPushButton"));
|
||||
// QApplication::style()->drawControl(QStyle::CE_PushButton, &opt, painter, option.widget);
|
||||
// painter->restore();
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (static_cast<const FolderStatusModel *>(index.model())->classify(index) != FolderStatusModel::RootFolder) {
|
||||
return;
|
||||
|
||||
@@ -423,9 +423,9 @@ FolderStatusModel::SubFolderInfo *FolderStatusModel::infoForFileId(const QByteAr
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QModelIndex FolderStatusModel::indexForPath(Folder *f, const QString &path) const
|
||||
QModelIndex FolderStatusModel::indexForPath(const QString &path) const
|
||||
{
|
||||
if (!f) {
|
||||
if (!FolderMan::instance()->currentSyncFolder()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
@@ -434,7 +434,7 @@ QModelIndex FolderStatusModel::indexForPath(Folder *f, const QString &path) cons
|
||||
// first level folder
|
||||
for (int i = 0; i < _folders.size(); ++i) {
|
||||
auto &info = _folders.at(i);
|
||||
if (info._folder == f) {
|
||||
if (info._folder == FolderMan::instance()->currentSyncFolder()) {
|
||||
if (path.isEmpty()) { // the folder object
|
||||
return index(i, 0);
|
||||
}
|
||||
@@ -450,7 +450,7 @@ QModelIndex FolderStatusModel::indexForPath(Folder *f, const QString &path) cons
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
auto parent = indexForPath(f, path.left(slashPos));
|
||||
auto parent = indexForPath(path.left(slashPos));
|
||||
if (!parent.isValid())
|
||||
return parent;
|
||||
|
||||
@@ -820,12 +820,12 @@ QStringList FolderStatusModel::createBlackList(FolderStatusModel::SubFolderInfo
|
||||
return result;
|
||||
}
|
||||
|
||||
void FolderStatusModel::slotUpdateFolderState(Folder *folder)
|
||||
void FolderStatusModel::slotUpdateFolderState()
|
||||
{
|
||||
if (!folder)
|
||||
if (!FolderMan::instance()->currentSyncFolder())
|
||||
return;
|
||||
for (int i = 0; i < _folders.count(); ++i) {
|
||||
if (_folders.at(i)._folder == folder) {
|
||||
if (_folders.at(i)._folder == FolderMan::instance()->currentSyncFolder()) {
|
||||
emit dataChanged(index(i), index(i));
|
||||
}
|
||||
}
|
||||
@@ -877,7 +877,7 @@ void FolderStatusModel::slotApplySelectiveSync()
|
||||
foreach (const auto &it, changes) {
|
||||
folder->journalDb()->avoidReadFromDbOnNextSync(it);
|
||||
}
|
||||
FolderMan::instance()->scheduleFolder(folder);
|
||||
FolderMan::instance()->scheduleFolder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -891,14 +891,13 @@ void FolderStatusModel::slotSetProgress(const ProgressInfo &progress)
|
||||
return; // for https://github.com/owncloud/client/issues/2648#issuecomment-71377909
|
||||
}
|
||||
|
||||
Folder *f = qobject_cast<Folder *>(sender());
|
||||
if (!f) {
|
||||
if (!FolderMan::instance()->currentSyncFolder()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int folderIndex = -1;
|
||||
for (int i = 0; i < _folders.count(); ++i) {
|
||||
if (_folders.at(i)._folder == f) {
|
||||
if (_folders.at(i)._folder == FolderMan::instance()->currentSyncFolder()) {
|
||||
folderIndex = i;
|
||||
break;
|
||||
}
|
||||
@@ -1065,8 +1064,9 @@ void FolderStatusModel::slotSetProgress(const ProgressInfo &progress)
|
||||
emit dataChanged(index(folderIndex), index(folderIndex), roles);
|
||||
}
|
||||
|
||||
void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
|
||||
void FolderStatusModel::slotFolderSyncStateChange()
|
||||
{
|
||||
Folder *f = FolderMan::instance()->currentSyncFolder();
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
@@ -1114,7 +1114,7 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
|
||||
}
|
||||
|
||||
// update the icon etc. now
|
||||
slotUpdateFolderState(f);
|
||||
slotUpdateFolderState();
|
||||
|
||||
if (f->syncResult().folderStructureWasChanged()
|
||||
&& (state == SyncResult::Success || state == SyncResult::Problem)) {
|
||||
@@ -1127,9 +1127,7 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
|
||||
void FolderStatusModel::slotFolderScheduleQueueChanged()
|
||||
{
|
||||
// Update messages on waiting folders.
|
||||
foreach (Folder *f, FolderMan::instance()->map()) {
|
||||
slotFolderSyncStateChange(f);
|
||||
}
|
||||
slotFolderSyncStateChange();
|
||||
}
|
||||
|
||||
void FolderStatusModel::resetFolders()
|
||||
@@ -1190,7 +1188,7 @@ void FolderStatusModel::slotSyncAllPendingBigFolders()
|
||||
foreach (const auto &it, undecidedList) {
|
||||
folder->journalDb()->avoidReadFromDbOnNextSync(it);
|
||||
}
|
||||
FolderMan::instance()->scheduleFolder(folder);
|
||||
FolderMan::instance()->scheduleFolder();
|
||||
}
|
||||
|
||||
resetFolders();
|
||||
@@ -1210,12 +1208,11 @@ void FolderStatusModel::slotSyncNoPendingBigFolders()
|
||||
|
||||
void FolderStatusModel::slotNewBigFolder()
|
||||
{
|
||||
auto f = qobject_cast<Folder *>(sender());
|
||||
ASSERT(f);
|
||||
ASSERT(FolderMan::instance()->currentSyncFolder());
|
||||
|
||||
int folderIndex = -1;
|
||||
for (int i = 0; i < _folders.count(); ++i) {
|
||||
if (_folders.at(i)._folder == f) {
|
||||
if (_folders.at(i)._folder == FolderMan::instance()->currentSyncFolder()) {
|
||||
folderIndex = i;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -129,10 +129,10 @@ public:
|
||||
* return a QModelIndex for the given path within the given folder.
|
||||
* Note: this method returns an invalid index if the path was not fetched from the server before
|
||||
*/
|
||||
QModelIndex indexForPath(Folder *f, const QString &path) const;
|
||||
QModelIndex indexForPath(const QString &path) const;
|
||||
|
||||
public slots:
|
||||
void slotUpdateFolderState(Folder *);
|
||||
void slotUpdateFolderState();
|
||||
void slotApplySelectiveSync();
|
||||
void resetFolders();
|
||||
void slotSyncAllPendingBigFolders();
|
||||
@@ -143,7 +143,7 @@ private slots:
|
||||
void slotUpdateDirectories(const QStringList &);
|
||||
void slotGatherPermissions(const QString &name, const QMap<QString, QString> &properties);
|
||||
void slotLscolFinishedWithError(QNetworkReply *r);
|
||||
void slotFolderSyncStateChange(Folder *f);
|
||||
void slotFolderSyncStateChange();
|
||||
void slotFolderScheduleQueueChanged();
|
||||
void slotNewBigFolder();
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <QNetworkProxy>
|
||||
#include <QDir>
|
||||
#include <QScopedValueRollback>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -172,6 +173,33 @@ void GeneralSettings::slotToggleOptionalServerNotifications(bool enable)
|
||||
{
|
||||
ConfigFile cfgFile;
|
||||
cfgFile.setOptionalServerNotifications(enable);
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
QString defaultFileStreamSyncPath = cfgFile.defaultFileStreamSyncPath();
|
||||
QString defaultFileStreamMirrorPath = cfgFile.defaultFileStreamMirrorPath();
|
||||
|
||||
if (defaultFileStreamSyncPath.isEmpty() || defaultFileStreamSyncPath.compare(QString("")) == 0)
|
||||
cfgFile.setDefaultFileStreamSyncPath(QString("/Volumes/" + Theme::instance()->appName() + "fs"));
|
||||
|
||||
if (defaultFileStreamMirrorPath.isEmpty() || defaultFileStreamMirrorPath.compare(QString("")) == 0)
|
||||
cfgFile.setDefaultFileStreamMirrorPath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/.cachedFiles");
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
//< Set configuration paths.
|
||||
QString m_defaultFileStreamSyncPath = cfgFile.defaultFileStreamSyncPath();
|
||||
QString m_defaultFileStreamMirrorPath = cfgFile.defaultFileStreamMirrorPath();
|
||||
QString m_defaultFileStreamLetterDrive = cfgFile.defaultFileStreamLetterDrive();
|
||||
|
||||
if (m_defaultFileStreamSyncPath.isEmpty() || m_defaultFileStreamSyncPath.compare(QString("")) == 0)
|
||||
cfgFile.setDefaultFileStreamSyncPath(QString("X:/Mi unidad"));
|
||||
|
||||
if (m_defaultFileStreamMirrorPath.isEmpty() || m_defaultFileStreamMirrorPath.compare(QString("")) == 0)
|
||||
cfgFile.setDefaultFileStreamMirrorPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cachedFiles");
|
||||
|
||||
if (m_defaultFileStreamLetterDrive.isEmpty() || m_defaultFileStreamLetterDrive.compare(QString("")) == 0)
|
||||
cfgFile.setDefaultFileStreamLetterDrive(QString("x"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void GeneralSettings::slotShowInExplorerNavigationPane(bool checked)
|
||||
|
||||
@@ -149,7 +149,7 @@ void IgnoreListEditor::slotUpdateLocalIgnoreList()
|
||||
// ignored (because the remote etag did not change) (issue #3172)
|
||||
foreach (Folder *folder, folderMan->map()) {
|
||||
folder->journalDb()->forceRemoteDiscoveryNextSync();
|
||||
folderMan->scheduleFolder(folder);
|
||||
folderMan->scheduleFolder();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcNotifications)
|
||||
Q_LOGGING_CATEGORY(lcNotificationsJob, "nextcloud.gui.notifications", QtInfoMsg)
|
||||
|
||||
NotificationConfirmJob::NotificationConfirmJob(AccountPtr account)
|
||||
: AbstractNetworkJob(account, "")
|
||||
, _widget(0)
|
||||
{
|
||||
setIgnoreCredentialFailure(true);
|
||||
}
|
||||
@@ -35,20 +34,10 @@ void NotificationConfirmJob::setLinkAndVerb(const QUrl &link, const QByteArray &
|
||||
_verb = verb;
|
||||
}
|
||||
|
||||
void NotificationConfirmJob::setWidget(NotificationWidget *widget)
|
||||
{
|
||||
_widget = widget;
|
||||
}
|
||||
|
||||
NotificationWidget *NotificationConfirmJob::widget()
|
||||
{
|
||||
return _widget;
|
||||
}
|
||||
|
||||
void NotificationConfirmJob::start()
|
||||
{
|
||||
if (!_link.isValid()) {
|
||||
qCWarning(lcNotifications) << "Attempt to trigger invalid URL: " << _link.toString();
|
||||
qCWarning(lcNotificationsJob) << "Attempt to trigger invalid URL: " << _link.toString();
|
||||
return;
|
||||
}
|
||||
QNetworkRequest req;
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class NotificationWidget;
|
||||
|
||||
/**
|
||||
* @brief The NotificationConfirmJob class
|
||||
* @ingroup gui
|
||||
@@ -54,20 +52,6 @@ public:
|
||||
*/
|
||||
void start() Q_DECL_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @brief setWidget stores the associated widget to be able to use
|
||||
* it when the job has finished
|
||||
* @param widget pointer to the notification widget to store
|
||||
*/
|
||||
void setWidget(NotificationWidget *widget);
|
||||
|
||||
/**
|
||||
* @brief widget - get the associated notification widget as stored
|
||||
* with setWidget method.
|
||||
* @return widget pointer to the notification widget
|
||||
*/
|
||||
NotificationWidget *widget();
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
@@ -83,7 +67,6 @@ private slots:
|
||||
private:
|
||||
QByteArray _verb;
|
||||
QUrl _link;
|
||||
NotificationWidget *_widget;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
540
src/gui/old-widgets/issueswidget.cpp
Normal file
540
src/gui/old-widgets/issueswidget.cpp
Normal file
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "issueswidget.h"
|
||||
#include "configfile.h"
|
||||
#include "syncresult.h"
|
||||
#include "syncengine.h"
|
||||
#include "logger.h"
|
||||
#include "theme.h"
|
||||
#include "folderman.h"
|
||||
#include "syncfileitem.h"
|
||||
#include "folder.h"
|
||||
#include "openfilemanager.h"
|
||||
#include "activityitemdelegate.h"
|
||||
#include "protocolwidget.h"
|
||||
#include "accountstate.h"
|
||||
#include "account.h"
|
||||
#include "accountmanager.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
#include "elidedlabel.h"
|
||||
|
||||
|
||||
#include "ui_issueswidget.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* If more issues are reported than this they will not show up
|
||||
* to avoid performance issues around sorting this many issues.
|
||||
*/
|
||||
static const int maxIssueCount = 50000;
|
||||
|
||||
static QPair<QString, QString> pathsWithIssuesKey(const ProtocolItem::ExtraData &data)
|
||||
{
|
||||
return qMakePair(data.folderName, data.path);
|
||||
}
|
||||
|
||||
IssuesWidget::IssuesWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, _ui(new Ui::IssuesWidget)
|
||||
{
|
||||
_ui->setupUi(this);
|
||||
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::progressInfo,
|
||||
this, &IssuesWidget::slotProgressInfo);
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::itemCompleted,
|
||||
this, &IssuesWidget::slotItemCompleted);
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::syncError,
|
||||
this, &IssuesWidget::addError);
|
||||
|
||||
connect(_ui->_treeWidget, &QTreeWidget::itemActivated, this, &IssuesWidget::slotOpenFile);
|
||||
connect(_ui->copyIssuesButton, &QAbstractButton::clicked, this, &IssuesWidget::copyToClipboard);
|
||||
|
||||
_ui->_treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(_ui->_treeWidget, &QTreeWidget::customContextMenuRequested, this, &IssuesWidget::slotItemContextMenu);
|
||||
|
||||
connect(_ui->showIgnores, &QAbstractButton::toggled, this, &IssuesWidget::slotRefreshIssues);
|
||||
connect(_ui->showWarnings, &QAbstractButton::toggled, this, &IssuesWidget::slotRefreshIssues);
|
||||
connect(_ui->filterAccount, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &IssuesWidget::slotRefreshIssues);
|
||||
connect(_ui->filterAccount, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &IssuesWidget::slotUpdateFolderFilters);
|
||||
connect(_ui->filterFolder, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &IssuesWidget::slotRefreshIssues);
|
||||
for (auto account : AccountManager::instance()->accounts()) {
|
||||
slotAccountAdded(account.data());
|
||||
}
|
||||
connect(AccountManager::instance(), &AccountManager::accountAdded,
|
||||
this, &IssuesWidget::slotAccountAdded);
|
||||
connect(AccountManager::instance(), &AccountManager::accountRemoved,
|
||||
this, &IssuesWidget::slotAccountRemoved);
|
||||
connect(FolderMan::instance(), &FolderMan::folderListChanged,
|
||||
this, &IssuesWidget::slotUpdateFolderFilters);
|
||||
|
||||
|
||||
// Adjust copyToClipboard() when making changes here!
|
||||
QStringList header;
|
||||
header << tr("Time");
|
||||
header << tr("File");
|
||||
header << tr("Folder");
|
||||
header << tr("Issue");
|
||||
|
||||
int timestampColumnExtra = 0;
|
||||
#ifdef Q_OS_WIN
|
||||
timestampColumnExtra = 20; // font metrics are broken on Windows, see #4721
|
||||
#endif
|
||||
|
||||
_ui->_treeWidget->setHeaderLabels(header);
|
||||
int timestampColumnWidth =
|
||||
ActivityItemDelegate::rowHeight() // icon
|
||||
+ _ui->_treeWidget->fontMetrics().width(ProtocolItem::timeString(QDateTime::currentDateTime()))
|
||||
+ timestampColumnExtra;
|
||||
_ui->_treeWidget->setColumnWidth(0, timestampColumnWidth);
|
||||
_ui->_treeWidget->setColumnWidth(1, 180);
|
||||
_ui->_treeWidget->setColumnCount(4);
|
||||
_ui->_treeWidget->setRootIsDecorated(false);
|
||||
_ui->_treeWidget->setTextElideMode(Qt::ElideMiddle);
|
||||
_ui->_treeWidget->header()->setObjectName("ActivityErrorListHeader");
|
||||
#if defined(Q_OS_MAC)
|
||||
_ui->_treeWidget->setMinimumWidth(400);
|
||||
#endif
|
||||
|
||||
_reenableSorting.setInterval(5000);
|
||||
connect(&_reenableSorting, &QTimer::timeout, this,
|
||||
[this]() { _ui->_treeWidget->setSortingEnabled(true); });
|
||||
|
||||
_ui->_tooManyIssuesWarning->hide();
|
||||
connect(this, &IssuesWidget::issueCountUpdated, this,
|
||||
[this](int count) { _ui->_tooManyIssuesWarning->setVisible(count >= maxIssueCount); });
|
||||
|
||||
_ui->_conflictHelp->hide();
|
||||
_ui->_conflictHelp->setText(
|
||||
tr("There were conflicts. <a href=\"%1\">Check the documentation on how to resolve them.</a>")
|
||||
.arg(Theme::instance()->conflictHelpUrl()));
|
||||
}
|
||||
|
||||
IssuesWidget::~IssuesWidget()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void IssuesWidget::showEvent(QShowEvent *ev)
|
||||
{
|
||||
ConfigFile cfg;
|
||||
cfg.restoreGeometryHeader(_ui->_treeWidget->header());
|
||||
|
||||
// Sorting by section was newly enabled. But if we restore the header
|
||||
// from a state where sorting was disabled, both of these flags will be
|
||||
// false and sorting will be impossible!
|
||||
_ui->_treeWidget->header()->setSectionsClickable(true);
|
||||
_ui->_treeWidget->header()->setSortIndicatorShown(true);
|
||||
|
||||
// Switch back to "first important, then by time" ordering
|
||||
_ui->_treeWidget->sortByColumn(0, Qt::DescendingOrder);
|
||||
|
||||
QWidget::showEvent(ev);
|
||||
}
|
||||
|
||||
void IssuesWidget::hideEvent(QHideEvent *ev)
|
||||
{
|
||||
ConfigFile cfg;
|
||||
cfg.saveGeometryHeader(_ui->_treeWidget->header());
|
||||
QWidget::hideEvent(ev);
|
||||
}
|
||||
|
||||
static bool persistsUntilLocalDiscovery(QTreeWidgetItem *item)
|
||||
{
|
||||
const auto data = ProtocolItem::extraData(item);
|
||||
return data.status == SyncFileItem::Conflict
|
||||
|| (data.status == SyncFileItem::FileIgnored && data.direction == SyncFileItem::Up);
|
||||
}
|
||||
|
||||
void IssuesWidget::cleanItems(const std::function<bool(QTreeWidgetItem *)> &shouldDelete)
|
||||
{
|
||||
_ui->_treeWidget->setSortingEnabled(false);
|
||||
|
||||
// The issue list is a state, clear it and let the next sync fill it
|
||||
// with ignored files and propagation errors.
|
||||
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
||||
for (int cnt = itemCnt - 1; cnt >= 0; cnt--) {
|
||||
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt);
|
||||
if (shouldDelete(item)) {
|
||||
_pathsWithIssues.remove(pathsWithIssuesKey(ProtocolItem::extraData(item)));
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
_ui->_treeWidget->setSortingEnabled(true);
|
||||
|
||||
// update the tabtext
|
||||
emit(issueCountUpdated(_ui->_treeWidget->topLevelItemCount()));
|
||||
}
|
||||
|
||||
void IssuesWidget::addItem(QTreeWidgetItem *item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
int count = _ui->_treeWidget->topLevelItemCount();
|
||||
if (count >= maxIssueCount)
|
||||
return;
|
||||
|
||||
_ui->_treeWidget->setSortingEnabled(false);
|
||||
_reenableSorting.start();
|
||||
|
||||
// Insert item specific errors behind the others
|
||||
int insertLoc = 0;
|
||||
if (!item->text(1).isEmpty()) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (_ui->_treeWidget->topLevelItem(i)->text(1).isEmpty()) {
|
||||
insertLoc = i + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wipe any existing message for the same folder and path
|
||||
auto newData = ProtocolItem::extraData(item);
|
||||
if (_pathsWithIssues.contains(pathsWithIssuesKey(newData))) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto otherItem = _ui->_treeWidget->topLevelItem(i);
|
||||
auto otherData = ProtocolItem::extraData(otherItem);
|
||||
if (otherData.path == newData.path && otherData.folderName == newData.folderName) {
|
||||
delete otherItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ui->_treeWidget->insertTopLevelItem(insertLoc, item);
|
||||
_pathsWithIssues.insert(pathsWithIssuesKey(newData));
|
||||
item->setHidden(!shouldBeVisible(item, currentAccountFilter(), currentFolderFilter()));
|
||||
emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount());
|
||||
}
|
||||
|
||||
void IssuesWidget::slotOpenFile(QTreeWidgetItem *item, int)
|
||||
{
|
||||
QString fileName = item->text(1);
|
||||
if (Folder *folder = ProtocolItem::folder(item)) {
|
||||
// folder->path() always comes back with trailing path
|
||||
QString fullPath = folder->path() + fileName;
|
||||
if (QFile(fullPath).exists()) {
|
||||
showInFileManager(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IssuesWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
|
||||
{
|
||||
if (progress.status() == ProgressInfo::Reconcile) {
|
||||
// Wipe all non-persistent entries - as well as the persistent ones
|
||||
// in cases where a local discovery was done.
|
||||
auto f = FolderMan::instance()->folder(folder);
|
||||
if (!f)
|
||||
return;
|
||||
const auto &engine = f->syncEngine();
|
||||
const auto style = engine.lastLocalDiscoveryStyle();
|
||||
cleanItems([&](QTreeWidgetItem *item) {
|
||||
if (ProtocolItem::extraData(item).folderName != folder)
|
||||
return false;
|
||||
if (style == LocalDiscoveryStyle::FilesystemOnly)
|
||||
return true;
|
||||
if (!persistsUntilLocalDiscovery(item))
|
||||
return true;
|
||||
|
||||
// Definitely wipe the entry if the file no longer exists
|
||||
if (!QFileInfo(f->path() + ProtocolItem::extraData(item).path).exists())
|
||||
return true;
|
||||
|
||||
auto path = QFileInfo(ProtocolItem::extraData(item).path).dir().path().toUtf8();
|
||||
if (path == ".")
|
||||
path.clear();
|
||||
|
||||
return engine.shouldDiscoverLocally(path);
|
||||
});
|
||||
}
|
||||
if (progress.status() == ProgressInfo::Done) {
|
||||
// We keep track very well of pending conflicts.
|
||||
// Inform other components about them.
|
||||
QStringList conflicts;
|
||||
auto tree = _ui->_treeWidget;
|
||||
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
|
||||
auto item = tree->topLevelItem(i);
|
||||
auto data = ProtocolItem::extraData(item);
|
||||
if (data.folderName == folder
|
||||
&& data.status == SyncFileItem::Conflict) {
|
||||
conflicts.append(data.path);
|
||||
}
|
||||
}
|
||||
emit ProgressDispatcher::instance()->folderConflicts(folder, conflicts);
|
||||
|
||||
_ui->_conflictHelp->setHidden(Theme::instance()->conflictHelpUrl().isEmpty() || conflicts.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
void IssuesWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item)
|
||||
{
|
||||
if (!item->showInIssuesTab())
|
||||
return;
|
||||
QTreeWidgetItem *line = ProtocolItem::create(folder, *item);
|
||||
if (!line)
|
||||
return;
|
||||
addItem(line);
|
||||
}
|
||||
|
||||
void IssuesWidget::slotRefreshIssues()
|
||||
{
|
||||
auto tree = _ui->_treeWidget;
|
||||
auto filterFolderAlias = currentFolderFilter();
|
||||
auto filterAccount = currentAccountFilter();
|
||||
|
||||
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
|
||||
auto item = tree->topLevelItem(i);
|
||||
item->setHidden(!shouldBeVisible(item, filterAccount, filterFolderAlias));
|
||||
}
|
||||
|
||||
_ui->_treeWidget->setColumnHidden(2, !filterFolderAlias.isEmpty());
|
||||
}
|
||||
|
||||
void IssuesWidget::slotAccountAdded(AccountState *account)
|
||||
{
|
||||
_ui->filterAccount->addItem(account->account()->displayName(), QVariant::fromValue(account));
|
||||
updateAccountChoiceVisibility();
|
||||
}
|
||||
|
||||
void IssuesWidget::slotAccountRemoved(AccountState *account)
|
||||
{
|
||||
for (int i = _ui->filterAccount->count() - 1; i >= 0; --i) {
|
||||
if (account == _ui->filterAccount->itemData(i).value<AccountState *>())
|
||||
_ui->filterAccount->removeItem(i);
|
||||
}
|
||||
updateAccountChoiceVisibility();
|
||||
}
|
||||
|
||||
void IssuesWidget::slotItemContextMenu(const QPoint &pos)
|
||||
{
|
||||
auto item = _ui->_treeWidget->itemAt(pos);
|
||||
if (!item)
|
||||
return;
|
||||
auto globalPos = _ui->_treeWidget->viewport()->mapToGlobal(pos);
|
||||
ProtocolItem::openContextMenu(globalPos, item, this);
|
||||
}
|
||||
|
||||
void IssuesWidget::updateAccountChoiceVisibility()
|
||||
{
|
||||
bool visible = _ui->filterAccount->count() > 2;
|
||||
_ui->filterAccount->setVisible(visible);
|
||||
_ui->accountLabel->setVisible(visible);
|
||||
slotUpdateFolderFilters();
|
||||
}
|
||||
|
||||
AccountState *IssuesWidget::currentAccountFilter() const
|
||||
{
|
||||
return _ui->filterAccount->currentData().value<AccountState *>();
|
||||
}
|
||||
|
||||
QString IssuesWidget::currentFolderFilter() const
|
||||
{
|
||||
return _ui->filterFolder->currentData().toString();
|
||||
}
|
||||
|
||||
bool IssuesWidget::shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount,
|
||||
const QString &filterFolderAlias) const
|
||||
{
|
||||
bool visible = true;
|
||||
auto data = ProtocolItem::extraData(item);
|
||||
auto status = data.status;
|
||||
visible &= (_ui->showIgnores->isChecked() || status != SyncFileItem::FileIgnored);
|
||||
visible &= (_ui->showWarnings->isChecked()
|
||||
|| (status != SyncFileItem::SoftError
|
||||
&& status != SyncFileItem::Restoration));
|
||||
|
||||
const auto &folderalias = data.folderName;
|
||||
if (filterAccount) {
|
||||
auto folder = FolderMan::instance()->folder(folderalias);
|
||||
visible &= folder && folder->accountState() == filterAccount;
|
||||
}
|
||||
visible &= (filterFolderAlias.isEmpty() || filterFolderAlias == folderalias);
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
void IssuesWidget::slotUpdateFolderFilters()
|
||||
{
|
||||
auto account = _ui->filterAccount->currentData().value<AccountState *>();
|
||||
|
||||
// If there is no account selector, show folders for the single
|
||||
// available account
|
||||
if (_ui->filterAccount->isHidden() && _ui->filterAccount->count() > 1) {
|
||||
account = _ui->filterAccount->itemData(1).value<AccountState *>();
|
||||
}
|
||||
|
||||
if (!account) {
|
||||
_ui->filterFolder->setCurrentIndex(0);
|
||||
}
|
||||
_ui->filterFolder->setEnabled(account != 0);
|
||||
|
||||
for (int i = _ui->filterFolder->count() - 1; i >= 1; --i) {
|
||||
_ui->filterFolder->removeItem(i);
|
||||
}
|
||||
|
||||
// Find all selectable folders while figuring out if we need a folder
|
||||
// selector in the first place
|
||||
bool anyAccountHasMultipleFolders = false;
|
||||
QSet<AccountState *> accountsWithFolders;
|
||||
for (auto folder : FolderMan::instance()->map().values()) {
|
||||
if (accountsWithFolders.contains(folder->accountState()))
|
||||
anyAccountHasMultipleFolders = true;
|
||||
accountsWithFolders.insert(folder->accountState());
|
||||
|
||||
if (folder->accountState() != account)
|
||||
continue;
|
||||
_ui->filterFolder->addItem(folder->shortGuiLocalPath(), folder->alias());
|
||||
}
|
||||
|
||||
// If we don't need the combo box, hide it.
|
||||
_ui->filterFolder->setVisible(anyAccountHasMultipleFolders);
|
||||
_ui->folderLabel->setVisible(anyAccountHasMultipleFolders);
|
||||
|
||||
// If there's no choice, select the only folder and disable
|
||||
if (_ui->filterFolder->count() == 2 && anyAccountHasMultipleFolders) {
|
||||
_ui->filterFolder->setCurrentIndex(1);
|
||||
_ui->filterFolder->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void IssuesWidget::storeSyncIssues(QTextStream &ts)
|
||||
{
|
||||
int topLevelItems = _ui->_treeWidget->topLevelItemCount();
|
||||
|
||||
for (int i = 0; i < topLevelItems; i++) {
|
||||
QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i);
|
||||
if (child->isHidden())
|
||||
continue;
|
||||
ts << right
|
||||
// time stamp
|
||||
<< qSetFieldWidth(20)
|
||||
<< child->data(0, Qt::DisplayRole).toString()
|
||||
// separator
|
||||
<< qSetFieldWidth(0) << ","
|
||||
|
||||
// file name
|
||||
<< qSetFieldWidth(64)
|
||||
<< child->data(1, Qt::DisplayRole).toString()
|
||||
// separator
|
||||
<< qSetFieldWidth(0) << ","
|
||||
|
||||
// folder
|
||||
<< qSetFieldWidth(30)
|
||||
<< child->data(2, Qt::DisplayRole).toString()
|
||||
// separator
|
||||
<< qSetFieldWidth(0) << ","
|
||||
|
||||
// action
|
||||
<< qSetFieldWidth(15)
|
||||
<< child->data(3, Qt::DisplayRole).toString()
|
||||
<< qSetFieldWidth(0)
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
void IssuesWidget::showFolderErrors(const QString &folderAlias)
|
||||
{
|
||||
auto folder = FolderMan::instance()->folder(folderAlias);
|
||||
if (!folder)
|
||||
return;
|
||||
|
||||
_ui->filterAccount->setCurrentIndex(
|
||||
qMax(0, _ui->filterAccount->findData(QVariant::fromValue(folder->accountState()))));
|
||||
_ui->filterFolder->setCurrentIndex(
|
||||
qMax(0, _ui->filterFolder->findData(folderAlias)));
|
||||
_ui->showIgnores->setChecked(false);
|
||||
_ui->showWarnings->setChecked(false);
|
||||
}
|
||||
|
||||
void IssuesWidget::addError(const QString &folderAlias, const QString &message,
|
||||
ErrorCategory category)
|
||||
{
|
||||
auto folder = FolderMan::instance()->folder(folderAlias);
|
||||
if (!folder)
|
||||
return;
|
||||
|
||||
QStringList columns;
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
const QString timeStr = ProtocolItem::timeString(timestamp);
|
||||
const QString longTimeStr = ProtocolItem::timeString(timestamp, QLocale::LongFormat);
|
||||
|
||||
columns << timeStr;
|
||||
columns << ""; // no "File" entry
|
||||
columns << folder->shortGuiLocalPath();
|
||||
columns << message;
|
||||
|
||||
QIcon icon = Theme::instance()->syncStateIcon(SyncResult::Error);
|
||||
|
||||
QTreeWidgetItem *twitem = new ProtocolItem(columns);
|
||||
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
|
||||
twitem->setIcon(0, icon);
|
||||
twitem->setToolTip(0, longTimeStr);
|
||||
twitem->setToolTip(3, message);
|
||||
ProtocolItem::ExtraData data;
|
||||
data.timestamp = timestamp;
|
||||
data.folderName = folderAlias;
|
||||
data.status = SyncFileItem::NormalError;
|
||||
ProtocolItem::setExtraData(twitem, data);
|
||||
|
||||
addItem(twitem);
|
||||
addErrorWidget(twitem, message, category);
|
||||
}
|
||||
|
||||
void IssuesWidget::addErrorWidget(QTreeWidgetItem *item, const QString &message, ErrorCategory category)
|
||||
{
|
||||
QWidget *widget = 0;
|
||||
if (category == ErrorCategory::InsufficientRemoteStorage) {
|
||||
widget = new QWidget;
|
||||
auto layout = new QHBoxLayout;
|
||||
widget->setLayout(layout);
|
||||
|
||||
auto label = new ElidedLabel(message, widget);
|
||||
label->setElideMode(Qt::ElideMiddle);
|
||||
layout->addWidget(label);
|
||||
|
||||
auto button = new QPushButton("Retry all uploads", widget);
|
||||
button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
|
||||
auto folderAlias = ProtocolItem::extraData(item).folderName;
|
||||
connect(button, &QPushButton::clicked,
|
||||
this, [this, folderAlias]() { retryInsufficentRemoteStorageErrors(folderAlias); });
|
||||
layout->addWidget(button);
|
||||
}
|
||||
|
||||
if (widget) {
|
||||
item->setText(3, QString());
|
||||
}
|
||||
_ui->_treeWidget->setItemWidget(item, 3, widget);
|
||||
}
|
||||
|
||||
void IssuesWidget::retryInsufficentRemoteStorageErrors(const QString &folderAlias)
|
||||
{
|
||||
auto folderman = FolderMan::instance();
|
||||
auto folder = folderman->folder(folderAlias);
|
||||
if (!folder)
|
||||
return;
|
||||
|
||||
folder->journalDb()->wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage);
|
||||
folderman->scheduleFolderNext(folder);
|
||||
}
|
||||
}
|
||||
99
src/gui/old-widgets/issueswidget.h
Normal file
99
src/gui/old-widgets/issueswidget.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef ISSUESWIDGET_H
|
||||
#define ISSUESWIDGET_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDateTime>
|
||||
#include <QLocale>
|
||||
#include <QTimer>
|
||||
|
||||
#include "progressdispatcher.h"
|
||||
#include "owncloudgui.h"
|
||||
|
||||
#include "ui_issueswidget.h"
|
||||
|
||||
class QPushButton;
|
||||
|
||||
namespace OCC {
|
||||
class SyncResult;
|
||||
|
||||
namespace Ui {
|
||||
class ProtocolWidget;
|
||||
}
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* @brief The ProtocolWidget class
|
||||
* @ingroup gui
|
||||
*/
|
||||
class IssuesWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit IssuesWidget(QWidget *parent = 0);
|
||||
~IssuesWidget();
|
||||
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
|
||||
|
||||
void storeSyncIssues(QTextStream &ts);
|
||||
void showFolderErrors(const QString &folderAlias);
|
||||
|
||||
public slots:
|
||||
void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
|
||||
void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
|
||||
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
||||
void slotOpenFile(QTreeWidgetItem *item, int);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *);
|
||||
void hideEvent(QHideEvent *);
|
||||
|
||||
signals:
|
||||
void copyToClipboard();
|
||||
void issueCountUpdated(int);
|
||||
|
||||
private slots:
|
||||
void slotRefreshIssues();
|
||||
void slotUpdateFolderFilters();
|
||||
void slotAccountAdded(AccountState *account);
|
||||
void slotAccountRemoved(AccountState *account);
|
||||
void slotItemContextMenu(const QPoint &pos);
|
||||
|
||||
private:
|
||||
void updateAccountChoiceVisibility();
|
||||
AccountState *currentAccountFilter() const;
|
||||
QString currentFolderFilter() const;
|
||||
bool shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount,
|
||||
const QString &filterFolderAlias) const;
|
||||
void cleanItems(const std::function<bool(QTreeWidgetItem *)> &shouldDelete);
|
||||
void addItem(QTreeWidgetItem *item);
|
||||
|
||||
/// Add the special error widget for the category, if any
|
||||
void addErrorWidget(QTreeWidgetItem *item, const QString &message, ErrorCategory category);
|
||||
|
||||
/// Wipes all insufficient remote storgage blacklist entries
|
||||
void retryInsufficentRemoteStorageErrors(const QString &folderAlias);
|
||||
|
||||
/// Each insert disables sorting, this timer reenables it
|
||||
QTimer _reenableSorting;
|
||||
|
||||
/// Optimization: keep track of all folder/paths pairs that have an associated issue
|
||||
QSet<QPair<QString, QString>> _pathsWithIssues;
|
||||
|
||||
Ui::IssuesWidget *_ui;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
198
src/gui/old-widgets/issueswidget.ui
Normal file
198
src/gui/old-widgets/issueswidget.ui
Normal file
@@ -0,0 +1,198 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OCC::IssuesWidget</class>
|
||||
<widget class="QWidget" name="OCC::IssuesWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>580</width>
|
||||
<height>578</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="_headerLabel">
|
||||
<property name="text">
|
||||
<string>List of issues</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QFormLayout" name="accountFolderLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="accountLabel">
|
||||
<property name="text">
|
||||
<string>Account</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="filterAccount">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string><no filter></string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="folderLabel">
|
||||
<property name="text">
|
||||
<string>Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="filterFolder">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string><no filter></string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="showWarnings">
|
||||
<property name="text">
|
||||
<string>Show warnings</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="showIgnores">
|
||||
<property name="text">
|
||||
<string>Show ignored files</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="_treeWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">3</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">4</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="_tooManyIssuesWarning">
|
||||
<property name="text">
|
||||
<string>There were too many issues. Not all will be visible here.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_conflictHelp">
|
||||
<property name="text">
|
||||
<string>There were conflicts. Check the documentation on how to resolve them.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="copyIssuesButton">
|
||||
<property name="toolTip">
|
||||
<string>Copy the issues list to the clipboard.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
169
src/gui/old-widgets/notificationwidget.cpp
Normal file
169
src/gui/old-widgets/notificationwidget.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "notificationwidget.h"
|
||||
#include "QProgressIndicator.h"
|
||||
#include "common/utility.h"
|
||||
#include "common/asserts.h"
|
||||
#include "guiutility.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
#include "ocsjob.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
Q_LOGGING_CATEGORY(lcNotifications, "nextcloud.gui.notifications", QtInfoMsg)
|
||||
|
||||
NotificationWidget::NotificationWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
_progressIndi = new QProgressIndicator(this);
|
||||
_ui.horizontalLayout->addWidget(_progressIndi);
|
||||
}
|
||||
|
||||
void NotificationWidget::setActivity(const Activity &activity)
|
||||
{
|
||||
_myActivity = activity;
|
||||
|
||||
_accountName = activity._accName;
|
||||
ASSERT(!_accountName.isEmpty());
|
||||
|
||||
_ui._subjectLabel->setVisible(!activity._subject.isEmpty());
|
||||
_ui._messageLabel->setVisible(!activity._message.isEmpty());
|
||||
|
||||
_ui._subjectLabel->setText(activity._subject);
|
||||
_ui._messageLabel->setText(activity._message);
|
||||
|
||||
_ui._notifIcon->setPixmap(QPixmap(":/client/resources/bell.svg"));
|
||||
_ui._notifIcon->setMinimumWidth(22);
|
||||
_ui._notifIcon->setMinimumHeight(22);
|
||||
_ui._notifIcon->show();
|
||||
|
||||
QString tText = tr("%1").arg(Utility::timeAgoInWords(activity._dateTime));
|
||||
_ui._timeLabel->setText(tText);
|
||||
|
||||
// always remove the buttons
|
||||
foreach (auto button, _ui._buttonBox->buttons()) {
|
||||
_ui._buttonBox->removeButton(button);
|
||||
}
|
||||
_buttons.clear();
|
||||
|
||||
// open the notification in the browser if there is a link
|
||||
if(!_myActivity._link.isEmpty()){
|
||||
QString buttonText(tr("More information"));
|
||||
QPushButton *openBrowser = _ui._buttonBox->addButton(buttonText, QDialogButtonBox::AcceptRole);
|
||||
openBrowser->setDefault(true);
|
||||
connect(openBrowser, &QAbstractButton::clicked, this, &NotificationWidget::slotOpenBrowserButtonClicked);
|
||||
_buttons.prepend(openBrowser);
|
||||
}
|
||||
|
||||
// display buttons for the links
|
||||
if (activity._links.isEmpty()) {
|
||||
// is there any case where this code is executed?
|
||||
// in case there is no action defined, do a close button.
|
||||
QPushButton *b = _ui._buttonBox->addButton(QDialogButtonBox::Close);
|
||||
b->setDefault(true);
|
||||
connect(b, &QAbstractButton::clicked, this, &NotificationWidget::slotButtonClicked);
|
||||
_buttons.append(b);
|
||||
} else {
|
||||
foreach (auto link, activity._links) {
|
||||
QPushButton *b = _ui._buttonBox->addButton(link._label, QDialogButtonBox::AcceptRole);
|
||||
b->setDefault(link._isPrimary);
|
||||
connect(b, &QAbstractButton::clicked, this, &NotificationWidget::slotButtonClicked);
|
||||
_buttons.append(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Activity NotificationWidget::activity() const
|
||||
{
|
||||
return _myActivity;
|
||||
}
|
||||
|
||||
void NotificationWidget::slotOpenBrowserButtonClicked(){
|
||||
QUrl url(_myActivity._link);
|
||||
Utility::openBrowser(url, this);
|
||||
}
|
||||
|
||||
void NotificationWidget::slotButtonClicked()
|
||||
{
|
||||
QObject *buttonWidget = QObject::sender();
|
||||
int index = -1;
|
||||
if (buttonWidget) {
|
||||
// find the button that was clicked, it has to be in the list
|
||||
// of buttons that were added to the button box before.
|
||||
for (int i = 0; i < _buttons.count(); i++) {
|
||||
if (_buttons.at(i) == buttonWidget) {
|
||||
index = i;
|
||||
}
|
||||
_buttons.at(i)->setEnabled(false);
|
||||
}
|
||||
|
||||
// there is an extra button: 'Open'
|
||||
if(!_myActivity._link.isEmpty())
|
||||
index--;
|
||||
|
||||
// if the button was found, the link must be called
|
||||
if (index > -1 && _myActivity._links.count() == 0) {
|
||||
// no links, that means it was the close button
|
||||
// empty link. Just close and remove the widget.
|
||||
QString doneText = tr("Closing in a few seconds...");
|
||||
_ui._timeLabel->setText(doneText);
|
||||
emit requestCleanupAndBlacklist(_myActivity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > -1 && index < _myActivity._links.count()) {
|
||||
ActivityLink triggeredLink = _myActivity._links.at(index);
|
||||
_actionLabel = triggeredLink._label;
|
||||
|
||||
if (!triggeredLink._link.isEmpty()) {
|
||||
qCInfo(lcNotifications) << "Notification Link: " << triggeredLink._verb << triggeredLink._link;
|
||||
_progressIndi->startAnimation();
|
||||
emit sendNotificationRequest(_accountName, triggeredLink._link, triggeredLink._verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationWidget::slotNotificationRequestFinished(int statusCode)
|
||||
{
|
||||
int i = 0;
|
||||
QString doneText;
|
||||
QLocale locale;
|
||||
|
||||
QString timeStr = locale.toString(QTime::currentTime());
|
||||
|
||||
// the ocs API returns stat code 100 or 200 inside the xml if it succeeded.
|
||||
if (statusCode != OCS_SUCCESS_STATUS_CODE && statusCode != OCS_SUCCESS_STATUS_CODE_V2) {
|
||||
qCWarning(lcNotifications) << "Notification Request to Server failed, leave button visible.";
|
||||
for (i = 0; i < _buttons.count(); i++) {
|
||||
_buttons.at(i)->setEnabled(true);
|
||||
}
|
||||
//: The second parameter is a time, such as 'failed at 09:58pm'
|
||||
doneText = tr("%1 request failed at %2").arg(_actionLabel, timeStr);
|
||||
} else {
|
||||
// the call to the ocs API succeeded.
|
||||
_ui._buttonBox->hide();
|
||||
|
||||
//: The second parameter is a time, such as 'selected at 09:58pm'
|
||||
doneText = tr("'%1' selected at %2").arg(_actionLabel, timeStr);
|
||||
}
|
||||
_ui._timeLabel->setText(doneText);
|
||||
|
||||
_progressIndi->stopAnimation();
|
||||
}
|
||||
}
|
||||
61
src/gui/old-widgets/notificationwidget.h
Normal file
61
src/gui/old-widgets/notificationwidget.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef NOTIFICATIONWIDGET_H
|
||||
#define NOTIFICATIONWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "activitydata.h"
|
||||
|
||||
#include "ui_notificationwidget.h"
|
||||
|
||||
#define NOTIFICATION_WIDGET_CLOSE_AFTER_MILLISECS 4800
|
||||
|
||||
class QProgressIndicator;
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class NotificationWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NotificationWidget(QWidget *parent = 0);
|
||||
|
||||
bool readyToClose();
|
||||
Activity activity() const;
|
||||
|
||||
signals:
|
||||
void sendNotificationRequest(const QString &, const QString &link, const QByteArray &verb);
|
||||
void requestCleanupAndBlacklist(const Activity &activity);
|
||||
|
||||
public slots:
|
||||
void setActivity(const Activity &activity);
|
||||
void slotNotificationRequestFinished(int statusCode);
|
||||
|
||||
private slots:
|
||||
void slotButtonClicked();
|
||||
void slotOpenBrowserButtonClicked();
|
||||
|
||||
private:
|
||||
Ui_NotificationWidget _ui;
|
||||
Activity _myActivity;
|
||||
QList<QPushButton *> _buttons;
|
||||
QString _accountName;
|
||||
QProgressIndicator *_progressIndi;
|
||||
QString _actionLabel;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NOTIFICATIONWIDGET_H
|
||||
185
src/gui/old-widgets/notificationwidget.ui
Normal file
185
src/gui/old-widgets/notificationwidget.ui
Normal file
@@ -0,0 +1,185 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NotificationWidget</class>
|
||||
<widget class="QWidget" name="NotificationWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>725</width>
|
||||
<height>129</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>26</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="labelsHorizontalLayout" stretch="1,1">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="_notifIcon">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>../../resources/bell.svg</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_subjectLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lorem ipsum dolor sit amet</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_messageLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod temporm </string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>28</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Preferred</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>28</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_timeLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="_buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
330
src/gui/old-widgets/protocolwidget.cpp
Normal file
330
src/gui/old-widgets/protocolwidget.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "protocolwidget.h"
|
||||
#include "configfile.h"
|
||||
#include "syncresult.h"
|
||||
#include "logger.h"
|
||||
#include "theme.h"
|
||||
#include "folderman.h"
|
||||
#include "syncfileitem.h"
|
||||
#include "folder.h"
|
||||
#include "openfilemanager.h"
|
||||
#include "activityitemdelegate.h"
|
||||
#include "guiutility.h"
|
||||
#include "accountstate.h"
|
||||
|
||||
#include "ui_protocolwidget.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
Q_DECLARE_METATYPE(OCC::ProtocolItem::ExtraData)
|
||||
|
||||
namespace OCC {
|
||||
|
||||
QString ProtocolItem::timeString(QDateTime dt, QLocale::FormatType format)
|
||||
{
|
||||
const QLocale loc = QLocale::system();
|
||||
QString dtFormat = loc.dateTimeFormat(format);
|
||||
static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
|
||||
dtFormat.replace(re, "\\1:mm:ss");
|
||||
return loc.toString(dt, dtFormat);
|
||||
}
|
||||
|
||||
ProtocolItem::ExtraData ProtocolItem::extraData(const QTreeWidgetItem *item)
|
||||
{
|
||||
return item->data(0, Qt::UserRole).value<ExtraData>();
|
||||
}
|
||||
|
||||
void ProtocolItem::setExtraData(QTreeWidgetItem *item, const ExtraData &data)
|
||||
{
|
||||
item->setData(0, Qt::UserRole, QVariant::fromValue(data));
|
||||
}
|
||||
|
||||
ProtocolItem *ProtocolItem::create(const QString &folder, const SyncFileItem &item)
|
||||
{
|
||||
auto f = FolderMan::instance()->folder(folder);
|
||||
if (!f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QStringList columns;
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
const QString timeStr = timeString(timestamp);
|
||||
const QString longTimeStr = timeString(timestamp, QLocale::LongFormat);
|
||||
|
||||
columns << timeStr;
|
||||
columns << Utility::fileNameForGuiUse(item._originalFile);
|
||||
columns << f->shortGuiLocalPath();
|
||||
|
||||
// If the error string is set, it's prefered because it is a useful user message.
|
||||
QString message = item._errorString;
|
||||
if (message.isEmpty()) {
|
||||
message = Progress::asResultString(item);
|
||||
}
|
||||
columns << message;
|
||||
|
||||
QIcon icon;
|
||||
if (item._status == SyncFileItem::NormalError
|
||||
|| item._status == SyncFileItem::FatalError
|
||||
|| item._status == SyncFileItem::DetailError
|
||||
|| item._status == SyncFileItem::BlacklistedError) {
|
||||
icon = Theme::instance()->syncStateIcon(SyncResult::Error);
|
||||
} else if (Progress::isWarningKind(item._status)) {
|
||||
icon = Theme::instance()->syncStateIcon(SyncResult::Problem);
|
||||
}
|
||||
|
||||
if (ProgressInfo::isSizeDependent(item)) {
|
||||
columns << Utility::octetsToString(item._size);
|
||||
}
|
||||
|
||||
ProtocolItem *twitem = new ProtocolItem(columns);
|
||||
// Warning: The data and tooltips on the columns define an implicit
|
||||
// interface and can only be changed with care.
|
||||
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
|
||||
twitem->setIcon(0, icon);
|
||||
twitem->setToolTip(0, longTimeStr);
|
||||
twitem->setToolTip(1, item._file);
|
||||
twitem->setToolTip(3, message);
|
||||
ProtocolItem::ExtraData data;
|
||||
data.timestamp = timestamp;
|
||||
data.path = item._file;
|
||||
data.folderName = folder;
|
||||
data.status = item._status;
|
||||
data.size = item._size;
|
||||
data.direction = item._direction;
|
||||
ProtocolItem::setExtraData(twitem, data);
|
||||
return twitem;
|
||||
}
|
||||
|
||||
SyncJournalFileRecord ProtocolItem::syncJournalRecord(QTreeWidgetItem *item)
|
||||
{
|
||||
SyncJournalFileRecord rec;
|
||||
auto f = folder(item);
|
||||
if (!f)
|
||||
return rec;
|
||||
f->journalDb()->getFileRecord(extraData(item).path, &rec);
|
||||
return rec;
|
||||
}
|
||||
|
||||
Folder *ProtocolItem::folder(QTreeWidgetItem *item)
|
||||
{
|
||||
return FolderMan::instance()->folder(extraData(item).folderName);
|
||||
}
|
||||
|
||||
void ProtocolItem::openContextMenu(QPoint globalPos, QTreeWidgetItem *item, QWidget *parent)
|
||||
{
|
||||
auto f = folder(item);
|
||||
if (!f)
|
||||
return;
|
||||
AccountPtr account = f->accountState()->account();
|
||||
auto rec = syncJournalRecord(item);
|
||||
// rec might not be valid
|
||||
|
||||
auto menu = new QMenu(parent);
|
||||
|
||||
if (rec.isValid()) {
|
||||
// "Open in Browser" action
|
||||
auto openInBrowser = menu->addAction(ProtocolWidget::tr("Open in browser"));
|
||||
QObject::connect(openInBrowser, &QAction::triggered, parent, [parent, account, rec]() {
|
||||
fetchPrivateLinkUrl(account, rec._path, rec.numericFileId(), parent,
|
||||
[parent](const QString &url) {
|
||||
Utility::openBrowser(url, parent);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// More actions will be conditionally added to the context menu here later
|
||||
|
||||
if (menu->actions().isEmpty()) {
|
||||
delete menu;
|
||||
return;
|
||||
}
|
||||
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->popup(globalPos);
|
||||
}
|
||||
|
||||
bool ProtocolItem::operator<(const QTreeWidgetItem &other) const
|
||||
{
|
||||
int column = treeWidget()->sortColumn();
|
||||
if (column == 0) {
|
||||
// Items with empty "File" column are larger than others,
|
||||
// otherwise sort by time (this uses lexicographic ordering)
|
||||
return std::forward_as_tuple(text(1).isEmpty(), extraData(this).timestamp)
|
||||
< std::forward_as_tuple(other.text(1).isEmpty(), extraData(&other).timestamp);
|
||||
} else if (column == 4) {
|
||||
return extraData(this).size < extraData(&other).size;
|
||||
}
|
||||
|
||||
return QTreeWidgetItem::operator<(other);
|
||||
}
|
||||
|
||||
ProtocolWidget::ProtocolWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, _ui(new Ui::ProtocolWidget)
|
||||
{
|
||||
_ui->setupUi(this);
|
||||
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::itemCompleted,
|
||||
this, &ProtocolWidget::slotItemCompleted);
|
||||
|
||||
connect(_ui->_treeWidget, &QTreeWidget::itemActivated, this, &ProtocolWidget::slotOpenFile);
|
||||
|
||||
_ui->_treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(_ui->_treeWidget, &QTreeWidget::customContextMenuRequested, this, &ProtocolWidget::slotItemContextMenu);
|
||||
|
||||
// Adjust copyToClipboard() when making changes here!
|
||||
QStringList header;
|
||||
header << tr("Time");
|
||||
header << tr("File");
|
||||
header << tr("Folder");
|
||||
header << tr("Action");
|
||||
header << tr("Size");
|
||||
|
||||
int timestampColumnExtra = 0;
|
||||
#ifdef Q_OS_WIN
|
||||
timestampColumnExtra = 20; // font metrics are broken on Windows, see #4721
|
||||
#endif
|
||||
|
||||
_ui->_treeWidget->setHeaderLabels(header);
|
||||
int timestampColumnWidth =
|
||||
_ui->_treeWidget->fontMetrics().width(ProtocolItem::timeString(QDateTime::currentDateTime()))
|
||||
+ timestampColumnExtra;
|
||||
_ui->_treeWidget->setColumnWidth(0, timestampColumnWidth);
|
||||
_ui->_treeWidget->setColumnWidth(1, 180);
|
||||
_ui->_treeWidget->setColumnCount(5);
|
||||
_ui->_treeWidget->setRootIsDecorated(false);
|
||||
_ui->_treeWidget->setTextElideMode(Qt::ElideMiddle);
|
||||
_ui->_treeWidget->header()->setObjectName("ActivityListHeader");
|
||||
#if defined(Q_OS_MAC)
|
||||
_ui->_treeWidget->setMinimumWidth(400);
|
||||
#endif
|
||||
_ui->_headerLabel->setText(tr("Local sync protocol"));
|
||||
|
||||
QPushButton *copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
|
||||
copyBtn->setToolTip(tr("Copy the activity list to the clipboard."));
|
||||
copyBtn->setEnabled(true);
|
||||
connect(copyBtn, &QAbstractButton::clicked, this, &ProtocolWidget::copyToClipboard);
|
||||
}
|
||||
|
||||
ProtocolWidget::~ProtocolWidget()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void ProtocolWidget::showEvent(QShowEvent *ev)
|
||||
{
|
||||
ConfigFile cfg;
|
||||
cfg.restoreGeometryHeader(_ui->_treeWidget->header());
|
||||
|
||||
// Sorting by section was newly enabled. But if we restore the header
|
||||
// from a state where sorting was disabled, both of these flags will be
|
||||
// false and sorting will be impossible!
|
||||
_ui->_treeWidget->header()->setSectionsClickable(true);
|
||||
_ui->_treeWidget->header()->setSortIndicatorShown(true);
|
||||
|
||||
// Switch back to "by time" ordering
|
||||
_ui->_treeWidget->sortByColumn(0, Qt::DescendingOrder);
|
||||
|
||||
QWidget::showEvent(ev);
|
||||
}
|
||||
|
||||
void ProtocolWidget::hideEvent(QHideEvent *ev)
|
||||
{
|
||||
ConfigFile cfg;
|
||||
cfg.saveGeometryHeader(_ui->_treeWidget->header());
|
||||
QWidget::hideEvent(ev);
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotItemContextMenu(const QPoint &pos)
|
||||
{
|
||||
auto item = _ui->_treeWidget->itemAt(pos);
|
||||
if (!item)
|
||||
return;
|
||||
auto globalPos = _ui->_treeWidget->viewport()->mapToGlobal(pos);
|
||||
ProtocolItem::openContextMenu(globalPos, item, this);
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotOpenFile(QTreeWidgetItem *item, int)
|
||||
{
|
||||
QString fileName = item->text(1);
|
||||
if (Folder *folder = ProtocolItem::folder(item)) {
|
||||
// folder->path() always comes back with trailing path
|
||||
QString fullPath = folder->path() + fileName;
|
||||
if (QFile(fullPath).exists()) {
|
||||
showInFileManager(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item)
|
||||
{
|
||||
if (!item->showInProtocolTab())
|
||||
return;
|
||||
QTreeWidgetItem *line = ProtocolItem::create(folder, *item);
|
||||
if (line) {
|
||||
// Limit the number of items
|
||||
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
||||
while (itemCnt > 2000) {
|
||||
delete _ui->_treeWidget->takeTopLevelItem(itemCnt - 1);
|
||||
itemCnt--;
|
||||
}
|
||||
_ui->_treeWidget->insertTopLevelItem(0, line);
|
||||
}
|
||||
}
|
||||
|
||||
void ProtocolWidget::storeSyncActivity(QTextStream &ts)
|
||||
{
|
||||
int topLevelItems = _ui->_treeWidget->topLevelItemCount();
|
||||
|
||||
for (int i = 0; i < topLevelItems; i++) {
|
||||
QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i);
|
||||
ts << right
|
||||
// time stamp
|
||||
<< qSetFieldWidth(20)
|
||||
<< child->data(0, Qt::DisplayRole).toString()
|
||||
// separator
|
||||
<< qSetFieldWidth(0) << ","
|
||||
|
||||
// file name
|
||||
<< qSetFieldWidth(64)
|
||||
<< child->data(1, Qt::DisplayRole).toString()
|
||||
// separator
|
||||
<< qSetFieldWidth(0) << ","
|
||||
|
||||
// folder
|
||||
<< qSetFieldWidth(30)
|
||||
<< child->data(2, Qt::DisplayRole).toString()
|
||||
// separator
|
||||
<< qSetFieldWidth(0) << ","
|
||||
|
||||
// action
|
||||
<< qSetFieldWidth(15)
|
||||
<< child->data(3, Qt::DisplayRole).toString()
|
||||
// separator
|
||||
<< qSetFieldWidth(0) << ","
|
||||
|
||||
// size
|
||||
<< qSetFieldWidth(10)
|
||||
<< child->data(4, Qt::DisplayRole).toString()
|
||||
<< qSetFieldWidth(0)
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
112
src/gui/old-widgets/protocolwidget.h
Normal file
112
src/gui/old-widgets/protocolwidget.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef PROTOCOLWIDGET_H
|
||||
#define PROTOCOLWIDGET_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDateTime>
|
||||
#include <QLocale>
|
||||
|
||||
#include "progressdispatcher.h"
|
||||
#include "owncloudgui.h"
|
||||
|
||||
#include "ui_protocolwidget.h"
|
||||
|
||||
class QPushButton;
|
||||
|
||||
namespace OCC {
|
||||
class SyncResult;
|
||||
|
||||
namespace Ui {
|
||||
class ProtocolWidget;
|
||||
}
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* The items used in the protocol and issue QTreeWidget
|
||||
*
|
||||
* Special sorting: It allows items for global entries to be moved to the top if the
|
||||
* sorting section is the "Time" column.
|
||||
*/
|
||||
class ProtocolItem : public QTreeWidgetItem
|
||||
{
|
||||
public:
|
||||
using QTreeWidgetItem::QTreeWidgetItem;
|
||||
|
||||
// Shared with IssueWidget
|
||||
static ProtocolItem *create(const QString &folder, const SyncFileItem &item);
|
||||
static QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat);
|
||||
|
||||
struct ExtraData
|
||||
{
|
||||
ExtraData()
|
||||
: status(SyncFileItem::NoStatus)
|
||||
, direction(SyncFileItem::None)
|
||||
{
|
||||
}
|
||||
|
||||
QString path;
|
||||
QString folderName;
|
||||
QDateTime timestamp;
|
||||
quint64 size = 0;
|
||||
SyncFileItem::Status status BITFIELD(4);
|
||||
SyncFileItem::Direction direction BITFIELD(3);
|
||||
};
|
||||
|
||||
static ExtraData extraData(const QTreeWidgetItem *item);
|
||||
static void setExtraData(QTreeWidgetItem *item, const ExtraData &data);
|
||||
|
||||
static SyncJournalFileRecord syncJournalRecord(QTreeWidgetItem *item);
|
||||
static Folder *folder(QTreeWidgetItem *item);
|
||||
|
||||
static void openContextMenu(QPoint globalPos, QTreeWidgetItem *item, QWidget *parent);
|
||||
|
||||
private:
|
||||
bool operator<(const QTreeWidgetItem &other) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ProtocolWidget class
|
||||
* @ingroup gui
|
||||
*/
|
||||
class ProtocolWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProtocolWidget(QWidget *parent = 0);
|
||||
~ProtocolWidget();
|
||||
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
|
||||
|
||||
void storeSyncActivity(QTextStream &ts);
|
||||
|
||||
public slots:
|
||||
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
||||
void slotOpenFile(QTreeWidgetItem *item, int);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *);
|
||||
void hideEvent(QHideEvent *);
|
||||
|
||||
private slots:
|
||||
void slotItemContextMenu(const QPoint &pos);
|
||||
|
||||
signals:
|
||||
void copyToClipboard();
|
||||
|
||||
private:
|
||||
Ui::ProtocolWidget *_ui;
|
||||
};
|
||||
}
|
||||
#endif // PROTOCOLWIDGET_H
|
||||
73
src/gui/old-widgets/protocolwidget.ui
Normal file
73
src/gui/old-widgets/protocolwidget.ui
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OCC::ProtocolWidget</class>
|
||||
<widget class="QWidget" name="OCC::ProtocolWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>612</width>
|
||||
<height>515</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="_headerLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QTreeWidget" name="_treeWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">3</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">4</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -34,10 +34,12 @@
|
||||
#include "accountmanager.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "quotainfo.h"
|
||||
#ifdef WITH_LIBCLOUDPROVIDERS
|
||||
#include "cloudproviders/cloudprovidermanager.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
@@ -55,6 +57,12 @@
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include "vfs_windows.h"
|
||||
#endif
|
||||
|
||||
namespace OCC {
|
||||
|
||||
const char propertyAccountC[] = "oc_account";
|
||||
@@ -78,6 +86,8 @@ ownCloudGui::ownCloudGui(Application *parent)
|
||||
#endif
|
||||
, _recentActionsMenu(0)
|
||||
, _qdbusmenuWorkaround(false)
|
||||
,_folderOpenActionMapper(new QSignalMapper(this))
|
||||
,_recentItemsMapper(new QSignalMapper(this))
|
||||
, _app(parent)
|
||||
{
|
||||
_tray = new Systray();
|
||||
@@ -94,6 +104,13 @@ ownCloudGui::ownCloudGui(Application *parent)
|
||||
|
||||
_tray->show();
|
||||
|
||||
/* use a signal mapper to map the open requests to the alias names */
|
||||
connect(_folderOpenActionMapper, SIGNAL(mapped(QString)),
|
||||
this, SLOT(slotFolderOpenAction(QString)));
|
||||
|
||||
connect(_recentItemsMapper, SIGNAL(mapped(QString)),
|
||||
this, SLOT(slotOpenPath(QString)));
|
||||
|
||||
ProgressDispatcher *pd = ProgressDispatcher::instance();
|
||||
connect(pd, &ProgressDispatcher::progressInfo, this,
|
||||
&ownCloudGui::slotUpdateProgress);
|
||||
@@ -190,11 +207,12 @@ void ownCloudGui::slotTrayClicked(QSystemTrayIcon::ActivationReason reason)
|
||||
// or SSL error dialog also comes to front.
|
||||
}
|
||||
|
||||
void ownCloudGui::slotSyncStateChange(Folder *folder)
|
||||
void ownCloudGui::slotSyncStateChange()
|
||||
{
|
||||
slotComputeOverallSyncStatus();
|
||||
updateContextMenuNeeded();
|
||||
|
||||
Folder * folder = FolderMan::instance()->currentSyncFolder();
|
||||
if (!folder) {
|
||||
return; // Valid, just a general GUI redraw was needed.
|
||||
}
|
||||
@@ -253,7 +271,8 @@ void ownCloudGui::slotComputeOverallSyncStatus()
|
||||
auto setStatusText = [&](const QString &text) {
|
||||
// Don't overwrite the status if we're currently syncing
|
||||
if (FolderMan::instance()->currentSyncFolder())
|
||||
return;
|
||||
if(FolderMan::instance()->currentSyncFolder()->isBusy())
|
||||
return;
|
||||
_actionStatus->setText(text);
|
||||
};
|
||||
|
||||
@@ -322,7 +341,7 @@ void ownCloudGui::slotComputeOverallSyncStatus()
|
||||
|
||||
SyncResult::Status overallStatus = SyncResult::Undefined;
|
||||
bool hasUnresolvedConflicts = false;
|
||||
FolderMan::trayOverallStatus(map.values(), &overallStatus, &hasUnresolvedConflicts);
|
||||
FolderMan::trayOverallStatus(&overallStatus, &hasUnresolvedConflicts);
|
||||
|
||||
// If the sync succeeded but there are unresolved conflicts,
|
||||
// show the problem icon!
|
||||
@@ -829,6 +848,20 @@ void ownCloudGui::slotNavigationAppsFetched(const QJsonDocument &reply, int stat
|
||||
}
|
||||
}
|
||||
|
||||
// TODO see pull #523
|
||||
auto accountList = AccountManager::instance()->accounts();
|
||||
if(accountList.size() > 1){
|
||||
// the list of apps will be displayed under the account that it belongs to
|
||||
foreach (QMenu *accountMenu, _accountMenus) {
|
||||
if(accountMenu->title() == account->account()->displayName()){
|
||||
buildNavigationAppsMenu(account, accountMenu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if(accountList.size() == 1){
|
||||
buildNavigationAppsMenu(account, _contextMenu.data());
|
||||
}
|
||||
|
||||
if(QObject *accountMenuObj = qvariant_cast<QObject*>(sender()->property(propertyMenuC))){
|
||||
if(QMenu *accountMenu = dynamic_cast<QMenu*>(accountMenuObj))
|
||||
buildNavigationAppsMenu(account, accountMenu);
|
||||
@@ -973,6 +1006,14 @@ void ownCloudGui::slotLogout()
|
||||
foreach (const auto &ai, list) {
|
||||
ai->signOutByUi();
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
VfsWindows::instance()->unmount();
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
VfsMacController::instance()->unmount();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ownCloudGui::slotUnpauseAllFolders()
|
||||
@@ -1185,4 +1226,31 @@ void ownCloudGui::slotRemoveDestroyedShareDialogs()
|
||||
}
|
||||
|
||||
|
||||
QuotaAction::QuotaAction(QAction* action, AccountStatePtr accountState) :
|
||||
QObject(action),
|
||||
_accountState(accountState),
|
||||
_action(action)
|
||||
{
|
||||
_quotaInfo = new QuotaInfo(_accountState.data(), this);
|
||||
|
||||
connect(_quotaInfo, SIGNAL(quotaUpdated(qint64,qint64)),
|
||||
this, SLOT(slotUpdateQuota(qint64,qint64)));
|
||||
_quotaInfo->setActive(true);
|
||||
}
|
||||
|
||||
void QuotaAction::slotUpdateQuota(qint64 total, qint64 used) {
|
||||
if( total > 0 ) {
|
||||
QString totalStr = Utility::octetsToString(total);
|
||||
QString usedStr = Utility::octetsToString(used);
|
||||
_action->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
|
||||
} else {
|
||||
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
|
||||
if (total == 0 || total == -1) {
|
||||
_action->setText("Quota not available");
|
||||
} else {
|
||||
QString usedStr = Utility::octetsToString(used);
|
||||
_action->setText(tr("%1 in use").arg(usedStr));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end namespace
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "progressdispatcher.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalMapper>
|
||||
#include <QPointer>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
@@ -39,6 +40,7 @@ class ShareDialog;
|
||||
class Application;
|
||||
class LogBrowser;
|
||||
class AccountState;
|
||||
class QuotaInfo;
|
||||
|
||||
enum class ShareDialogStartPage {
|
||||
UsersAndGroups,
|
||||
@@ -89,7 +91,7 @@ public slots:
|
||||
void slotShowSettings();
|
||||
void slotShowSyncProtocol();
|
||||
void slotShutdown();
|
||||
void slotSyncStateChange(Folder *);
|
||||
void slotSyncStateChange();
|
||||
void slotTrayClicked(QSystemTrayIcon::ActivationReason reason);
|
||||
void slotToggleLogBrowser();
|
||||
void slotOpenOwnCloud();
|
||||
@@ -166,9 +168,32 @@ private:
|
||||
QMap<AccountStatePtr, QJsonArray> _navApps;
|
||||
|
||||
QList<QAction *> _recentItemsActions;
|
||||
|
||||
QSignalMapper *_folderOpenActionMapper;
|
||||
QSignalMapper *_recentItemsMapper;
|
||||
|
||||
Application *_app;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Quota info menu item
|
||||
* @ingroup gui
|
||||
*/
|
||||
class QuotaAction : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QuotaAction(QAction *action, AccountStatePtr accountState);
|
||||
|
||||
private slots:
|
||||
void slotUpdateQuota(qint64, qint64);
|
||||
|
||||
private:
|
||||
AccountStatePtr _accountState;
|
||||
QAction *_action;
|
||||
QuotaInfo *_quotaInfo;
|
||||
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
#endif // OWNCLOUDGUI_H
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user