mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-29 12:19:03 +03:00
Merge remote-tracking branch 'origin/1.7'
Conflicts: src/CMakeLists.txt src/cmd/cmd.cpp src/gui/socketapi.h src/libsync/syncengine.h test/CMakeLists.txt
This commit is contained in:
commit
7ae0338f5c
132 changed files with 168481 additions and 11619 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -2,4 +2,4 @@
|
||||||
.gitignore export-ignore
|
.gitignore export-ignore
|
||||||
.gitattributes export-ignore
|
.gitattributes export-ignore
|
||||||
.commit-template export-ignore
|
.commit-template export-ignore
|
||||||
/binary export-ignore
|
binary/ export-ignore
|
||||||
|
|
|
@ -122,6 +122,12 @@ find_package(Sphinx)
|
||||||
find_package(PdfLatex)
|
find_package(PdfLatex)
|
||||||
|
|
||||||
|
|
||||||
|
find_package(SQLite3 3.8.0 REQUIRED)
|
||||||
|
# On some OS, we want to use our own, not the system sqlite
|
||||||
|
if (USE_OUR_OWN_SQLITE3)
|
||||||
|
include_directories(BEFORE ${SQLITE3_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ set( MIRALL_VERSION_PATCH 0 )
|
||||||
set( MIRALL_SOVERSION 0 )
|
set( MIRALL_SOVERSION 0 )
|
||||||
|
|
||||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||||
set( MIRALL_VERSION_SUFFIX "beta1") #e.g. beta1, beta2, rc1
|
set( MIRALL_VERSION_SUFFIX "beta4") #e.g. beta1, beta2, rc1
|
||||||
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||||
|
|
||||||
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||||
|
|
|
@ -23,7 +23,7 @@ identity="$3"
|
||||||
prjfile=$build_path/admin/osx/macosx.pkgproj
|
prjfile=$build_path/admin/osx/macosx.pkgproj
|
||||||
|
|
||||||
# The name of the installer package
|
# The name of the installer package
|
||||||
installer="ownCloud-@MIRALL_VERSION_FULL@@MIRALL_VERSION_SUFFIX@"
|
installer="@APPLICATION_NAME@-@MIRALL_VERSION_FULL@@MIRALL_VERSION_SUFFIX@"
|
||||||
installer_file="$installer.pkg"
|
installer_file="$installer.pkg"
|
||||||
installer_file_tar="$installer.pkg.tar"
|
installer_file_tar="$installer.pkg.tar"
|
||||||
installer_file_tar_bz2="$installer.pkg.tar.bz2"
|
installer_file_tar_bz2="$installer.pkg.tar.bz2"
|
||||||
|
@ -62,9 +62,9 @@ fi
|
||||||
|
|
||||||
# Sparkle wants a tbz, it cannot install raw pkg
|
# Sparkle wants a tbz, it cannot install raw pkg
|
||||||
cd $install_path
|
cd $install_path
|
||||||
tar cf $installer_file_tar $installer_file
|
tar cf "$installer_file_tar" "$installer_file"
|
||||||
bzip2 -9 $installer_file_tar
|
bzip2 -9 "$installer_file_tar"
|
||||||
mv $installer_file_tar_bz2 $installer_file_tbz
|
mv "$installer_file_tar_bz2" "$installer_file_tbz"
|
||||||
rc=$?
|
rc=$?
|
||||||
if [ $rc == 0 ]; then
|
if [ $rc == 0 ]; then
|
||||||
echo "Successfully created $installer_file"
|
echo "Successfully created $installer_file"
|
||||||
|
|
334
admin/osx/macdeployqt.py
Executable file
334
admin/osx/macdeployqt.py
Executable file
|
@ -0,0 +1,334 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# This file is part of ownCloud.
|
||||||
|
# It was inspired in large part by the macdeploy script in Clementine
|
||||||
|
# and Tomahawk
|
||||||
|
#
|
||||||
|
# ownCloud 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; version 2 of the License.
|
||||||
|
#
|
||||||
|
# ownCLoud 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 General Public License
|
||||||
|
# along with ownCloud. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import commands
|
||||||
|
import sys
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
def QueryQMake(attrib):
|
||||||
|
return subprocess.check_output([qmake_path, '-query', attrib]).rstrip('\n')
|
||||||
|
|
||||||
|
FRAMEWORK_SEARCH_PATH=[
|
||||||
|
'/Library/Frameworks',
|
||||||
|
os.path.join(os.environ['HOME'], 'Library/Frameworks')
|
||||||
|
]
|
||||||
|
|
||||||
|
LIBRARY_SEARCH_PATH=['/usr/local/lib', '.']
|
||||||
|
|
||||||
|
QT_PLUGINS = [
|
||||||
|
'accessible/libqtaccessiblewidgets.dylib',
|
||||||
|
'sqldrivers/libqsqlite.dylib',
|
||||||
|
'platforms/libqcocoa.dylib',
|
||||||
|
'imageformats/libqgif.dylib',
|
||||||
|
'imageformats/libqico.dylib',
|
||||||
|
'imageformats/libqjpeg.dylib',
|
||||||
|
'imageformats/libqsvg.dylib',
|
||||||
|
'imageformats/libqmng.dylib',
|
||||||
|
]
|
||||||
|
|
||||||
|
QT_PLUGINS_SEARCH_PATH=[
|
||||||
|
# os.path.join(os.environ['QTDIR'], 'plugins'),
|
||||||
|
'/usr/local/Cellar/qt/5.2.1/plugins',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CouldNotFindQtPluginErrorFindFrameworkError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InstallNameToolError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CouldNotFindQtPluginError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CouldNotFindScriptPluginError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CouldNotFindFrameworkError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print 'Usage: %s <bundle.app> <path-to-qmake>' % sys.argv[0]
|
||||||
|
exit()
|
||||||
|
|
||||||
|
def is_exe(fpath):
|
||||||
|
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
||||||
|
|
||||||
|
bundle_dir = sys.argv[1]
|
||||||
|
qmake_path = sys.argv[2]
|
||||||
|
|
||||||
|
bundle_name = os.path.basename(bundle_dir).split('.')[0]
|
||||||
|
|
||||||
|
commands = []
|
||||||
|
|
||||||
|
binary_dir = os.path.join(bundle_dir, 'Contents', 'MacOS')
|
||||||
|
frameworks_dir = os.path.join(bundle_dir, 'Contents', 'Frameworks')
|
||||||
|
commands.append(['mkdir', '-p', frameworks_dir])
|
||||||
|
resources_dir = os.path.join(bundle_dir, 'Contents', 'Resources')
|
||||||
|
commands.append(['mkdir', '-p', resources_dir])
|
||||||
|
plugins_dir = os.path.join(bundle_dir, 'Contents', 'PlugIns')
|
||||||
|
binaries = [i for i in glob(os.path.join(bundle_dir, 'Contents', 'MacOS', "*")) if is_exe(i)];
|
||||||
|
|
||||||
|
|
||||||
|
fixed_libraries = []
|
||||||
|
fixed_frameworks = []
|
||||||
|
|
||||||
|
def WriteQtConf():
|
||||||
|
print "Writing qt.conf..."
|
||||||
|
with open(os.path.join(resources_dir, 'qt.conf'), 'w') as f:
|
||||||
|
f.write("[Paths]\nPlugins = PlugIns\n");
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def GetBrokenLibraries(binary):
|
||||||
|
#print "Checking libs for binary: %s" % binary
|
||||||
|
output = subprocess.Popen(['otool', '-L', binary], stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
broken_libs = {
|
||||||
|
'frameworks': [],
|
||||||
|
'libs': []}
|
||||||
|
for line in [x.split(' ')[0].lstrip() for x in output.split('\n')[1:]]:
|
||||||
|
#print "Checking line: %s" % line
|
||||||
|
if not line: # skip empty lines
|
||||||
|
continue
|
||||||
|
if os.path.basename(binary) == os.path.basename(line):
|
||||||
|
#print "mnope %s-%s" % (os.path.basename(binary), os.path.basename(line))
|
||||||
|
continue
|
||||||
|
if re.match(r'^\s*/System/', line):
|
||||||
|
continue # System framework
|
||||||
|
elif re.match(r'^\s*/usr/lib/', line):
|
||||||
|
#print "unix style system lib"
|
||||||
|
continue # unix style system library
|
||||||
|
elif re.match(r'Breakpad', line):
|
||||||
|
continue # Manually added by cmake.
|
||||||
|
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line):
|
||||||
|
# Potentially already fixed library
|
||||||
|
if '.framework' in line:
|
||||||
|
relative_path = os.path.join(*line.split('/')[3:])
|
||||||
|
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
||||||
|
broken_libs['frameworks'].append(relative_path)
|
||||||
|
else:
|
||||||
|
relative_path = os.path.join(*line.split('/')[1:])
|
||||||
|
#print "RELPATH %s %s" % (relative_path, os.path.join(binary_dir, relative_path))
|
||||||
|
if not os.path.exists(os.path.join(binary_dir, relative_path)):
|
||||||
|
broken_libs['libs'].append(relative_path)
|
||||||
|
elif re.search(r'\w+\.framework', line):
|
||||||
|
broken_libs['frameworks'].append(line)
|
||||||
|
else:
|
||||||
|
broken_libs['libs'].append(line)
|
||||||
|
|
||||||
|
return broken_libs
|
||||||
|
|
||||||
|
def FindFramework(path):
|
||||||
|
search_pathes = FRAMEWORK_SEARCH_PATH
|
||||||
|
search_pathes.insert(0, QueryQMake('QT_INSTALL_LIBS'))
|
||||||
|
for search_path in search_pathes:
|
||||||
|
abs_path = os.path.join(search_path, path)
|
||||||
|
if os.path.exists(abs_path):
|
||||||
|
return abs_path
|
||||||
|
|
||||||
|
raise CouldNotFindFrameworkError(path)
|
||||||
|
|
||||||
|
def FindLibrary(path):
|
||||||
|
if os.path.exists(path):
|
||||||
|
return path
|
||||||
|
search_pathes = LIBRARY_SEARCH_PATH
|
||||||
|
search_pathes.insert(0, QueryQMake('QT_INSTALL_LIBS'))
|
||||||
|
for search_path in search_pathes:
|
||||||
|
abs_path = os.path.join(search_path, path)
|
||||||
|
if os.path.exists(abs_path):
|
||||||
|
return abs_path
|
||||||
|
else: # try harder---look for lib name in library folders
|
||||||
|
newpath = os.path.join(search_path,os.path.basename(path))
|
||||||
|
if os.path.exists(newpath):
|
||||||
|
return newpath
|
||||||
|
|
||||||
|
return ""
|
||||||
|
#raise CouldNotFindFrameworkError(path)
|
||||||
|
|
||||||
|
def FixAllLibraries(broken_libs):
|
||||||
|
for framework in broken_libs['frameworks']:
|
||||||
|
FixFramework(framework)
|
||||||
|
for lib in broken_libs['libs']:
|
||||||
|
FixLibrary(lib)
|
||||||
|
|
||||||
|
def FixFramework(path):
|
||||||
|
if path in fixed_libraries:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
fixed_libraries.append(path)
|
||||||
|
abs_path = FindFramework(path)
|
||||||
|
broken_libs = GetBrokenLibraries(abs_path)
|
||||||
|
FixAllLibraries(broken_libs)
|
||||||
|
|
||||||
|
new_path = CopyFramework(abs_path)
|
||||||
|
id = os.sep.join(new_path.split(os.sep)[3:])
|
||||||
|
FixFrameworkId(new_path, id)
|
||||||
|
for framework in broken_libs['frameworks']:
|
||||||
|
FixFrameworkInstallPath(framework, new_path)
|
||||||
|
for library in broken_libs['libs']:
|
||||||
|
FixLibraryInstallPath(library, new_path)
|
||||||
|
|
||||||
|
def FixLibrary(path):
|
||||||
|
if path in fixed_libraries or FindSystemLibrary(os.path.basename(path)) is not None:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
fixed_libraries.append(path)
|
||||||
|
abs_path = FindLibrary(path)
|
||||||
|
if abs_path == "":
|
||||||
|
print "Could not resolve %s, not fixing!" % path
|
||||||
|
return
|
||||||
|
broken_libs = GetBrokenLibraries(abs_path)
|
||||||
|
FixAllLibraries(broken_libs)
|
||||||
|
|
||||||
|
new_path = CopyLibrary(abs_path)
|
||||||
|
FixLibraryId(new_path)
|
||||||
|
for framework in broken_libs['frameworks']:
|
||||||
|
FixFrameworkInstallPath(framework, new_path)
|
||||||
|
for library in broken_libs['libs']:
|
||||||
|
FixLibraryInstallPath(library, new_path)
|
||||||
|
|
||||||
|
def FixPlugin(abs_path, subdir):
|
||||||
|
broken_libs = GetBrokenLibraries(abs_path)
|
||||||
|
FixAllLibraries(broken_libs)
|
||||||
|
|
||||||
|
new_path = CopyPlugin(abs_path, subdir)
|
||||||
|
for framework in broken_libs['frameworks']:
|
||||||
|
FixFrameworkInstallPath(framework, new_path)
|
||||||
|
for library in broken_libs['libs']:
|
||||||
|
FixLibraryInstallPath(library, new_path)
|
||||||
|
|
||||||
|
def FixBinary(path):
|
||||||
|
broken_libs = GetBrokenLibraries(path)
|
||||||
|
FixAllLibraries(broken_libs)
|
||||||
|
for framework in broken_libs['frameworks']:
|
||||||
|
FixFrameworkInstallPath(framework, path)
|
||||||
|
for library in broken_libs['libs']:
|
||||||
|
FixLibraryInstallPath(library, path)
|
||||||
|
|
||||||
|
def CopyLibrary(path):
|
||||||
|
new_path = os.path.join(binary_dir, os.path.basename(path))
|
||||||
|
args = ['ditto', '--arch=x86_64', path, new_path]
|
||||||
|
commands.append(args)
|
||||||
|
args = ['chmod', 'u+w', new_path]
|
||||||
|
commands.append(args)
|
||||||
|
return new_path
|
||||||
|
|
||||||
|
def CopyPlugin(path, subdir):
|
||||||
|
new_path = os.path.join(plugins_dir, subdir, os.path.basename(path))
|
||||||
|
args = ['mkdir', '-p', os.path.dirname(new_path)]
|
||||||
|
commands.append(args)
|
||||||
|
args = ['ditto', '--arch=x86_64', path, new_path]
|
||||||
|
commands.append(args)
|
||||||
|
args = ['chmod', 'u+w', new_path]
|
||||||
|
commands.append(args)
|
||||||
|
return new_path
|
||||||
|
|
||||||
|
def CopyFramework(path):
|
||||||
|
parts = path.split(os.sep)
|
||||||
|
print "CopyFramework:", path
|
||||||
|
for i, part in enumerate(parts):
|
||||||
|
if re.match(r'\w+\.framework', part):
|
||||||
|
full_path = os.path.join(frameworks_dir, *parts[i:-1])
|
||||||
|
break
|
||||||
|
args = ['mkdir', '-p', full_path]
|
||||||
|
commands.append(args)
|
||||||
|
args = ['ditto', '--arch=x86_64', path, full_path]
|
||||||
|
commands.append(args)
|
||||||
|
args = ['chmod', 'u+w', os.path.join(full_path, parts[-1])]
|
||||||
|
commands.append(args)
|
||||||
|
|
||||||
|
info_plist = os.path.join(os.path.split(path)[0], '..', '..', 'Contents', 'Info.plist')
|
||||||
|
if os.path.exists(info_plist):
|
||||||
|
args = ['cp', '-r', info_plist, resources_dir]
|
||||||
|
commands.append(args)
|
||||||
|
|
||||||
|
return os.path.join(full_path, parts[-1])
|
||||||
|
|
||||||
|
def FixId(path, library_name):
|
||||||
|
id = '@executable_path/../Frameworks/%s' % library_name
|
||||||
|
args = ['install_name_tool', '-id', id, path]
|
||||||
|
commands.append(args)
|
||||||
|
|
||||||
|
def FixLibraryId(path):
|
||||||
|
library_name = os.path.basename(path)
|
||||||
|
FixId(path, library_name)
|
||||||
|
|
||||||
|
def FixFrameworkId(path, id):
|
||||||
|
FixId(path, id)
|
||||||
|
|
||||||
|
def FixInstallPath(library_path, library, new_path):
|
||||||
|
args = ['install_name_tool', '-change', library_path, new_path, library]
|
||||||
|
commands.append(args)
|
||||||
|
|
||||||
|
def FindSystemLibrary(library_name):
|
||||||
|
for path in ['/lib', '/usr/lib']:
|
||||||
|
full_path = os.path.join(path, library_name)
|
||||||
|
if os.path.exists(full_path):
|
||||||
|
return full_path
|
||||||
|
return None
|
||||||
|
|
||||||
|
def FixLibraryInstallPath(library_path, library):
|
||||||
|
system_library = FindSystemLibrary(os.path.basename(library_path))
|
||||||
|
if system_library is None:
|
||||||
|
new_path = '@executable_path/../MacOS/%s' % os.path.basename(library_path)
|
||||||
|
FixInstallPath(library_path, library, new_path)
|
||||||
|
else:
|
||||||
|
FixInstallPath(library_path, library, system_library)
|
||||||
|
|
||||||
|
def FixFrameworkInstallPath(library_path, library):
|
||||||
|
parts = library_path.split(os.sep)
|
||||||
|
for i, part in enumerate(parts):
|
||||||
|
if re.match(r'\w+\.framework', part):
|
||||||
|
full_path = os.path.join(*parts[i:])
|
||||||
|
break
|
||||||
|
new_path = '@executable_path/../Frameworks/%s' % full_path
|
||||||
|
FixInstallPath(library_path, library, new_path)
|
||||||
|
|
||||||
|
def FindQtPlugin(name):
|
||||||
|
search_path = QT_PLUGINS_SEARCH_PATH
|
||||||
|
search_path.insert(0, QueryQMake('QT_INSTALL_PLUGINS'))
|
||||||
|
for path in search_path:
|
||||||
|
if os.path.exists(path):
|
||||||
|
if os.path.exists(os.path.join(path, name)):
|
||||||
|
return os.path.join(path, name)
|
||||||
|
raise CouldNotFindQtPluginError(name)
|
||||||
|
|
||||||
|
for binary in binaries:
|
||||||
|
FixBinary(binary)
|
||||||
|
|
||||||
|
for plugin in QT_PLUGINS:
|
||||||
|
FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
|
||||||
|
|
||||||
|
if len(sys.argv) <= 2:
|
||||||
|
print 'Will run %d commands:' % len(commands)
|
||||||
|
for command in commands:
|
||||||
|
print ' '.join(command)
|
||||||
|
|
||||||
|
for command in commands:
|
||||||
|
p = subprocess.Popen(command)
|
||||||
|
os.waitpid(p.pid, 0)
|
||||||
|
|
||||||
|
WriteQtConf()
|
|
@ -22,7 +22,7 @@
|
||||||
<key>GID</key>
|
<key>GID</key>
|
||||||
<integer>80</integer>
|
<integer>80</integer>
|
||||||
<key>PATH</key>
|
<key>PATH</key>
|
||||||
<string>owncloud.app</string>
|
<string>@APPLICATION_EXECUTABLE@.app</string>
|
||||||
<key>PATH_TYPE</key>
|
<key>PATH_TYPE</key>
|
||||||
<integer>3</integer>
|
<integer>3</integer>
|
||||||
<key>PERMISSIONS</key>
|
<key>PERMISSIONS</key>
|
||||||
|
@ -1055,7 +1055,7 @@
|
||||||
<key>BACKGROUND_PATH</key>
|
<key>BACKGROUND_PATH</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>PATH</key>
|
<key>PATH</key>
|
||||||
<string>./owncloud.app/Contents/Resources/owncloud_logo_blue.png</string>
|
<string>./@APPLICATION_EXECUTABLE@.app/Contents/Resources/owncloud_logo_blue.png</string>
|
||||||
<key>PATH_TYPE</key>
|
<key>PATH_TYPE</key>
|
||||||
<integer>3</integer>
|
<integer>3</integer>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
src_app="$1"
|
src_app="$1"
|
||||||
identity="$2"
|
identity="$2"
|
||||||
|
|
||||||
QT_FMWKS=`basename ${TMP_APP}/Contents/Frameworks`/Qt*
|
|
||||||
QT_FMWK_VERSION="5"
|
QT_FMWK_VERSION="5"
|
||||||
|
|
||||||
fix_frameworks() {
|
fix_frameworks() {
|
||||||
|
@ -26,8 +25,8 @@ fix_frameworks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fix_frameworks "$src_app" `qmake -query QT_INSTALL_LIBS` "$src_app"/Contents/Frameworks
|
fix_frameworks "$src_app" `qmake -query QT_INSTALL_LIBS` "$src_app"/Contents/Frameworks
|
||||||
codesign -s "$identity" --force --verify --verbose --deep "$src_app"
|
codesign -s "$identity" --force --verbose=4 --deep "$src_app"
|
||||||
|
|
||||||
# Just for our debug purposes:
|
# Just for our debug purposes:
|
||||||
spctl -a -t exec -vv $src_app
|
spctl -a -t exec -vv $src_app
|
||||||
codesign -dv $src_app
|
codesign -dv $src_app
|
||||||
|
|
2
binary
2
binary
|
@ -1 +1 @@
|
||||||
Subproject commit 82d72bc62d1f036a4d73a8e9375b52db6e0573b3
|
Subproject commit 18d9ac810b03866e1939d66723d01b544bbdde7e
|
|
@ -14,7 +14,8 @@ if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)")
|
||||||
|
|
||||||
# add -Wconversion ?
|
# add -Wconversion ?
|
||||||
# cannot be pedantic with sqlite3 directly linked
|
# cannot be pedantic with sqlite3 directly linked
|
||||||
if (NOT CSYNC_STATIC_COMPILE_DIR)
|
# FIXME Can we somehow not use those flags for sqlite3.* but use them for the rest of csync?
|
||||||
|
if (NOT USE_OUR_OWN_SQLITE3)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors")
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes")
|
||||||
|
|
|
@ -50,8 +50,18 @@ if (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
|
||||||
set(SQLite3_VERSION _SQLITE3_VERSION)
|
set(SQLite3_VERSION _SQLITE3_VERSION)
|
||||||
endif (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
|
endif (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
if (APPLE OR WIN32)
|
||||||
find_package_handle_standard_args(SQLite3 DEFAULT_MSG SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIRS)
|
set(USE_OUR_OWN_SQLITE3 TRUE)
|
||||||
|
set(SQLITE3_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src/3rdparty/sqlite3)
|
||||||
|
set(SQLITE3_LIBRARIES "")
|
||||||
|
set(SQLITE3_SOURCE ${SQLITE3_INCLUDE_DIR}/sqlite3.c)
|
||||||
|
MESSAGE(STATUS "Using own sqlite3 from " ${SQLITE3_INCLUDE_DIR})
|
||||||
|
else()
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(SQLite3 DEFAULT_MSG SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIRS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# show the SQLITE3_INCLUDE_DIRS and SQLITE3_LIBRARIES variables only in the advanced view
|
# show the SQLITE3_INCLUDE_DIRS and SQLITE3_LIBRARIES variables only in the advanced view
|
||||||
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)
|
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)
|
||||||
|
|
|
@ -391,6 +391,7 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||||
; Make sure only to copy qt, not qt_help, etc
|
; Make sure only to copy qt, not qt_help, etc
|
||||||
File "${MING_SHARE}\qt5\translations\qt_??.qm"
|
File "${MING_SHARE}\qt5\translations\qt_??.qm"
|
||||||
File "${MING_SHARE}\qt5\translations\qt_??_??.qm"
|
File "${MING_SHARE}\qt5\translations\qt_??_??.qm"
|
||||||
|
File "${MING_SHARE}\qt5\translations\qtbase_*.qm"
|
||||||
File "${MING_SHARE}\qt5\translations\qtkeychain_*.qm"
|
File "${MING_SHARE}\qt5\translations\qtkeychain_*.qm"
|
||||||
|
|
||||||
SetOutPath "$INSTDIR\platforms"
|
SetOutPath "$INSTDIR\platforms"
|
||||||
|
|
|
@ -153,7 +153,18 @@ if(NOT Qt5Core_FOUND)
|
||||||
include( ${QT_USE_FILE} )
|
include( ${QT_USE_FILE} )
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
add_definitions("-DQ_DECL_OVERRIDE=override")
|
if (CMAKE_COMPILER_IS_GNUCC)
|
||||||
|
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||||
|
OUTPUT_VARIABLE GCC_VERSION)
|
||||||
|
if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
|
||||||
|
add_definitions("-DQ_DECL_OVERRIDE=override")
|
||||||
|
else()
|
||||||
|
add_definitions("-DQ_DECL_OVERRIDE=")
|
||||||
|
endif()
|
||||||
|
else() #clang or others
|
||||||
|
add_definitions("-DQ_DECL_OVERRIDE=override")
|
||||||
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if( Qt5Core_DIR )
|
if( Qt5Core_DIR )
|
||||||
|
|
|
@ -4,13 +4,6 @@ add_subdirectory(std)
|
||||||
add_subdirectory(httpbf)
|
add_subdirectory(httpbf)
|
||||||
|
|
||||||
# Statically include sqlite
|
# Statically include sqlite
|
||||||
if (CSYNC_STATIC_COMPILE_DIR)
|
|
||||||
set(SQLITE3_INCLUDE_DIRS "")
|
|
||||||
set(SQLITE3_LIBRARIES "")
|
|
||||||
include_directories(${CSYNC_STATIC_COMPILE_DIR})
|
|
||||||
else (CSYNC_STATIC_COMPILE_DIR)
|
|
||||||
find_package(SQLite3 3.3.9 REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CSYNC_PUBLIC_INCLUDE_DIRS
|
set(CSYNC_PUBLIC_INCLUDE_DIRS
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
@ -86,8 +79,8 @@ set(csync_HDRS
|
||||||
)
|
)
|
||||||
|
|
||||||
# Statically include sqlite
|
# Statically include sqlite
|
||||||
if (CSYNC_STATIC_COMPILE_DIR)
|
if (USE_OUR_OWN_SQLITE3)
|
||||||
list(APPEND csync_SRCS ${CSYNC_STATIC_COMPILE_DIR}/dictionary.c ${CSYNC_STATIC_COMPILE_DIR}/sqlite3.c)
|
list(APPEND csync_SRCS ${SQLITE3_SOURCE})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|
|
@ -254,6 +254,13 @@ int csync_update(CSYNC *ctx) {
|
||||||
|
|
||||||
csync_gettime(&finish);
|
csync_gettime(&finish);
|
||||||
|
|
||||||
|
/* Finalize the sql precompiled statements after the update run since
|
||||||
|
* it runs in its own thread. Precompiled statements shoult not be shared
|
||||||
|
* across thread borders according to
|
||||||
|
* http://www.sqlite.org/cvstrac/wiki?p=MultiThreading
|
||||||
|
*/
|
||||||
|
csync_statedb_finalize_statements(ctx);
|
||||||
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
||||||
"Update detection for remote replica took %.2f seconds "
|
"Update detection for remote replica took %.2f seconds "
|
||||||
"walking %zu files.",
|
"walking %zu files.",
|
||||||
|
|
|
@ -110,7 +110,7 @@ static int ssl_callback_by_neon(void *userdata, int failures,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DEBUG_WEBDAV("## VERIFY_SSL CERT: %d", ret );
|
DEBUG_WEBDAV("## VERIFY_SSL CERT: %d", ret );
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -761,6 +761,7 @@ int owncloud_commit(CSYNC* ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->owncloud_context->is_first_propfind = true;
|
ctx->owncloud_context->is_first_propfind = true;
|
||||||
|
ctx->owncloud_context->dav_session.no_recursive_propfind = true;
|
||||||
/* DEBUG_WEBDAV( "********** vio_module_shutdown" ); */
|
/* DEBUG_WEBDAV( "********** vio_module_shutdown" ); */
|
||||||
|
|
||||||
ctx->owncloud_context->dav_session.ctx = 0;
|
ctx->owncloud_context->dav_session.ctx = 0;
|
||||||
|
|
|
@ -339,6 +339,8 @@ void fill_webdav_properties_into_resource(struct resource* newres, const ne_prop
|
||||||
file_id = ne_propset_value( set, &ls_props[4] );
|
file_id = ne_propset_value( set, &ls_props[4] );
|
||||||
directDownloadUrl = ne_propset_value( set, &ls_props[5] );
|
directDownloadUrl = ne_propset_value( set, &ls_props[5] );
|
||||||
directDownloadCookies = ne_propset_value( set, &ls_props[6] );
|
directDownloadCookies = ne_propset_value( set, &ls_props[6] );
|
||||||
|
|
||||||
|
// permission flags: Defined in https://github.com/owncloud/core/issues/8322
|
||||||
perm = ne_propset_value( set, &ls_props[7] );
|
perm = ne_propset_value( set, &ls_props[7] );
|
||||||
|
|
||||||
if( resourcetype && strncmp( resourcetype, "<DAV:collection>", 16 ) == 0) {
|
if( resourcetype && strncmp( resourcetype, "<DAV:collection>", 16 ) == 0) {
|
||||||
|
|
|
@ -100,6 +100,8 @@ struct csync_s {
|
||||||
sqlite3_stmt* by_hash_stmt;
|
sqlite3_stmt* by_hash_stmt;
|
||||||
sqlite3_stmt* by_fileid_stmt;
|
sqlite3_stmt* by_fileid_stmt;
|
||||||
sqlite3_stmt* by_inode_stmt;
|
sqlite3_stmt* by_inode_stmt;
|
||||||
|
|
||||||
|
int lastReturnValue;
|
||||||
} statedb;
|
} statedb;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -146,8 +148,8 @@ struct csync_s {
|
||||||
struct csync_owncloud_ctx_s *owncloud_context;
|
struct csync_owncloud_ctx_s *owncloud_context;
|
||||||
|
|
||||||
/* hooks for checking the white list */
|
/* hooks for checking the white list */
|
||||||
void *checkBlackListData;
|
void *checkSelectiveSyncBlackListData;
|
||||||
int (*checkBlackListHook)(void*, const char*);
|
int (*checkSelectiveSyncBlackListHook)(void*, const char*);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,11 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
|
||||||
/* Do not remove a directory that has ignored files */
|
/* Do not remove a directory that has ignored files */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (cur->child_modified) {
|
||||||
|
/* re-create directory that has modified contents */
|
||||||
|
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
cur->instruction = CSYNC_INSTRUCTION_REMOVE;
|
cur->instruction = CSYNC_INSTRUCTION_REMOVE;
|
||||||
break;
|
break;
|
||||||
case CSYNC_INSTRUCTION_EVAL_RENAME:
|
case CSYNC_INSTRUCTION_EVAL_RENAME:
|
||||||
|
|
|
@ -50,6 +50,22 @@
|
||||||
|
|
||||||
#define BUF_SIZE 16
|
#define BUF_SIZE 16
|
||||||
|
|
||||||
|
#define sqlite_open(A, B) sqlite3_open_v2(A,B, SQLITE_OPEN_READONLY+SQLITE_OPEN_NOMUTEX, NULL)
|
||||||
|
|
||||||
|
#define SQLTM_TIME 150000
|
||||||
|
#define SQLTM_COUNT 10
|
||||||
|
|
||||||
|
#define SQLITE_BUSY_HANDLED(F) if(1) { \
|
||||||
|
int n = 0; \
|
||||||
|
do { rc = F ; \
|
||||||
|
if( (rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED) ) { \
|
||||||
|
n++; \
|
||||||
|
usleep(SQLTM_TIME); \
|
||||||
|
} \
|
||||||
|
}while( (n < SQLTM_COUNT) && ((rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED))); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void csync_set_statedb_exists(CSYNC *ctx, int val) {
|
void csync_set_statedb_exists(CSYNC *ctx, int val) {
|
||||||
ctx->statedb.exists = val;
|
ctx->statedb.exists = val;
|
||||||
}
|
}
|
||||||
|
@ -73,85 +89,12 @@ static int _csync_check_db_integrity(sqlite3 *db) {
|
||||||
c_strlist_destroy(result);
|
c_strlist_destroy(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
if( sqlite3_threadsafe() == 0 ) {
|
||||||
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "* WARNING: SQLite module is not threadsafe!");
|
||||||
}
|
rc = -1;
|
||||||
|
|
||||||
static int _csync_statedb_check(const char *statedb) {
|
|
||||||
int fd = -1, rc;
|
|
||||||
ssize_t r;
|
|
||||||
char buf[BUF_SIZE] = {0};
|
|
||||||
sqlite3 *db = NULL;
|
|
||||||
csync_stat_t sb;
|
|
||||||
|
|
||||||
mbchar_t *wstatedb = c_utf8_to_locale(statedb);
|
|
||||||
|
|
||||||
if (wstatedb == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check db version */
|
|
||||||
#ifdef _WIN32
|
|
||||||
_fmode = _O_BINARY;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fd = _topen(wstatedb, O_RDONLY);
|
|
||||||
|
|
||||||
if (fd >= 0) {
|
|
||||||
/* Check size. Size of zero is a valid database actually. */
|
|
||||||
rc = _tfstat(fd, &sb);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
if (sb.st_size == 0) {
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Database size is zero byte!");
|
|
||||||
close(fd);
|
|
||||||
} else {
|
|
||||||
r = read(fd, (void *) buf, sizeof(buf) - 1);
|
|
||||||
close(fd);
|
|
||||||
if (r >= 0) {
|
|
||||||
buf[BUF_SIZE - 1] = '\0';
|
|
||||||
if (c_streq(buf, "SQLite format 3")) {
|
|
||||||
if (sqlite3_open(statedb, &db ) == SQLITE_OK) {
|
|
||||||
rc = _csync_check_db_integrity(db);
|
|
||||||
if( sqlite3_close(db) != 0 ) {
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "WARN: sqlite3_close error!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if( rc >= 0 ) {
|
|
||||||
/* everything is fine */
|
|
||||||
c_free_locale_string(wstatedb);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Integrity check failed!");
|
|
||||||
} else {
|
|
||||||
/* resources need to be freed even when open failed */
|
|
||||||
sqlite3_close(db);
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "database corrupted, removing!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "sqlite version mismatch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
/* if it comes here, the database is broken and should be recreated. */
|
|
||||||
_tunlink(wstatedb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c_free_locale_string(wstatedb);
|
return rc;
|
||||||
|
|
||||||
/* create database */
|
|
||||||
rc = sqlite3_open(statedb, &db);
|
|
||||||
if (rc == SQLITE_OK) {
|
|
||||||
sqlite3_close(db);
|
|
||||||
csync_win32_set_file_hidden(statedb, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
sqlite3_close(db);
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "sqlite3_open failed: %s %s", sqlite3_errmsg(db), statedb);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _csync_statedb_is_empty(sqlite3 *db) {
|
static int _csync_statedb_is_empty(sqlite3 *db) {
|
||||||
|
@ -187,19 +130,10 @@ int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* csync_statedb_check tries to open the statedb and creates it in case
|
ctx->statedb.lastReturnValue = SQLITE_OK;
|
||||||
* its not there.
|
|
||||||
*/
|
|
||||||
check_rc = _csync_statedb_check(statedb);
|
|
||||||
if (check_rc < 0) {
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: checking csync database failed - bail out.");
|
|
||||||
|
|
||||||
rc = -1;
|
/* Openthe database */
|
||||||
goto out;
|
if (sqlite_open(statedb, &db) != SQLITE_OK) {
|
||||||
}
|
|
||||||
|
|
||||||
/* Open or create the temporary database */
|
|
||||||
if (sqlite3_open(statedb, &db) != SQLITE_OK) {
|
|
||||||
const char *errmsg= sqlite3_errmsg(ctx->statedb.db);
|
const char *errmsg= sqlite3_errmsg(ctx->statedb.db);
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: Failed to sqlite3 open statedb - bail out: %s.",
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: Failed to sqlite3 open statedb - bail out: %s.",
|
||||||
errmsg ? errmsg : "<no sqlite3 errormsg>");
|
errmsg ? errmsg : "<no sqlite3 errormsg>");
|
||||||
|
@ -208,16 +142,31 @@ int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If check_rc == 1 the database is new and empty as a result. */
|
if (_csync_check_db_integrity(db) != 0) {
|
||||||
if ((check_rc == 1) || _csync_statedb_is_empty(db)) {
|
const char *errmsg= sqlite3_errmsg(db);
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "statedb doesn't exist");
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: sqlite3 integrity check failed - bail out: %s.",
|
||||||
|
errmsg ? errmsg : "<no sqlite3 errormsg>");
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_csync_statedb_is_empty(db)) {
|
||||||
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "statedb contents doesn't exist");
|
||||||
csync_set_statedb_exists(ctx, 0);
|
csync_set_statedb_exists(ctx, 0);
|
||||||
} else {
|
} else {
|
||||||
csync_set_statedb_exists(ctx, 1);
|
csync_set_statedb_exists(ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print out the version */
|
||||||
|
//
|
||||||
|
result = csync_statedb_query(db, "SELECT sqlite_version();");
|
||||||
|
if (result && result->count >= 1) {
|
||||||
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "sqlite3 version \"%s\"", *result->vector);
|
||||||
|
}
|
||||||
|
c_strlist_destroy(result);
|
||||||
|
|
||||||
/* optimization for speeding up SQLite */
|
/* optimization for speeding up SQLite */
|
||||||
result = csync_statedb_query(db, "PRAGMA synchronous = FULL;");
|
result = csync_statedb_query(db, "PRAGMA synchronous = NORMAL;");
|
||||||
c_strlist_destroy(result);
|
c_strlist_destroy(result);
|
||||||
result = csync_statedb_query(db, "PRAGMA case_sensitive_like = ON;");
|
result = csync_statedb_query(db, "PRAGMA case_sensitive_like = ON;");
|
||||||
c_strlist_destroy(result);
|
c_strlist_destroy(result);
|
||||||
|
@ -243,21 +192,7 @@ int csync_statedb_close(CSYNC *ctx) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* deallocate query resources */
|
csync_statedb_finalize_statements(ctx);
|
||||||
if( ctx->statedb.by_hash_stmt ) {
|
|
||||||
rc = sqlite3_finalize(ctx->statedb.by_hash_stmt);
|
|
||||||
ctx->statedb.by_hash_stmt = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ctx->statedb.by_fileid_stmt ) {
|
|
||||||
rc = sqlite3_finalize(ctx->statedb.by_fileid_stmt);
|
|
||||||
ctx->statedb.by_fileid_stmt = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ctx->statedb.by_inode_stmt ) {
|
|
||||||
rc = sqlite3_finalize(ctx->statedb.by_inode_stmt);
|
|
||||||
ctx->statedb.by_inode_stmt = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_close(ctx->statedb.db);
|
sqlite3_close(ctx->statedb.db);
|
||||||
|
|
||||||
|
@ -281,7 +216,7 @@ static int _csync_file_stat_from_metadata_table( csync_file_stat_t **st, sqlite3
|
||||||
|
|
||||||
column_count = sqlite3_column_count(stmt);
|
column_count = sqlite3_column_count(stmt);
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
SQLITE_BUSY_HANDLED( sqlite3_step(stmt) );
|
||||||
|
|
||||||
if( rc == SQLITE_ROW ) {
|
if( rc == SQLITE_ROW ) {
|
||||||
if(column_count > 7) {
|
if(column_count > 7) {
|
||||||
|
@ -344,7 +279,8 @@ csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx,
|
||||||
if( ctx->statedb.by_hash_stmt == NULL ) {
|
if( ctx->statedb.by_hash_stmt == NULL ) {
|
||||||
const char *hash_query = "SELECT * FROM metadata WHERE phash=?1";
|
const char *hash_query = "SELECT * FROM metadata WHERE phash=?1";
|
||||||
|
|
||||||
rc = sqlite3_prepare_v2(ctx->statedb.db, hash_query, strlen(hash_query), &ctx->statedb.by_hash_stmt, NULL);
|
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, hash_query, strlen(hash_query), &ctx->statedb.by_hash_stmt, NULL));
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( rc != SQLITE_OK ) {
|
if( rc != SQLITE_OK ) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for hash query.");
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for hash query.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -358,6 +294,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx,
|
||||||
sqlite3_bind_int64(ctx->statedb.by_hash_stmt, 1, (long long signed int)phash);
|
sqlite3_bind_int64(ctx->statedb.by_hash_stmt, 1, (long long signed int)phash);
|
||||||
|
|
||||||
rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_hash_stmt);
|
rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_hash_stmt);
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
|
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc);
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc);
|
||||||
}
|
}
|
||||||
|
@ -366,6 +303,28 @@ csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx,
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void csync_statedb_finalize_statements(CSYNC *ctx) {
|
||||||
|
if( !ctx ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deallocate query resources */
|
||||||
|
if( ctx->statedb.by_fileid_stmt ) {
|
||||||
|
sqlite3_finalize(ctx->statedb.by_fileid_stmt);
|
||||||
|
ctx->statedb.by_fileid_stmt = NULL;
|
||||||
|
}
|
||||||
|
if( ctx->statedb.by_hash_stmt ) {
|
||||||
|
sqlite3_finalize(ctx->statedb.by_hash_stmt);
|
||||||
|
ctx->statedb.by_hash_stmt = NULL;
|
||||||
|
}
|
||||||
|
if( ctx->statedb.by_inode_stmt) {
|
||||||
|
sqlite3_finalize(ctx->statedb.by_inode_stmt);
|
||||||
|
ctx->statedb.by_inode_stmt = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->statedb.lastReturnValue = SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx,
|
csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx,
|
||||||
const char *file_id ) {
|
const char *file_id ) {
|
||||||
csync_file_stat_t *st = NULL;
|
csync_file_stat_t *st = NULL;
|
||||||
|
@ -385,7 +344,8 @@ csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx,
|
||||||
if( ctx->statedb.by_fileid_stmt == NULL ) {
|
if( ctx->statedb.by_fileid_stmt == NULL ) {
|
||||||
const char *query = "SELECT * FROM metadata WHERE fileid=?1";
|
const char *query = "SELECT * FROM metadata WHERE fileid=?1";
|
||||||
|
|
||||||
rc = sqlite3_prepare_v2(ctx->statedb.db, query, strlen(query), &ctx->statedb.by_fileid_stmt, NULL);
|
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, query, strlen(query), &ctx->statedb.by_fileid_stmt, NULL));
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( rc != SQLITE_OK ) {
|
if( rc != SQLITE_OK ) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for file id query.");
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for file id query.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -396,6 +356,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx,
|
||||||
sqlite3_bind_text(ctx->statedb.by_fileid_stmt, 1, file_id, -1, SQLITE_STATIC);
|
sqlite3_bind_text(ctx->statedb.by_fileid_stmt, 1, file_id, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_fileid_stmt);
|
rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_fileid_stmt);
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
|
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc);
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc);
|
||||||
}
|
}
|
||||||
|
@ -423,7 +384,8 @@ csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx,
|
||||||
if( ctx->statedb.by_inode_stmt == NULL ) {
|
if( ctx->statedb.by_inode_stmt == NULL ) {
|
||||||
const char *inode_query = "SELECT * FROM metadata WHERE inode=?1";
|
const char *inode_query = "SELECT * FROM metadata WHERE inode=?1";
|
||||||
|
|
||||||
rc = sqlite3_prepare_v2(ctx->statedb.db, inode_query, strlen(inode_query), &ctx->statedb.by_inode_stmt, NULL);
|
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, inode_query, strlen(inode_query), &ctx->statedb.by_inode_stmt, NULL));
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( rc != SQLITE_OK ) {
|
if( rc != SQLITE_OK ) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for inode query.");
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for inode query.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -437,6 +399,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx,
|
||||||
sqlite3_bind_int64(ctx->statedb.by_inode_stmt, 1, (long long signed int)inode);
|
sqlite3_bind_int64(ctx->statedb.by_inode_stmt, 1, (long long signed int)inode);
|
||||||
|
|
||||||
rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_inode_stmt);
|
rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_inode_stmt);
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
|
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata by inode: %d!", rc);
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata by inode: %d!", rc);
|
||||||
}
|
}
|
||||||
|
@ -485,7 +448,8 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = sqlite3_prepare_v2(ctx->statedb.db, BELOW_PATH_QUERY, -1, &stmt, NULL);
|
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, BELOW_PATH_QUERY, -1, &stmt, NULL));
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( rc != SQLITE_OK ) {
|
if( rc != SQLITE_OK ) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query.");
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query.");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -507,6 +471,7 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||||
|
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
|
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
do {
|
do {
|
||||||
csync_file_stat_t *st = NULL;
|
csync_file_stat_t *st = NULL;
|
||||||
|
|
||||||
|
@ -522,6 +487,7 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||||
}
|
}
|
||||||
} while( rc == SQLITE_ROW );
|
} while( rc == SQLITE_ROW );
|
||||||
|
|
||||||
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( rc != SQLITE_DONE ) {
|
if( rc != SQLITE_DONE ) {
|
||||||
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
|
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -99,25 +99,10 @@ int csync_statedb_get_below_path(CSYNC *ctx, const char *path);
|
||||||
c_strlist_t *csync_statedb_query(sqlite3 *db, const char *statement);
|
c_strlist_t *csync_statedb_query(sqlite3 *db, const char *statement);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Insert function for the statedb.
|
* @brief csync_statedb_finalize_statements - Clear prepared statements
|
||||||
*
|
* @param ctx The csync context
|
||||||
* @param ctx The csync context.
|
|
||||||
* @param statement The SQL statement to insert into the statedb.
|
|
||||||
*
|
|
||||||
* @return The rowid of the most recent INSERT on success, 0 if the query
|
|
||||||
* wasn't successful.
|
|
||||||
*/
|
*/
|
||||||
typedef struct csync_progressinfo_s {
|
void csync_statedb_finalize_statements(CSYNC *ctx);
|
||||||
struct csync_progressinfo_s *next;
|
|
||||||
uint64_t phash;
|
|
||||||
uint64_t modtime;
|
|
||||||
char *md5;
|
|
||||||
int error;
|
|
||||||
int chunk;
|
|
||||||
int transferId;
|
|
||||||
char *tmpfile;
|
|
||||||
char *error_string;
|
|
||||||
} csync_progressinfo_t;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,10 @@ static bool _csync_sameextension(const char *p1, const char *p2) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static bool _last_db_return_error(CSYNC* ctx) {
|
||||||
|
return ctx->statedb.lastReturnValue != SQLITE_OK && ctx->statedb.lastReturnValue != SQLITE_DONE && ctx->statedb.lastReturnValue != SQLITE_ROW;
|
||||||
|
}
|
||||||
|
|
||||||
static int _csync_detect_update(CSYNC *ctx, const char *file,
|
static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
const csync_vio_file_stat_t *fs, const int type) {
|
const csync_vio_file_stat_t *fs, const int type) {
|
||||||
uint64_t h = 0;
|
uint64_t h = 0;
|
||||||
|
@ -146,10 +150,10 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
if (excluded != CSYNC_NOT_EXCLUDED) {
|
if (excluded != CSYNC_NOT_EXCLUDED) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", path, excluded);
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", path, excluded);
|
||||||
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) {
|
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) {
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
|
if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->current_fs) {
|
if (ctx->current_fs) {
|
||||||
|
@ -157,9 +161,9 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->current == REMOTE_REPLICA && ctx->checkBlackListHook) {
|
if (ctx->current == REMOTE_REPLICA && ctx->checkSelectiveSyncBlackListHook) {
|
||||||
if (ctx->checkBlackListHook(ctx->checkBlackListData, path)) {
|
if (ctx->checkSelectiveSyncBlackListHook(ctx->checkSelectiveSyncBlackListData, path)) {
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,8 +194,15 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs->mtime == 0) {
|
if (fs->mtime == 0) {
|
||||||
tmp = csync_statedb_get_stat_by_hash(ctx, h);
|
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - mtime is zero!", path);
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - mtime is zero!", path);
|
||||||
|
|
||||||
|
tmp = csync_statedb_get_stat_by_hash(ctx, h);
|
||||||
|
if(_last_db_return_error(ctx)) {
|
||||||
|
SAFE_FREE(st);
|
||||||
|
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - not found in db, IGNORE!", path);
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - not found in db, IGNORE!", path);
|
||||||
st->instruction = CSYNC_INSTRUCTION_IGNORE;
|
st->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||||
|
@ -228,6 +239,12 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
if (csync_get_statedb_exists(ctx)) {
|
if (csync_get_statedb_exists(ctx)) {
|
||||||
tmp = csync_statedb_get_stat_by_hash(ctx, h);
|
tmp = csync_statedb_get_stat_by_hash(ctx, h);
|
||||||
|
|
||||||
|
if(_last_db_return_error(ctx)) {
|
||||||
|
SAFE_FREE(st);
|
||||||
|
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if(tmp && tmp->phash == h ) { /* there is an entry in the database */
|
if(tmp && tmp->phash == h ) { /* there is an entry in the database */
|
||||||
/* we have an update! */
|
/* we have an update! */
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Database entry found, compare: %" PRId64 " <-> %" PRId64 ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64 ", size: %" PRId64 " <-> %" PRId64,
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Database entry found, compare: %" PRId64 " <-> %" PRId64 ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64 ", size: %" PRId64 " <-> %" PRId64,
|
||||||
|
@ -289,6 +306,12 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
|
|
||||||
tmp = csync_statedb_get_stat_by_inode(ctx, fs->inode);
|
tmp = csync_statedb_get_stat_by_inode(ctx, fs->inode);
|
||||||
|
|
||||||
|
if(_last_db_return_error(ctx)) {
|
||||||
|
SAFE_FREE(st);
|
||||||
|
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* translate the file type between the two stat types csync has. */
|
/* translate the file type between the two stat types csync has. */
|
||||||
if( tmp && tmp->type == 0 ) {
|
if( tmp && tmp->type == 0 ) {
|
||||||
tmp_vio_type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
tmp_vio_type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
||||||
|
@ -319,6 +342,12 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
} else {
|
} else {
|
||||||
/* Remote Replica Rename check */
|
/* Remote Replica Rename check */
|
||||||
tmp = csync_statedb_get_stat_by_file_id(ctx, fs->file_id);
|
tmp = csync_statedb_get_stat_by_file_id(ctx, fs->file_id);
|
||||||
|
|
||||||
|
if(_last_db_return_error(ctx)) {
|
||||||
|
SAFE_FREE(st);
|
||||||
|
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if(tmp ) { /* tmp existing at all */
|
if(tmp ) { /* tmp existing at all */
|
||||||
if ((tmp->type == CSYNC_FTW_TYPE_DIR && fs->type != CSYNC_VIO_FILE_TYPE_DIRECTORY) ||
|
if ((tmp->type == CSYNC_FTW_TYPE_DIR && fs->type != CSYNC_VIO_FILE_TYPE_DIRECTORY) ||
|
||||||
(tmp->type == CSYNC_FTW_TYPE_FILE && fs->type != CSYNC_VIO_FILE_TYPE_REGULAR)) {
|
(tmp->type == CSYNC_FTW_TYPE_FILE && fs->type != CSYNC_VIO_FILE_TYPE_REGULAR)) {
|
||||||
|
@ -346,8 +375,9 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Unable to open statedb, setting inst to NEW" );
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Unable to open statedb" );
|
||||||
st->instruction = CSYNC_INSTRUCTION_NEW;
|
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -603,10 +633,13 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||||
path = filename + ulen;
|
path = filename + ulen;
|
||||||
|
|
||||||
/* skip ".csync_journal.db" and ".csync_journal.db.ctmp" */
|
/* skip ".csync_journal.db" and ".csync_journal.db.ctmp" */
|
||||||
|
/* Isn't this done via csync_exclude already? */
|
||||||
if (c_streq(path, ".csync_journal.db")
|
if (c_streq(path, ".csync_journal.db")
|
||||||
|| c_streq(path, ".csync_journal.db.ctmp")
|
|| c_streq(path, ".csync_journal.db.ctmp")
|
||||||
|| c_streq(path, ".csync_journal.db.ctmp-journal")
|
|| c_streq(path, ".csync_journal.db.ctmp-journal")
|
||||||
|| c_streq(path, ".csync-progressdatabase")) {
|
|| c_streq(path, ".csync-progressdatabase")
|
||||||
|
|| c_streq(path, ".csync_journal.db-shm")
|
||||||
|
|| c_streq(path, ".csync_journal.db-wal")) {
|
||||||
csync_vio_file_stat_destroy(dirent);
|
csync_vio_file_stat_destroy(dirent);
|
||||||
dirent = NULL;
|
dirent = NULL;
|
||||||
SAFE_FREE(filename);
|
SAFE_FREE(filename);
|
||||||
|
@ -652,6 +685,11 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||||
uint64_t h = c_jhash64((uint8_t *) path, len, 0);
|
uint64_t h = c_jhash64((uint8_t *) path, len, 0);
|
||||||
etag = csync_statedb_get_etag( ctx, h );
|
etag = csync_statedb_get_etag( ctx, h );
|
||||||
|
|
||||||
|
if(_last_db_return_error(ctx)) {
|
||||||
|
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
if( etag ) {
|
if( etag ) {
|
||||||
SAFE_FREE(fs->etag);
|
SAFE_FREE(fs->etag);
|
||||||
fs->etag = etag;
|
fs->etag = etag;
|
||||||
|
@ -671,11 +709,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||||
rc = fn(ctx, filename, fs, flag);
|
rc = fn(ctx, filename, fs, flag);
|
||||||
/* this function may update ctx->current and ctx->read_from_db */
|
/* this function may update ctx->current and ctx->read_from_db */
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only for the local replica we have to destroy stat(), for the remote one it is a pointer to dirent */
|
/* Only for the local replica we have to destroy stat(), for the remote one it is a pointer to dirent */
|
||||||
if (ctx->replica == LOCAL_REPLICA) {
|
if (ctx->replica == LOCAL_REPLICA) {
|
||||||
csync_vio_file_stat_destroy(fs);
|
csync_vio_file_stat_destroy(fs);
|
||||||
|
@ -690,7 +723,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag == CSYNC_FTW_FLAG_DIR && depth
|
if (flag == CSYNC_FTW_FLAG_DIR && depth && rc == 0
|
||||||
&& (!ctx->current_fs || ctx->current_fs->instruction != CSYNC_INSTRUCTION_IGNORE)) {
|
&& (!ctx->current_fs || ctx->current_fs->instruction != CSYNC_INSTRUCTION_IGNORE)) {
|
||||||
rc = csync_ftw(ctx, filename, fn, depth - 1);
|
rc = csync_ftw(ctx, filename, fn, depth - 1);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
@ -710,6 +743,11 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (flag == CSYNC_FTW_FLAG_DIR && ctx->current_fs
|
if (flag == CSYNC_FTW_FLAG_DIR && ctx->current_fs
|
||||||
&& (ctx->current_fs->instruction == CSYNC_INSTRUCTION_EVAL ||
|
&& (ctx->current_fs->instruction == CSYNC_INSTRUCTION_EVAL ||
|
||||||
ctx->current_fs->instruction == CSYNC_INSTRUCTION_NEW)) {
|
ctx->current_fs->instruction == CSYNC_INSTRUCTION_NEW)) {
|
||||||
|
|
|
@ -55,19 +55,11 @@ static void setup(void **state)
|
||||||
|
|
||||||
static void setup_db(void **state)
|
static void setup_db(void **state)
|
||||||
{
|
{
|
||||||
CSYNC *csync;
|
char *errmsg;
|
||||||
char *stmt = NULL;
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
c_strlist_t *result = NULL;
|
sqlite3 *db = NULL;
|
||||||
|
|
||||||
setup(state);
|
const char *sql = "CREATE TABLE IF NOT EXISTS metadata ("
|
||||||
csync = *state;
|
|
||||||
|
|
||||||
// rc = csync_statedb_create_tables(csync->statedb.db);
|
|
||||||
assert_int_equal(rc, 0);
|
|
||||||
|
|
||||||
result = csync_statedb_query(csync->statedb.db,
|
|
||||||
"CREATE TABLE IF NOT EXISTS metadata ("
|
|
||||||
"phash INTEGER(8),"
|
"phash INTEGER(8),"
|
||||||
"pathlen INTEGER,"
|
"pathlen INTEGER,"
|
||||||
"path VARCHAR(4096),"
|
"path VARCHAR(4096),"
|
||||||
|
@ -79,29 +71,25 @@ static void setup_db(void **state)
|
||||||
"type INTEGER,"
|
"type INTEGER,"
|
||||||
"md5 VARCHAR(32),"
|
"md5 VARCHAR(32),"
|
||||||
"PRIMARY KEY(phash)"
|
"PRIMARY KEY(phash)"
|
||||||
");"
|
");";
|
||||||
);
|
|
||||||
|
|
||||||
assert_non_null(result);
|
const char *sql2 = "INSERT INTO metadata"
|
||||||
c_strlist_destroy(result);
|
|
||||||
|
|
||||||
|
|
||||||
stmt = sqlite3_mprintf("INSERT INTO metadata"
|
|
||||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5) VALUES"
|
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5) VALUES"
|
||||||
"(%lu, %d, '%q', %d, %d, %d, %d, %lu, %d, %lu);",
|
"(42, 42, 'Its funny stuff', 23, 42, 43, 55, 66, 2, 54);";
|
||||||
42,
|
|
||||||
42,
|
|
||||||
"It's a rainy day",
|
setup(state);
|
||||||
23,
|
rc = sqlite3_open( TESTDB, &db);
|
||||||
42,
|
assert_int_equal(rc, SQLITE_OK);
|
||||||
42,
|
|
||||||
42,
|
rc = sqlite3_exec( db, sql, NULL, NULL, &errmsg );
|
||||||
42,
|
assert_int_equal(rc, SQLITE_OK);
|
||||||
2,
|
|
||||||
43);
|
rc = sqlite3_exec( db, sql2, NULL, NULL, &errmsg );
|
||||||
|
assert_int_equal(rc, SQLITE_OK);
|
||||||
|
|
||||||
|
sqlite3_close(db);
|
||||||
|
|
||||||
// rc = csync_statedb_insert(csync->statedb.db, stmt);
|
|
||||||
sqlite3_free(stmt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void teardown(void **state) {
|
static void teardown(void **state) {
|
||||||
|
@ -139,41 +127,6 @@ static void check_csync_statedb_query_statement(void **state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_csync_statedb_create_error(void **state)
|
|
||||||
{
|
|
||||||
CSYNC *csync = *state;
|
|
||||||
c_strlist_t *result;
|
|
||||||
|
|
||||||
result = csync_statedb_query(csync->statedb.db, "CREATE TABLE test(phash INTEGER, text VARCHAR(10));");
|
|
||||||
assert_non_null(result);
|
|
||||||
c_strlist_destroy(result);
|
|
||||||
|
|
||||||
result = csync_statedb_query(csync->statedb.db, "CREATE TABLE test(phash INTEGER, text VARCHAR(10));");
|
|
||||||
assert_null(result);
|
|
||||||
|
|
||||||
c_strlist_destroy(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void check_csync_statedb_insert_statement(void **state)
|
|
||||||
{
|
|
||||||
CSYNC *csync = *state;
|
|
||||||
c_strlist_t *result;
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
result = csync_statedb_query(csync->statedb.db, "CREATE TABLE test(phash INTEGER, text VARCHAR(10));");
|
|
||||||
assert_non_null(result);
|
|
||||||
c_strlist_destroy(result);
|
|
||||||
|
|
||||||
// rc = csync_statedb_insert(csync->statedb.db, "INSERT;");
|
|
||||||
assert_int_equal(rc, 0);
|
|
||||||
// rc = csync_statedb_insert(csync->statedb.db, "INSERT");
|
|
||||||
assert_int_equal(rc, 0);
|
|
||||||
// rc = csync_statedb_insert(csync->statedb.db, "");
|
|
||||||
assert_int_equal(rc, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void check_csync_statedb_drop_tables(void **state)
|
static void check_csync_statedb_drop_tables(void **state)
|
||||||
{
|
{
|
||||||
// CSYNC *csync = *state;
|
// CSYNC *csync = *state;
|
||||||
|
@ -255,8 +208,6 @@ int torture_run_tests(void)
|
||||||
{
|
{
|
||||||
const UnitTest tests[] = {
|
const UnitTest tests[] = {
|
||||||
unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown),
|
unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown),
|
||||||
unit_test_setup_teardown(check_csync_statedb_create_error, setup, teardown),
|
|
||||||
unit_test_setup_teardown(check_csync_statedb_insert_statement, setup, teardown),
|
|
||||||
unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown),
|
unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown),
|
||||||
unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown),
|
unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown),
|
||||||
unit_test_setup_teardown(check_csync_statedb_write, setup, teardown),
|
unit_test_setup_teardown(check_csync_statedb_write, setup, teardown),
|
||||||
|
|
|
@ -23,11 +23,64 @@
|
||||||
|
|
||||||
#define TESTDB "/tmp/check_csync/journal.db"
|
#define TESTDB "/tmp/check_csync/journal.db"
|
||||||
|
|
||||||
|
static int firstrun = 1;
|
||||||
|
|
||||||
|
static void statedb_create_metadata_table(sqlite3 *db)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if( db ) {
|
||||||
|
const char *sql = "CREATE TABLE IF NOT EXISTS metadata("
|
||||||
|
"phash INTEGER(8),"
|
||||||
|
"pathlen INTEGER,"
|
||||||
|
"path VARCHAR(4096),"
|
||||||
|
"inode INTEGER,"
|
||||||
|
"uid INTEGER,"
|
||||||
|
"gid INTEGER,"
|
||||||
|
"mode INTEGER,"
|
||||||
|
"modtime INTEGER(8),"
|
||||||
|
"type INTEGER,"
|
||||||
|
"md5 VARCHAR(32),"
|
||||||
|
"PRIMARY KEY(phash));";
|
||||||
|
|
||||||
|
rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
|
||||||
|
const char *msg = sqlite3_errmsg(db);
|
||||||
|
assert_int_equal( rc, SQLITE_OK );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void statedb_insert_metadata(sqlite3 *db)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if( db ) {
|
||||||
|
char *stmt = sqlite3_mprintf("INSERT INTO metadata"
|
||||||
|
"(phash, pathlen, path, inode, uid, gid, mode, modtime,type,md5) VALUES"
|
||||||
|
"(%lld, %d, '%q', %d, %d, %d, %d, %lld, %d, '%q');",
|
||||||
|
(long long signed int)42,
|
||||||
|
42,
|
||||||
|
"I_was_wurst_before_I_became_wurstsalat",
|
||||||
|
619070,
|
||||||
|
42,
|
||||||
|
42,
|
||||||
|
42,
|
||||||
|
(long long signed int)42,
|
||||||
|
0,
|
||||||
|
"4711");
|
||||||
|
|
||||||
|
char *errmsg;
|
||||||
|
rc = sqlite3_exec(db, stmt, NULL, NULL, &errmsg);
|
||||||
|
sqlite3_free(stmt);
|
||||||
|
assert_int_equal( rc, SQLITE_OK );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void setup(void **state)
|
static void setup(void **state)
|
||||||
{
|
{
|
||||||
CSYNC *csync;
|
CSYNC *csync;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
unlink(TESTDB);
|
||||||
rc = system("mkdir -p /tmp/check_csync");
|
rc = system("mkdir -p /tmp/check_csync");
|
||||||
assert_int_equal(rc, 0);
|
assert_int_equal(rc, 0);
|
||||||
rc = system("mkdir -p /tmp/check_csync1");
|
rc = system("mkdir -p /tmp/check_csync1");
|
||||||
|
@ -38,10 +91,21 @@ static void setup(void **state)
|
||||||
assert_int_equal(rc, 0);
|
assert_int_equal(rc, 0);
|
||||||
rc = csync_init(csync);
|
rc = csync_init(csync);
|
||||||
assert_int_equal(rc, 0);
|
assert_int_equal(rc, 0);
|
||||||
|
|
||||||
|
/* Create a new db with metadata */
|
||||||
|
sqlite3 *db;
|
||||||
|
csync->statedb.file = c_strdup(TESTDB);
|
||||||
|
rc = sqlite3_open(csync->statedb.file, &db);
|
||||||
|
statedb_create_metadata_table(db);
|
||||||
|
if( firstrun ) {
|
||||||
|
statedb_insert_metadata(db);
|
||||||
|
firstrun = 0;
|
||||||
|
}
|
||||||
|
sqlite3_close(db);
|
||||||
|
|
||||||
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
|
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
|
||||||
assert_int_equal(rc, 0);
|
assert_int_equal(rc, 0);
|
||||||
|
|
||||||
|
|
||||||
*state = csync;
|
*state = csync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +135,7 @@ static void teardown(void **state)
|
||||||
CSYNC *csync = *state;
|
CSYNC *csync = *state;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
unlink( csync->statedb.file);
|
||||||
rc = csync_destroy(csync);
|
rc = csync_destroy(csync);
|
||||||
assert_int_equal(rc, 0);
|
assert_int_equal(rc, 0);
|
||||||
|
|
||||||
|
@ -249,6 +314,7 @@ static void check_csync_detect_update_db_eval(void **state)
|
||||||
csync_vio_file_stat_destroy(fs);
|
csync_vio_file_stat_destroy(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void check_csync_detect_update_db_rename(void **state)
|
static void check_csync_detect_update_db_rename(void **state)
|
||||||
{
|
{
|
||||||
CSYNC *csync = *state;
|
CSYNC *csync = *state;
|
||||||
|
@ -256,27 +322,6 @@ static void check_csync_detect_update_db_rename(void **state)
|
||||||
|
|
||||||
csync_vio_file_stat_t *fs;
|
csync_vio_file_stat_t *fs;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char *stmt = NULL;
|
|
||||||
|
|
||||||
// rc = csync_statedb_create_tables(csync->statedb.db);
|
|
||||||
|
|
||||||
assert_int_equal(rc, 0);
|
|
||||||
stmt = sqlite3_mprintf("INSERT INTO metadata"
|
|
||||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime,type,md5) VALUES"
|
|
||||||
"(%lld, %d, '%q', %d, %d, %d, %d, %lld, %d, '%q');",
|
|
||||||
(long long signed int)42,
|
|
||||||
42,
|
|
||||||
"I_was_wurst_before_I_became_wurstsalat",
|
|
||||||
619070,
|
|
||||||
42,
|
|
||||||
42,
|
|
||||||
42,
|
|
||||||
(long long signed int)42,
|
|
||||||
0,
|
|
||||||
"4711");
|
|
||||||
|
|
||||||
// rc = csync_statedb_insert(csync->statedb.db, stmt);
|
|
||||||
sqlite3_free(stmt);
|
|
||||||
|
|
||||||
fs = create_fstat("wurst.txt", 0, 1, 42);
|
fs = create_fstat("wurst.txt", 0, 1, 42);
|
||||||
assert_non_null(fs);
|
assert_non_null(fs);
|
||||||
|
|
1
csync/tests/ownCloud/.gitignore
vendored
Normal file
1
csync/tests/ownCloud/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
t1.cfg
|
|
@ -63,7 +63,7 @@ our %config;
|
||||||
|
|
||||||
@ISA = qw(Exporter);
|
@ISA = qw(Exporter);
|
||||||
@EXPORT = qw( initTesting createRemoteDir removeRemoteDir createLocalDir cleanup csync
|
@EXPORT = qw( initTesting createRemoteDir removeRemoteDir createLocalDir cleanup csync
|
||||||
assertLocalDirs assertLocalAndRemoteDir glob_put put_to_dir
|
assertLocalDirs assertLocalAndRemoteDir glob_put put_to_dir
|
||||||
putToDirLWP localDir remoteDir localCleanup createLocalFile md5OfFile
|
putToDirLWP localDir remoteDir localCleanup createLocalFile md5OfFile
|
||||||
remoteCleanup server initLocalDir initRemoteDir moveRemoteFile
|
remoteCleanup server initLocalDir initRemoteDir moveRemoteFile
|
||||||
printInfo remoteFileId createShare removeShare assert
|
printInfo remoteFileId createShare removeShare assert
|
||||||
|
@ -73,12 +73,12 @@ sub server
|
||||||
{
|
{
|
||||||
return $owncloud;
|
return $owncloud;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub fromFileName($)
|
sub fromFileName($)
|
||||||
{
|
{
|
||||||
my ($file) = @_;
|
my ($file) = @_;
|
||||||
if ( $^O eq "darwin" ) {
|
if ( $^O eq "darwin" ) {
|
||||||
my $fromFileName = NFC( Encode::decode('utf-8', $file) );
|
my $fromFileName = NFC( Encode::decode('utf-8', $file) );
|
||||||
return $fromFileName;
|
return $fromFileName;
|
||||||
} else {
|
} else {
|
||||||
return $file;
|
return $file;
|
||||||
|
@ -89,7 +89,7 @@ sub fromFileName($)
|
||||||
sub initTesting(;$)
|
sub initTesting(;$)
|
||||||
{
|
{
|
||||||
my ($prefix) = @_;
|
my ($prefix) = @_;
|
||||||
|
|
||||||
my $cfgFile = "./t1.cfg";
|
my $cfgFile = "./t1.cfg";
|
||||||
$cfgFile = "/etc/ownCloud/t1.cfg" if( -r "/etc/ownCloud/t1.cfg" );
|
$cfgFile = "/etc/ownCloud/t1.cfg" if( -r "/etc/ownCloud/t1.cfg" );
|
||||||
|
|
||||||
|
@ -115,6 +115,8 @@ sub initTesting(;$)
|
||||||
|
|
||||||
$owncloud .= "/" unless( $owncloud =~ /\/$/ );
|
$owncloud .= "/" unless( $owncloud =~ /\/$/ );
|
||||||
|
|
||||||
|
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
|
||||||
|
|
||||||
print "Connecting to ownCloud at ". $owncloud ."\n";
|
print "Connecting to ownCloud at ". $owncloud ."\n";
|
||||||
|
|
||||||
# For SSL set the environment variable needed by the LWP module for SSL
|
# For SSL set the environment variable needed by the LWP module for SSL
|
||||||
|
@ -129,15 +131,15 @@ sub initTesting(;$)
|
||||||
-pass=> $passwd );
|
-pass=> $passwd );
|
||||||
# $d->DebugLevel(3);
|
# $d->DebugLevel(3);
|
||||||
$prefix = "t1" unless( defined $prefix );
|
$prefix = "t1" unless( defined $prefix );
|
||||||
|
|
||||||
my $dirId = sprintf("%02d", rand(100));
|
my $dirId = sprintf("%02d", rand(100));
|
||||||
my $dateTime = strftime('%Y%m%d%H%M%S',localtime);
|
my $dateTime = strftime('%Y%m%d%H%M%S',localtime);
|
||||||
my $dir = sprintf( "%s-%s-%s/", $prefix, $dateTime, $dirId );
|
my $dir = sprintf( "%s-%s-%s/", $prefix, $dateTime, $dirId );
|
||||||
|
|
||||||
$localDir = $dir;
|
$localDir = $dir;
|
||||||
$localDir .= "/" unless( $localDir =~ /\/$/ );
|
$localDir .= "/" unless( $localDir =~ /\/$/ );
|
||||||
$remoteDir = $dir;
|
$remoteDir = $dir;
|
||||||
|
|
||||||
initRemoteDir();
|
initRemoteDir();
|
||||||
initLocalDir();
|
initLocalDir();
|
||||||
printf( "Test directory name is %s\n", $dir );
|
printf( "Test directory name is %s\n", $dir );
|
||||||
|
@ -166,7 +168,9 @@ sub testDirUrl()
|
||||||
# the global var $remoteDir;
|
# the global var $remoteDir;
|
||||||
sub initRemoteDir
|
sub initRemoteDir
|
||||||
{
|
{
|
||||||
$d->open( $owncloud );
|
$d->open( $owncloud )
|
||||||
|
or die("Couldn't open $owncloud: " .$d->message . "\n");
|
||||||
|
|
||||||
my $url = testDirUrl();
|
my $url = testDirUrl();
|
||||||
|
|
||||||
my $re = $d->mkcol( $url );
|
my $re = $d->mkcol( $url );
|
||||||
|
@ -204,7 +208,7 @@ sub removeRemoteDir($;$)
|
||||||
if( $re == 0 ) {
|
if( $re == 0 ) {
|
||||||
print "Failed to remove directory <$url>:" . $d->message() ."\n";
|
print "Failed to remove directory <$url>:" . $d->message() ."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $re;
|
return $re;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +340,7 @@ sub localDir()
|
||||||
return $localDir;
|
return $localDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remoteDir()
|
sub remoteDir()
|
||||||
{
|
{
|
||||||
return $remoteDir;
|
return $remoteDir;
|
||||||
}
|
}
|
||||||
|
@ -381,7 +385,7 @@ sub traverse( $$;$ )
|
||||||
{
|
{
|
||||||
my ($remote, $acceptConflicts, $aurl) = @_;
|
my ($remote, $acceptConflicts, $aurl) = @_;
|
||||||
$remote .= '/' unless $remote =~ /(^|\/)$/;
|
$remote .= '/' unless $remote =~ /(^|\/)$/;
|
||||||
|
|
||||||
my $url = testDirUrl() . $remote;
|
my $url = testDirUrl() . $remote;
|
||||||
if( $aurl ) {
|
if( $aurl ) {
|
||||||
$url = $aurl . $remote;
|
$url = $aurl . $remote;
|
||||||
|
@ -489,13 +493,13 @@ sub glob_put( $$;$ )
|
||||||
print " *** Putting $lfile to $puturl\n";
|
print " *** Putting $lfile to $puturl\n";
|
||||||
# putToDirLWP( $lfile, $puturl );
|
# putToDirLWP( $lfile, $puturl );
|
||||||
put_to_dir($lfile, $puturl, $optionsRef);
|
put_to_dir($lfile, $puturl, $optionsRef);
|
||||||
|
|
||||||
# if( ! $d->put( -local=>$lfile, -url=> $puturl ) ) {
|
# if( ! $d->put( -local=>$lfile, -url=> $puturl ) ) {
|
||||||
#print " ### FAILED to put: ". $d->message . '\n';
|
#print " ### FAILED to put: ". $d->message . '\n';
|
||||||
# s}
|
# s}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +529,7 @@ sub put_to_dir( $$;$ )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# The HTTP DAV module often does a PROPFIND before it really PUTs. That
|
# The HTTP DAV module often does a PROPFIND before it really PUTs. That
|
||||||
# is not neccessary if we know that the directory is really there.
|
# is not neccessary if we know that the directory is really there.
|
||||||
# Use this function in this case:
|
# Use this function in this case:
|
||||||
sub putToDirLWP($$)
|
sub putToDirLWP($$)
|
||||||
|
@ -545,13 +549,13 @@ sub putToDirLWP($$)
|
||||||
my $string = <FILE>;
|
my $string = <FILE>;
|
||||||
close FILE;
|
close FILE;
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new();
|
my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 });
|
||||||
$ua->agent( "ownCloudTest_$localDir");
|
$ua->agent( "ownCloudTest_$localDir");
|
||||||
my $req = PUT $puturl, Content_Type => 'application/octet-stream',
|
my $req = PUT $puturl, Content_Type => 'application/octet-stream',
|
||||||
Content => $string;
|
Content => $string;
|
||||||
$req->authorization_basic($user, $passwd);
|
$req->authorization_basic($user, $passwd);
|
||||||
my $response = $ua->request($req);
|
my $response = $ua->request($req);
|
||||||
|
|
||||||
if ($response->is_success()) {
|
if ($response->is_success()) {
|
||||||
# print "OK: ", $response->content;
|
# print "OK: ", $response->content;
|
||||||
} else {
|
} else {
|
||||||
|
@ -579,7 +583,7 @@ sub getToFileLWP( $$ )
|
||||||
my $geturl = testDirUrl() . $file;
|
my $geturl = testDirUrl() . $file;
|
||||||
print "GETting $geturl to $localFile\n";
|
print "GETting $geturl to $localFile\n";
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new();
|
my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 });
|
||||||
$ua->agent( "ownCloudTest_$localDir");
|
$ua->agent( "ownCloudTest_$localDir");
|
||||||
$ua->credentials( server(), "foo", $user, $passwd);
|
$ua->credentials( server(), "foo", $user, $passwd);
|
||||||
my $req = $ua->get($geturl, ":content_file" => $localFile);
|
my $req = $ua->get($geturl, ":content_file" => $localFile);
|
||||||
|
@ -594,15 +598,15 @@ sub getToFileLWP( $$ )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub createLocalFile( $$ )
|
sub createLocalFile( $$ )
|
||||||
{
|
{
|
||||||
my ($fname, $size) = @_;
|
my ($fname, $size) = @_;
|
||||||
$size = 1024 unless( $size );
|
$size = 1024 unless( $size );
|
||||||
|
|
||||||
my $md5 = Digest::MD5->new;
|
my $md5 = Digest::MD5->new;
|
||||||
|
|
||||||
open(FILE, ">", $fname) or die "Can't open $fname for writing ($!)";
|
open(FILE, ">", $fname) or die "Can't open $fname for writing ($!)";
|
||||||
|
|
||||||
my $minimum = 32;
|
my $minimum = 32;
|
||||||
my $range = 96;
|
my $range = 96;
|
||||||
|
|
||||||
|
@ -620,20 +624,20 @@ sub createLocalFile( $$ )
|
||||||
print FILE $s;
|
print FILE $s;
|
||||||
$md5->add($s);
|
$md5->add($s);
|
||||||
close FILE;
|
close FILE;
|
||||||
return $md5->hexdigest;
|
return $md5->hexdigest;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub md5OfFile( $ )
|
sub md5OfFile( $ )
|
||||||
{
|
{
|
||||||
my ($file) = @_;
|
my ($file) = @_;
|
||||||
|
|
||||||
open FILE, "$file";
|
open FILE, "$file";
|
||||||
|
|
||||||
my $ctx = Digest::MD5->new;
|
my $ctx = Digest::MD5->new;
|
||||||
$ctx->addfile (*FILE);
|
$ctx->addfile (*FILE);
|
||||||
my $hash = $ctx->hexdigest;
|
my $hash = $ctx->hexdigest;
|
||||||
close (FILE);
|
close (FILE);
|
||||||
|
|
||||||
return $hash;
|
return $hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,27 +651,27 @@ sub moveRemoteFile($$;$)
|
||||||
|
|
||||||
my $fromUrl = testDirUrl(). $from;
|
my $fromUrl = testDirUrl(). $from;
|
||||||
my $toUrl = testDirUrl() . $to;
|
my $toUrl = testDirUrl() . $to;
|
||||||
|
|
||||||
if( $no_testdir ) {
|
if( $no_testdir ) {
|
||||||
$fromUrl = $from;
|
$fromUrl = $from;
|
||||||
$toUrl = $to;
|
$toUrl = $to;
|
||||||
}
|
}
|
||||||
|
|
||||||
$d->move($fromUrl, $toUrl);
|
$d->move($fromUrl, $toUrl);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub printInfo($)
|
sub printInfo($)
|
||||||
{
|
{
|
||||||
my ($info) = @_;
|
my ($info) = @_;
|
||||||
my $tt = 6+length( $info );
|
my $tt = 6+length( $info );
|
||||||
|
|
||||||
print "#" x $tt;
|
print "#" x $tt;
|
||||||
printf( "\n# %2d. %s", $infoCnt, $info );
|
printf( "\n# %2d. %s", $infoCnt, $info );
|
||||||
print "\n" unless $info =~ /\n$/;
|
print "\n" unless $info =~ /\n$/;
|
||||||
print "#" x $tt;
|
print "#" x $tt;
|
||||||
print "\n";
|
print "\n";
|
||||||
|
|
||||||
$infoCnt++;
|
$infoCnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,16 +722,15 @@ sub createShare($$)
|
||||||
my $re = $dd->mkcol( $url );
|
my $re = $dd->mkcol( $url );
|
||||||
if( $re == 0 ) {
|
if( $re == 0 ) {
|
||||||
print "Failed to create test dir $url\n";
|
print "Failed to create test dir $url\n";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new();
|
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 } );
|
||||||
$ua->agent( "ownCloudTest_sharing");
|
$ua->agent( "ownCloudTest_sharing");
|
||||||
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares
|
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares
|
||||||
my $puturl = $ocs_url . "apps/files_sharing/api/v1/shares";
|
my $puturl = $ocs_url . "ocs/v1.php/apps/files_sharing/api/v1/shares";
|
||||||
|
|
||||||
my $string = "path=$dir&shareType=0&shareWith=$user&publicUpload=false&permissions=$readWrite";
|
my $string = "path=$dir&shareType=0&shareWith=$user&publicUpload=false&permissions=$readWrite";
|
||||||
print ">>>>>>>>>> $string\n";
|
print ">>>>>>>>>> $puturl $string\n";
|
||||||
|
|
||||||
my $req = POST $puturl, Content => $string;
|
my $req = POST $puturl, Content => $string;
|
||||||
$req->authorization_basic($share_user, $share_passwd);
|
$req->authorization_basic($share_user, $share_passwd);
|
||||||
|
@ -757,17 +760,16 @@ sub removeShare($$)
|
||||||
-pass => $share_passwd );
|
-pass => $share_passwd );
|
||||||
$dd->open( $owncloud);
|
$dd->open( $owncloud);
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new();
|
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
|
||||||
$ua->agent( "ownCloudTest_sharing");
|
$ua->agent( "ownCloudTest_sharing");
|
||||||
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares
|
|
||||||
my $url = $ocs_url . "apps/files_sharing/api/v1/shares/" . $shareId;
|
my $url = $ocs_url . "ocs/v1.php/apps/files_sharing/api/v1/shares/" . $shareId;
|
||||||
|
|
||||||
my $req = DELETE $url;
|
my $req = DELETE $url;
|
||||||
$req->authorization_basic($share_user, $share_passwd);
|
$req->authorization_basic($share_user, $share_passwd);
|
||||||
my $response = $ua->request($req);
|
my $response = $ua->request($req);
|
||||||
|
|
||||||
if ($response->is_success()) {
|
if ($response->is_success()) {
|
||||||
# print "OK: ", $response->content;
|
|
||||||
print $response->decoded_content;
|
print $response->decoded_content;
|
||||||
if( $response->decoded_content =~ /<status_code>(\d+)<\/status_code>/m) {
|
if( $response->decoded_content =~ /<status_code>(\d+)<\/status_code>/m) {
|
||||||
my $code = $1;
|
my $code = $1;
|
||||||
|
|
|
@ -169,6 +169,19 @@ assertLocalAndRemoteDir( '', 0);
|
||||||
assert( -e localDir().'remoteToLocal1/rtlX' );
|
assert( -e localDir().'remoteToLocal1/rtlX' );
|
||||||
assert( -e localDir().'remoteToLocal1/rtlX/rtl11/file.txt' );
|
assert( -e localDir().'remoteToLocal1/rtlX/rtl11/file.txt' );
|
||||||
|
|
||||||
|
printInfo( "Remove a directory on the server with new files on the client");
|
||||||
|
removeRemoteDir('remoteToLocal1/rtlX');
|
||||||
|
system("echo hello > " . localDir(). "remoteToLocal1/rtlX/rtl11/hello.txt");
|
||||||
|
csync();
|
||||||
|
assertLocalAndRemoteDir( '', 0);
|
||||||
|
# file.txt must be gone because the directory was removed on the server, but hello.txt must be there
|
||||||
|
# as it is a new file
|
||||||
|
assert( ! -e localDir().'remoteToLocal1/rtlX/rtl11/file.txt' );
|
||||||
|
assert( -e localDir().'remoteToLocal1/rtlX/rtl11/hello.txt' );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ==================================================================
|
# ==================================================================
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
|
@ -91,7 +91,10 @@ assertLocalAndRemoteDir( 'newdir', 0);
|
||||||
assert( -e localDir().'newdir/rtl1/rtl11/newfile.dat' );
|
assert( -e localDir().'newdir/rtl1/rtl11/newfile.dat' );
|
||||||
assert( -e localDir().'newdir/rtl1/rtl11/myfile.txt' );
|
assert( -e localDir().'newdir/rtl1/rtl11/myfile.txt' );
|
||||||
assert( ! -e localDir().'newdir/rtl11/test.txt' );
|
assert( ! -e localDir().'newdir/rtl11/test.txt' );
|
||||||
assert( ! -e localDir().'remoteToLocal1' );
|
# BUG! remoteToLocal1 is not deleted because changes were detected
|
||||||
|
# (even if the changed fileswere moved)
|
||||||
|
# assert( ! -e localDir().'remoteToLocal1' );
|
||||||
|
assert( ! -e localDir().'remoteToLocal1/rtl1' );
|
||||||
|
|
||||||
printInfo("Move file and create another one with the same name.");
|
printInfo("Move file and create another one with the same name.");
|
||||||
move( localDir() . 'newdir/myfile.txt', localDir() . 'newdir/oldfile.txt' );
|
move( localDir() . 'newdir/myfile.txt', localDir() . 'newdir/oldfile.txt' );
|
||||||
|
|
|
@ -33,11 +33,13 @@ print "Hello, this is t6, a tester for csync with ownCloud.\n";
|
||||||
|
|
||||||
initTesting();
|
initTesting();
|
||||||
|
|
||||||
sub createPostUpdateScript()
|
sub createPostUpdateScript($)
|
||||||
{
|
{
|
||||||
my $srcFile = localDir()."BIG.file";
|
my ($name) = @_;
|
||||||
|
|
||||||
|
my $srcFile = localDir().'BIG1.file';
|
||||||
my $cred = configValue("user") . ":" . configValue("passwd");
|
my $cred = configValue("user") . ":" . configValue("passwd");
|
||||||
my $cmd = "curl -T $srcFile -u $cred " . testDirUrl();
|
my $cmd = "curl -T $srcFile -u $cred " . testDirUrl().$name;
|
||||||
my $script = "/tmp/post_update_script.sh";
|
my $script = "/tmp/post_update_script.sh";
|
||||||
open SC, ">$script" || die("Can not create script file");
|
open SC, ">$script" || die("Can not create script file");
|
||||||
print SC "#!/bin/bash\n";
|
print SC "#!/bin/bash\n";
|
||||||
|
@ -48,11 +50,11 @@ sub createPostUpdateScript()
|
||||||
return $script;
|
return $script;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getETagFromJournal($)
|
sub getETagFromJournal($$)
|
||||||
{
|
{
|
||||||
my ($num) = @_;
|
my ($name,$num) = @_;
|
||||||
|
|
||||||
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='BIG.file';\"";
|
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='$name';\"";
|
||||||
open(my $fh, '-|', $sql) or die $!;
|
open(my $fh, '-|', $sql) or die $!;
|
||||||
my $etag = <$fh>;
|
my $etag = <$fh>;
|
||||||
close $fh;
|
close $fh;
|
||||||
|
@ -61,14 +63,14 @@ sub getETagFromJournal($)
|
||||||
return $etag;
|
return $etag;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub chunkFileTest( $$ )
|
sub chunkFileTest( $$ )
|
||||||
{
|
{
|
||||||
my ($name, $size) = @_;
|
my ($name, $size) = @_;
|
||||||
|
|
||||||
# Big file chunking
|
# Big file chunking
|
||||||
createLocalFile( localDir().$name, $size );
|
createLocalFile( localDir().$name, $size );
|
||||||
assert( -e localDir().$name );
|
assert( -e localDir().$name );
|
||||||
|
|
||||||
my $bigMd5 = md5OfFile( localDir().$name );
|
my $bigMd5 = md5OfFile( localDir().$name );
|
||||||
|
|
||||||
csync();
|
csync();
|
||||||
|
@ -89,26 +91,39 @@ sub chunkFileTest( $$ )
|
||||||
}
|
}
|
||||||
|
|
||||||
printInfo("Big file that needs chunking with default chunk size");
|
printInfo("Big file that needs chunking with default chunk size");
|
||||||
chunkFileTest( "BIG.file", 23251233 );
|
chunkFileTest( "BIG1.file", 23251233 );
|
||||||
|
|
||||||
printInfo("Update the existing file and trigger reupload");
|
printInfo("Update the existing file and trigger reupload");
|
||||||
# change the existing file again -> update
|
# change the existing file again -> update
|
||||||
chunkFileTest( "BIG.file", 21762122 );
|
chunkFileTest( "BIG2.file", 21762122 );
|
||||||
|
|
||||||
printInfo("Cause a precondition failed error");
|
printInfo("Cause a precondition failed error");
|
||||||
# Now overwrite the existing file to change it
|
# Now overwrite the existing file to change it
|
||||||
createLocalFile( localDir()."BIG.file", 21832199 );
|
createLocalFile( localDir()."BIG3.file", 21832 );
|
||||||
|
sleep(2);
|
||||||
|
csync();
|
||||||
|
createLocalFile( localDir().'BIG3.file', 34323 );
|
||||||
|
sleep(2);
|
||||||
# and create a post update script
|
# and create a post update script
|
||||||
my $script = createPostUpdateScript();
|
my $script = createPostUpdateScript('BIG3.file');
|
||||||
$ENV{'OWNCLOUD_POST_UPDATE_SCRIPT'} = $script;
|
$ENV{'OWNCLOUD_POST_UPDATE_SCRIPT'} = $script;
|
||||||
|
|
||||||
# Save the etag before the sync
|
# Save the etag before the sync
|
||||||
my $firstETag = getETagFromJournal('First');
|
my $firstETag = getETagFromJournal('BIG3.file', 'First');
|
||||||
csync(); # Sync, which ends in a precondition failed error
|
sleep(2);
|
||||||
|
csync(); # Sync, which ends in a precondition failed error
|
||||||
# get the etag again. It has to be unchanged because of the error.
|
# get the etag again. It has to be unchanged because of the error.
|
||||||
my $secondETag = getETagFromJournal('Second');
|
my $secondETag = getETagFromJournal('BIG3.file', 'Second');
|
||||||
assert( $firstETag eq $secondETag, "Different ETags, no precondition error." );
|
|
||||||
|
|
||||||
|
# Now the result is that there is a conflict file because since 1.7
|
||||||
|
# the sync is stopped on preconditoin failed and done again.
|
||||||
|
my $seen = 0;
|
||||||
|
opendir(my $dh, localDir() );
|
||||||
|
while(readdir $dh) {
|
||||||
|
$seen = 1 if ( /BIG3_conflict.*\.file/ );
|
||||||
|
}
|
||||||
|
closedir $dh;
|
||||||
|
assert( $seen == 1, "No conflict file created on precondition failed!" );
|
||||||
unlink($script);
|
unlink($script);
|
||||||
|
|
||||||
# Set a custom chunk size in environment.
|
# Set a custom chunk size in environment.
|
||||||
|
|
|
@ -48,6 +48,7 @@ mkdir($tmpdir);
|
||||||
createLocalFile( $tmpdir . "HELLO.dat", 100 );
|
createLocalFile( $tmpdir . "HELLO.dat", 100 );
|
||||||
createLocalFile( $tmpdir . "Hello.dat", 150 );
|
createLocalFile( $tmpdir . "Hello.dat", 150 );
|
||||||
createLocalFile( $tmpdir . "Normal.dat", 110 );
|
createLocalFile( $tmpdir . "Normal.dat", 110 );
|
||||||
|
createLocalFile( $tmpdir . "test.dat", 170 );
|
||||||
|
|
||||||
#put them in some directories
|
#put them in some directories
|
||||||
createRemoteDir( "dir" );
|
createRemoteDir( "dir" );
|
||||||
|
@ -73,14 +74,20 @@ assertLocalAndRemoteDir( '', 0);
|
||||||
|
|
||||||
printInfo( "Renaming one file to the same name as another one with different casing" );
|
printInfo( "Renaming one file to the same name as another one with different casing" );
|
||||||
moveRemoteFile( 'dir/Hello.dat', 'dir/NORMAL.dat');
|
moveRemoteFile( 'dir/Hello.dat', 'dir/NORMAL.dat');
|
||||||
|
moveRemoteFile( 'dir/test.dat', 'dir/TEST.dat');
|
||||||
|
|
||||||
csync();
|
csync();
|
||||||
|
|
||||||
#It should not have do the move
|
# Hello -> NORMAL should not have do the move since the case conflict
|
||||||
assert( -e localDir() . 'dir/Hello.dat' );
|
assert( -e localDir() . 'dir/Hello.dat' );
|
||||||
assert( !-e localDir() . 'dir/NORMAL.dat' );
|
assert( !-e localDir() . 'dir/NORMAL.dat' );
|
||||||
assert( -e localDir() . 'dir/Normal.dat' );
|
assert( -e localDir() . 'dir/Normal.dat' );
|
||||||
|
|
||||||
|
#test->TEST should have been worked.
|
||||||
|
assert( -e localDir() . 'dir/TEST.dat' );
|
||||||
|
assert( !-e localDir() . 'dir/test.dat' );
|
||||||
|
|
||||||
|
|
||||||
printInfo( "Another directory with the same name but different casing is created" );
|
printInfo( "Another directory with the same name but different casing is created" );
|
||||||
|
|
||||||
createRemoteDir( "DIR" );
|
createRemoteDir( "DIR" );
|
||||||
|
|
|
@ -3,7 +3,7 @@ if(APPLE)
|
||||||
add_custom_target( mac_overlayplugin ALL
|
add_custom_target( mac_overlayplugin ALL
|
||||||
xcodebuild -workspace ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloud.xcworkspace
|
xcodebuild -workspace ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloud.xcworkspace
|
||||||
-scheme OwnCloudFinder.osax SYMROOT=${CMAKE_CURRENT_BINARY_DIR} archive
|
-scheme OwnCloudFinder.osax SYMROOT=${CMAKE_CURRENT_BINARY_DIR} archive
|
||||||
COMMENT building Mac Overlay iccons)
|
COMMENT building Mac Overlay icons)
|
||||||
|
|
||||||
INSTALL( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/OwnCloudFinder.osax/Contents
|
INSTALL( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/OwnCloudFinder.osax/Contents
|
||||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/Library/ScriptingAdditions/OwnCloudFinder.osax/ )
|
DESTINATION ${CMAKE_INSTALL_PREFIX}/Library/ScriptingAdditions/OwnCloudFinder.osax/ )
|
||||||
|
|
|
@ -118,7 +118,7 @@ static ContentManager* sharedInstance = nil;
|
||||||
|
|
||||||
if (![_fileNamesCache objectForKey:normalizedPath] || ![[_fileNamesCache objectForKey:normalizedPath] isEqualTo:res]) {
|
if (![_fileNamesCache objectForKey:normalizedPath] || ![[_fileNamesCache objectForKey:normalizedPath] isEqualTo:res]) {
|
||||||
[_fileNamesCache setObject:res forKey:normalizedPath];
|
[_fileNamesCache setObject:res forKey:normalizedPath];
|
||||||
// NSLog(@"SET value %d", [res intValue]);
|
//NSLog(@"SET value %d %@", [res intValue], normalizedPath);
|
||||||
_hasChangedContent = YES;
|
_hasChangedContent = YES;
|
||||||
[self performSelector:@selector(repaintAllWindowsIfNeeded) withObject:0 afterDelay:1.0]; // 1 sec
|
[self performSelector:@selector(repaintAllWindowsIfNeeded) withObject:0 afterDelay:1.0]; // 1 sec
|
||||||
}
|
}
|
||||||
|
@ -139,12 +139,12 @@ static ContentManager* sharedInstance = nil;
|
||||||
}
|
}
|
||||||
NSString* normalizedPath = [path decomposedStringWithCanonicalMapping];
|
NSString* normalizedPath = [path decomposedStringWithCanonicalMapping];
|
||||||
|
|
||||||
if (![[RequestManager sharedInstance] isRegisteredPath:normalizedPath]) {
|
if (![[RequestManager sharedInstance] isRegisteredPath:normalizedPath isDirectory:isDir]) {
|
||||||
return [NSNumber numberWithInt:0];
|
return [NSNumber numberWithInt:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSNumber* result = [_fileNamesCache objectForKey:normalizedPath];
|
NSNumber* result = [_fileNamesCache objectForKey:normalizedPath];
|
||||||
// NSLog(@"XXXXXXX Asking for icon for path %@ = %d",path, [result intValue]);
|
// NSLog(@"XXXXXXX Asking for icon for path %@ = %d",normalizedPath, [result intValue]);
|
||||||
|
|
||||||
if( result == nil ) {
|
if( result == nil ) {
|
||||||
// start the async call
|
// start the async call
|
||||||
|
@ -185,22 +185,29 @@ static ContentManager* sharedInstance = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( [keysToDelete count] > 0 ) {
|
if( [keysToDelete count] > 0 ) {
|
||||||
NSLog( @"Entries to delete: %d", [keysToDelete count]);
|
NSLog( @"Entries to delete: %lu", (unsigned long)[keysToDelete count]);
|
||||||
[_fileNamesCache removeObjectsForKeys:keysToDelete];
|
[_fileNamesCache removeObjectsForKeys:keysToDelete];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reFetchFileNameCacheForPath:(NSString*)path
|
- (void)reFetchFileNameCacheForPath:(NSString*)path
|
||||||
{
|
{
|
||||||
NSLog(@"%@", NSStringFromSelector(_cmd));
|
NSLog(@"%@", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
for (id p in [_fileNamesCache keyEnumerator]) {
|
for (id p in [_fileNamesCache keyEnumerator]) {
|
||||||
if ( path && [p hasPrefix:path] ) {
|
if ( path && [p hasPrefix:path] ) {
|
||||||
NSNumber *askState = [[RequestManager sharedInstance] askForIcon:p isDirectory:false]; // FIXME
|
[[RequestManager sharedInstance] askForIcon:p isDirectory:false]; // FIXME isDirectory parameter
|
||||||
//[_fileNamesCache setObject:askState forKey:p];
|
//[_fileNamesCache setObject:askState forKey:p]; We don't do this since we want to keep the old icon meanwhile
|
||||||
NSLog(@"%@ %@", NSStringFromSelector(_cmd), p);
|
//NSLog(@"%@ %@", NSStringFromSelector(_cmd), p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ask for directory itself
|
||||||
|
if ([path hasSuffix:@"/"]) {
|
||||||
|
path = [path substringToIndex:path.length - 1];
|
||||||
|
}
|
||||||
|
[[RequestManager sharedInstance] askForIcon:path isDirectory:true];
|
||||||
|
//NSLog(@"%@ %@", NSStringFromSelector(_cmd), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -226,7 +233,7 @@ static ContentManager* sharedInstance = nil;
|
||||||
- (void)repaintAllWindowsIfNeeded
|
- (void)repaintAllWindowsIfNeeded
|
||||||
{
|
{
|
||||||
if (!_hasChangedContent) {
|
if (!_hasChangedContent) {
|
||||||
NSLog(@"%@ Repaint scheduled but not needed", NSStringFromSelector(_cmd));
|
//NSLog(@"%@ Repaint scheduled but not needed", NSStringFromSelector(_cmd));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.h
Normal file
24
shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// FinishedIconCache.h
|
||||||
|
// OwnCloudFinder
|
||||||
|
//
|
||||||
|
// Created by Markus Goetz on 01/10/14.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface FinishedIconCache : NSObject {
|
||||||
|
NSCache *_cache;
|
||||||
|
long long _hits;
|
||||||
|
long long _misses;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (FinishedIconCache*)sharedInstance;
|
||||||
|
|
||||||
|
- (NSImage*)getIcon:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h;
|
||||||
|
- (void)registerIcon:(NSImage*)icon withFileName:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h;
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
91
shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.m
Normal file
91
shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.m
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
//
|
||||||
|
// FinishedIconCache.m
|
||||||
|
// OwnCloudFinder
|
||||||
|
//
|
||||||
|
// Created by Markus Goetz on 01/10/14.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FinishedIconCache.h"
|
||||||
|
|
||||||
|
|
||||||
|
@interface FinishedIconCacheItem : NSObject
|
||||||
|
@property (nonatomic, strong) NSImage *icon;
|
||||||
|
@property (nonatomic) NSTimeInterval maxAge;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FinishedIconCacheItem
|
||||||
|
@synthesize icon;
|
||||||
|
@synthesize maxAge;
|
||||||
|
- (void)dealloc {
|
||||||
|
//NSLog(@"RELEASE %@ %@", self, self.icon);
|
||||||
|
if (self.icon) {
|
||||||
|
[self->icon release];
|
||||||
|
}
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FinishedIconCache
|
||||||
|
|
||||||
|
static FinishedIconCache* sharedInstance = nil;
|
||||||
|
|
||||||
|
- init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
_cache = [[NSCache alloc] init];
|
||||||
|
_cache.totalCostLimit = (2880 * 1800); // mbp15 screen size
|
||||||
|
_hits = 0;
|
||||||
|
_misses = 0;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[_cache dealloc];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (FinishedIconCache*)sharedInstance
|
||||||
|
{
|
||||||
|
@synchronized(self)
|
||||||
|
{
|
||||||
|
if (sharedInstance == nil)
|
||||||
|
{
|
||||||
|
sharedInstance = [[self alloc] init];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (NSImage*)getIcon:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h
|
||||||
|
{
|
||||||
|
NSString *cacheKey = [NSString stringWithFormat:@"%@--%d--%f%f", fileName, idx, w,h];
|
||||||
|
FinishedIconCacheItem *item = [_cache objectForKey:cacheKey];
|
||||||
|
if (item) {
|
||||||
|
if (item.maxAge > [[NSDate date] timeIntervalSinceReferenceDate]) {
|
||||||
|
_hits++;
|
||||||
|
return item.icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_misses++;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)registerIcon:(NSImage*)icon withFileName:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h
|
||||||
|
{
|
||||||
|
NSString *cacheKey = [NSString stringWithFormat:@"%@--%d--%f%f", fileName, idx, w, h];
|
||||||
|
FinishedIconCacheItem *item = [[FinishedIconCacheItem alloc] init];
|
||||||
|
item.icon = icon;
|
||||||
|
// max age between 1 sec and 5 sec
|
||||||
|
item.maxAge = [[NSDate date] timeIntervalSinceReferenceDate] + 1.0 + 4.0*((double)arc4random() / 0x100000000);
|
||||||
|
[_cache setObject:item forKey:cacheKey cost:w*h];
|
||||||
|
[item release];
|
||||||
|
//NSLog(@"CACHE hit/miss ratio: %f", (float)_hits/(float)_misses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -3,25 +3,77 @@
|
||||||
//
|
//
|
||||||
// This class is in the public domain.
|
// This class is in the public domain.
|
||||||
// Originally created by Robbie Hanson in Q3 2010.
|
// Originally created by Robbie Hanson in Q3 2010.
|
||||||
// Updated and maintained by Deusty LLC and the Mac development community.
|
// Updated and maintained by Deusty LLC and the Apple development community.
|
||||||
//
|
//
|
||||||
// http://code.google.com/p/cocoaasyncsocket/
|
// https://github.com/robbiehanson/CocoaAsyncSocket
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <Security/Security.h>
|
#import <Security/Security.h>
|
||||||
|
#import <Security/SecureTransport.h>
|
||||||
#import <dispatch/dispatch.h>
|
#import <dispatch/dispatch.h>
|
||||||
|
|
||||||
@class GCDAsyncReadPacket;
|
@class GCDAsyncReadPacket;
|
||||||
@class GCDAsyncWritePacket;
|
@class GCDAsyncWritePacket;
|
||||||
|
@class GCDAsyncSocketPreBuffer;
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
|
||||||
|
// Compiling for iOS
|
||||||
|
|
||||||
|
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 50000 // iOS 5.0 supported
|
||||||
|
|
||||||
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 50000 // iOS 5.0 supported and required
|
||||||
|
|
||||||
|
#define IS_SECURE_TRANSPORT_AVAILABLE YES
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_AVAILABLE 1
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_UNAVAILABLE 0
|
||||||
|
|
||||||
|
#else // iOS 5.0 supported but not required
|
||||||
|
|
||||||
|
#ifndef NSFoundationVersionNumber_iPhoneOS_5_0
|
||||||
|
#define NSFoundationVersionNumber_iPhoneOS_5_0 881.00
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define IS_SECURE_TRANSPORT_AVAILABLE (NSFoundationVersionNumber >= NSFoundationVersionNumber_iPhoneOS_5_0)
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_AVAILABLE 1
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_UNAVAILABLE 1
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else // iOS 5.0 not supported
|
||||||
|
|
||||||
|
#define IS_SECURE_TRANSPORT_AVAILABLE NO
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_AVAILABLE 0
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_UNAVAILABLE 1
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Compiling for Mac OS X
|
||||||
|
|
||||||
|
#define IS_SECURE_TRANSPORT_AVAILABLE YES
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_AVAILABLE 1
|
||||||
|
#define SECURE_TRANSPORT_MAYBE_UNAVAILABLE 0
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
extern NSString *const GCDAsyncSocketException;
|
extern NSString *const GCDAsyncSocketException;
|
||||||
extern NSString *const GCDAsyncSocketErrorDomain;
|
extern NSString *const GCDAsyncSocketErrorDomain;
|
||||||
|
|
||||||
#if !TARGET_OS_IPHONE
|
extern NSString *const GCDAsyncSocketQueueName;
|
||||||
|
extern NSString *const GCDAsyncSocketThreadName;
|
||||||
|
|
||||||
|
#if SECURE_TRANSPORT_MAYBE_AVAILABLE
|
||||||
extern NSString *const GCDAsyncSocketSSLCipherSuites;
|
extern NSString *const GCDAsyncSocketSSLCipherSuites;
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
extern NSString *const GCDAsyncSocketSSLProtocolVersionMin;
|
||||||
|
extern NSString *const GCDAsyncSocketSSLProtocolVersionMax;
|
||||||
|
#else
|
||||||
extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters;
|
extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
enum GCDAsyncSocketError
|
enum GCDAsyncSocketError
|
||||||
{
|
{
|
||||||
|
@ -42,51 +94,6 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@interface GCDAsyncSocket : NSObject
|
@interface GCDAsyncSocket : NSObject
|
||||||
{
|
|
||||||
uint32_t flags;
|
|
||||||
uint16_t config;
|
|
||||||
|
|
||||||
id delegate;
|
|
||||||
dispatch_queue_t delegateQueue;
|
|
||||||
|
|
||||||
int socket4FD;
|
|
||||||
int socket6FD;
|
|
||||||
int connectIndex;
|
|
||||||
NSData * connectInterface4;
|
|
||||||
NSData * connectInterface6;
|
|
||||||
|
|
||||||
dispatch_queue_t socketQueue;
|
|
||||||
|
|
||||||
dispatch_source_t accept4Source;
|
|
||||||
dispatch_source_t accept6Source;
|
|
||||||
dispatch_source_t connectTimer;
|
|
||||||
dispatch_source_t readSource;
|
|
||||||
dispatch_source_t writeSource;
|
|
||||||
dispatch_source_t readTimer;
|
|
||||||
dispatch_source_t writeTimer;
|
|
||||||
|
|
||||||
NSMutableArray *readQueue;
|
|
||||||
NSMutableArray *writeQueue;
|
|
||||||
|
|
||||||
GCDAsyncReadPacket *currentRead;
|
|
||||||
GCDAsyncWritePacket *currentWrite;
|
|
||||||
|
|
||||||
unsigned long socketFDBytesAvailable;
|
|
||||||
|
|
||||||
NSMutableData *partialReadBuffer;
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
CFStreamClientContext streamContext;
|
|
||||||
CFReadStreamRef readStream;
|
|
||||||
CFWriteStreamRef writeStream;
|
|
||||||
#else
|
|
||||||
SSLContextRef sslContext;
|
|
||||||
NSMutableData *sslReadBuffer;
|
|
||||||
size_t sslWriteCachedLength;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
id userData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GCDAsyncSocket uses the standard delegate paradigm,
|
* GCDAsyncSocket uses the standard delegate paradigm,
|
||||||
|
@ -99,6 +106,8 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
* The socket queue is optional.
|
* The socket queue is optional.
|
||||||
* If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue.
|
* If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue.
|
||||||
* If you choose to provide a socket queue, the socket queue must not be a concurrent queue.
|
* If you choose to provide a socket queue, the socket queue must not be a concurrent queue.
|
||||||
|
* If you choose to provide a socket queue, and the socket queue has a configured target queue,
|
||||||
|
* then please see the discussion for the method markSocketQueueTargetQueue.
|
||||||
*
|
*
|
||||||
* The delegate queue and socket queue can optionally be the same.
|
* The delegate queue and socket queue can optionally be the same.
|
||||||
**/
|
**/
|
||||||
|
@ -121,39 +130,6 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
- (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
|
- (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
|
||||||
- (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
|
- (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
|
||||||
|
|
||||||
/**
|
|
||||||
* Traditionally sockets are not closed until the conversation is over.
|
|
||||||
* However, it is technically possible for the remote enpoint to close its write stream.
|
|
||||||
* Our socket would then be notified that there is no more data to be read,
|
|
||||||
* but our socket would still be writeable and the remote endpoint could continue to receive our data.
|
|
||||||
*
|
|
||||||
* The argument for this confusing functionality stems from the idea that a client could shut down its
|
|
||||||
* write stream after sending a request to the server, thus notifying the server there are to be no further requests.
|
|
||||||
* In practice, however, this technique did little to help server developers.
|
|
||||||
*
|
|
||||||
* To make matters worse, from a TCP perspective there is no way to tell the difference from a read stream close
|
|
||||||
* and a full socket close. They both result in the TCP stack receiving a FIN packet. The only way to tell
|
|
||||||
* is by continuing to write to the socket. If it was only a read stream close, then writes will continue to work.
|
|
||||||
* Otherwise an error will be occur shortly (when the remote end sends us a RST packet).
|
|
||||||
*
|
|
||||||
* In addition to the technical challenges and confusion, many high level socket/stream API's provide
|
|
||||||
* no support for dealing with the problem. If the read stream is closed, the API immediately declares the
|
|
||||||
* socket to be closed, and shuts down the write stream as well. In fact, this is what Apple's CFStream API does.
|
|
||||||
* It might sound like poor design at first, but in fact it simplifies development.
|
|
||||||
*
|
|
||||||
* The vast majority of the time if the read stream is closed it's because the remote endpoint closed its socket.
|
|
||||||
* Thus it actually makes sense to close the socket at this point.
|
|
||||||
* And in fact this is what most networking developers want and expect to happen.
|
|
||||||
* However, if you are writing a server that interacts with a plethora of clients,
|
|
||||||
* you might encounter a client that uses the discouraged technique of shutting down its write stream.
|
|
||||||
* If this is the case, you can set this property to NO,
|
|
||||||
* and make use of the socketDidCloseReadStream delegate method.
|
|
||||||
*
|
|
||||||
* The default value is YES.
|
|
||||||
**/
|
|
||||||
- (BOOL)autoDisconnectOnClosedReadStream;
|
|
||||||
- (void)setAutoDisconnectOnClosedReadStream:(BOOL)flag;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By default, both IPv4 and IPv6 are enabled.
|
* By default, both IPv4 and IPv6 are enabled.
|
||||||
*
|
*
|
||||||
|
@ -211,6 +187,15 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
**/
|
**/
|
||||||
- (BOOL)acceptOnInterface:(NSString *)interface port:(uint16_t)port error:(NSError **)errPtr;
|
- (BOOL)acceptOnInterface:(NSString *)interface port:(uint16_t)port error:(NSError **)errPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the socket to begin listening and accepting connections on the unix domain at the given url.
|
||||||
|
* When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it,
|
||||||
|
* and the socket:didAcceptNewSocket: delegate method will be invoked.
|
||||||
|
*
|
||||||
|
* The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
|
||||||
|
**/
|
||||||
|
- (BOOL)acceptOnUrl:(NSURL *)url error:(NSError **)errPtr;
|
||||||
|
|
||||||
#pragma mark Connecting
|
#pragma mark Connecting
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -326,6 +311,10 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
viaInterface:(NSString *)interface
|
viaInterface:(NSString *)interface
|
||||||
withTimeout:(NSTimeInterval)timeout
|
withTimeout:(NSTimeInterval)timeout
|
||||||
error:(NSError **)errPtr;
|
error:(NSError **)errPtr;
|
||||||
|
/**
|
||||||
|
* Connects to the unix domain socket at the given url, using the specified timeout.
|
||||||
|
*/
|
||||||
|
- (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
|
||||||
|
|
||||||
#pragma mark Disconnecting
|
#pragma mark Disconnecting
|
||||||
|
|
||||||
|
@ -389,6 +378,7 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
**/
|
**/
|
||||||
- (NSString *)connectedHost;
|
- (NSString *)connectedHost;
|
||||||
- (uint16_t)connectedPort;
|
- (uint16_t)connectedPort;
|
||||||
|
- (NSURL *)connectedUrl;
|
||||||
|
|
||||||
- (NSString *)localHost;
|
- (NSString *)localHost;
|
||||||
- (uint16_t)localPort;
|
- (uint16_t)localPort;
|
||||||
|
@ -646,6 +636,12 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
maxLength:(NSUInteger)length
|
maxLength:(NSUInteger)length
|
||||||
tag:(long)tag;
|
tag:(long)tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns progress of the current read, from 0.0 to 1.0, or NaN if no current read (use isnan() to check).
|
||||||
|
* The parameters "tag", "done" and "total" will be filled in if they aren't NULL.
|
||||||
|
**/
|
||||||
|
- (float)progressOfReadReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr;
|
||||||
|
|
||||||
#pragma mark Writing
|
#pragma mark Writing
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -667,6 +663,12 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
**/
|
**/
|
||||||
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
|
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns progress of the current write, from 0.0 to 1.0, or NaN if no current write (use isnan() to check).
|
||||||
|
* The parameters "tag", "done" and "total" will be filled in if they aren't NULL.
|
||||||
|
**/
|
||||||
|
- (float)progressOfWriteReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr;
|
||||||
|
|
||||||
#pragma mark Security
|
#pragma mark Security
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -678,7 +680,8 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
* Any reads or writes scheduled after this method is called will occur over the secured connection.
|
* Any reads or writes scheduled after this method is called will occur over the secured connection.
|
||||||
*
|
*
|
||||||
* The possible keys and values for the TLS settings are well documented.
|
* The possible keys and values for the TLS settings are well documented.
|
||||||
* Some possible keys are:
|
* Standard keys are:
|
||||||
|
*
|
||||||
* - kCFStreamSSLLevel
|
* - kCFStreamSSLLevel
|
||||||
* - kCFStreamSSLAllowsExpiredCertificates
|
* - kCFStreamSSLAllowsExpiredCertificates
|
||||||
* - kCFStreamSSLAllowsExpiredRoots
|
* - kCFStreamSSLAllowsExpiredRoots
|
||||||
|
@ -688,6 +691,18 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
* - kCFStreamSSLCertificates
|
* - kCFStreamSSLCertificates
|
||||||
* - kCFStreamSSLIsServer
|
* - kCFStreamSSLIsServer
|
||||||
*
|
*
|
||||||
|
* If SecureTransport is available on iOS:
|
||||||
|
*
|
||||||
|
* - GCDAsyncSocketSSLCipherSuites
|
||||||
|
* - GCDAsyncSocketSSLProtocolVersionMin
|
||||||
|
* - GCDAsyncSocketSSLProtocolVersionMax
|
||||||
|
*
|
||||||
|
* If SecureTransport is available on Mac OS X:
|
||||||
|
*
|
||||||
|
* - GCDAsyncSocketSSLCipherSuites
|
||||||
|
* - GCDAsyncSocketSSLDiffieHellmanParameters;
|
||||||
|
*
|
||||||
|
*
|
||||||
* Please refer to Apple's documentation for associated values, as well as other possible keys.
|
* Please refer to Apple's documentation for associated values, as well as other possible keys.
|
||||||
*
|
*
|
||||||
* If you pass in nil or an empty dictionary, the default settings will be used.
|
* If you pass in nil or an empty dictionary, the default settings will be used.
|
||||||
|
@ -712,6 +727,114 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
|
|
||||||
#pragma mark Advanced
|
#pragma mark Advanced
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traditionally sockets are not closed until the conversation is over.
|
||||||
|
* However, it is technically possible for the remote enpoint to close its write stream.
|
||||||
|
* Our socket would then be notified that there is no more data to be read,
|
||||||
|
* but our socket would still be writeable and the remote endpoint could continue to receive our data.
|
||||||
|
*
|
||||||
|
* The argument for this confusing functionality stems from the idea that a client could shut down its
|
||||||
|
* write stream after sending a request to the server, thus notifying the server there are to be no further requests.
|
||||||
|
* In practice, however, this technique did little to help server developers.
|
||||||
|
*
|
||||||
|
* To make matters worse, from a TCP perspective there is no way to tell the difference from a read stream close
|
||||||
|
* and a full socket close. They both result in the TCP stack receiving a FIN packet. The only way to tell
|
||||||
|
* is by continuing to write to the socket. If it was only a read stream close, then writes will continue to work.
|
||||||
|
* Otherwise an error will be occur shortly (when the remote end sends us a RST packet).
|
||||||
|
*
|
||||||
|
* In addition to the technical challenges and confusion, many high level socket/stream API's provide
|
||||||
|
* no support for dealing with the problem. If the read stream is closed, the API immediately declares the
|
||||||
|
* socket to be closed, and shuts down the write stream as well. In fact, this is what Apple's CFStream API does.
|
||||||
|
* It might sound like poor design at first, but in fact it simplifies development.
|
||||||
|
*
|
||||||
|
* The vast majority of the time if the read stream is closed it's because the remote endpoint closed its socket.
|
||||||
|
* Thus it actually makes sense to close the socket at this point.
|
||||||
|
* And in fact this is what most networking developers want and expect to happen.
|
||||||
|
* However, if you are writing a server that interacts with a plethora of clients,
|
||||||
|
* you might encounter a client that uses the discouraged technique of shutting down its write stream.
|
||||||
|
* If this is the case, you can set this property to NO,
|
||||||
|
* and make use of the socketDidCloseReadStream delegate method.
|
||||||
|
*
|
||||||
|
* The default value is YES.
|
||||||
|
**/
|
||||||
|
- (BOOL)autoDisconnectOnClosedReadStream;
|
||||||
|
- (void)setAutoDisconnectOnClosedReadStream:(BOOL)flag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue.
|
||||||
|
* In most cases, the instance creates this queue itself.
|
||||||
|
* However, to allow for maximum flexibility, the internal queue may be passed in the init method.
|
||||||
|
* This allows for some advanced options such as controlling socket priority via target queues.
|
||||||
|
* However, when one begins to use target queues like this, they open the door to some specific deadlock issues.
|
||||||
|
*
|
||||||
|
* For example, imagine there are 2 queues:
|
||||||
|
* dispatch_queue_t socketQueue;
|
||||||
|
* dispatch_queue_t socketTargetQueue;
|
||||||
|
*
|
||||||
|
* If you do this (pseudo-code):
|
||||||
|
* socketQueue.targetQueue = socketTargetQueue;
|
||||||
|
*
|
||||||
|
* Then all socketQueue operations will actually get run on the given socketTargetQueue.
|
||||||
|
* This is fine and works great in most situations.
|
||||||
|
* But if you run code directly from within the socketTargetQueue that accesses the socket,
|
||||||
|
* you could potentially get deadlock. Imagine the following code:
|
||||||
|
*
|
||||||
|
* - (BOOL)socketHasSomething
|
||||||
|
* {
|
||||||
|
* __block BOOL result = NO;
|
||||||
|
* dispatch_block_t block = ^{
|
||||||
|
* result = [self someInternalMethodToBeRunOnlyOnSocketQueue];
|
||||||
|
* }
|
||||||
|
* if (is_executing_on_queue(socketQueue))
|
||||||
|
* block();
|
||||||
|
* else
|
||||||
|
* dispatch_sync(socketQueue, block);
|
||||||
|
*
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* What happens if you call this method from the socketTargetQueue? The result is deadlock.
|
||||||
|
* This is because the GCD API offers no mechanism to discover a queue's targetQueue.
|
||||||
|
* Thus we have no idea if our socketQueue is configured with a targetQueue.
|
||||||
|
* If we had this information, we could easily avoid deadlock.
|
||||||
|
* But, since these API's are missing or unfeasible, you'll have to explicitly set it.
|
||||||
|
*
|
||||||
|
* IF you pass a socketQueue via the init method,
|
||||||
|
* AND you've configured the passed socketQueue with a targetQueue,
|
||||||
|
* THEN you should pass the end queue in the target hierarchy.
|
||||||
|
*
|
||||||
|
* For example, consider the following queue hierarchy:
|
||||||
|
* socketQueue -> ipQueue -> moduleQueue
|
||||||
|
*
|
||||||
|
* This example demonstrates priority shaping within some server.
|
||||||
|
* All incoming client connections from the same IP address are executed on the same target queue.
|
||||||
|
* And all connections for a particular module are executed on the same target queue.
|
||||||
|
* Thus, the priority of all networking for the entire module can be changed on the fly.
|
||||||
|
* Additionally, networking traffic from a single IP cannot monopolize the module.
|
||||||
|
*
|
||||||
|
* Here's how you would accomplish something like that:
|
||||||
|
* - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock
|
||||||
|
* {
|
||||||
|
* dispatch_queue_t socketQueue = dispatch_queue_create("", NULL);
|
||||||
|
* dispatch_queue_t ipQueue = [self ipQueueForAddress:address];
|
||||||
|
*
|
||||||
|
* dispatch_set_target_queue(socketQueue, ipQueue);
|
||||||
|
* dispatch_set_target_queue(iqQueue, moduleQueue);
|
||||||
|
*
|
||||||
|
* return socketQueue;
|
||||||
|
* }
|
||||||
|
* - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
|
||||||
|
* {
|
||||||
|
* [clientConnections addObject:newSocket];
|
||||||
|
* [newSocket markSocketQueueTargetQueue:moduleQueue];
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue.
|
||||||
|
* This is often NOT the case, as such queues are used solely for execution shaping.
|
||||||
|
**/
|
||||||
|
- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue;
|
||||||
|
- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It's not thread-safe to access certain variables from outside the socket's internal queue.
|
* It's not thread-safe to access certain variables from outside the socket's internal queue.
|
||||||
*
|
*
|
||||||
|
@ -805,7 +928,9 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
**/
|
**/
|
||||||
- (BOOL)enableBackgroundingOnSocket;
|
- (BOOL)enableBackgroundingOnSocket;
|
||||||
|
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
|
#if SECURE_TRANSPORT_MAYBE_AVAILABLE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is only available from within the context of a performBlock: invocation.
|
* This method is only available from within the context of a performBlock: invocation.
|
||||||
|
@ -881,6 +1006,12 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||||
**/
|
**/
|
||||||
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
|
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a socket connects and is ready for reading and writing.
|
||||||
|
* The host parameter will be an IP address, not a DNS name.
|
||||||
|
**/
|
||||||
|
- (void)socket:(GCDAsyncSocket *)sock didConnectToUrl:(NSURL *)url;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a socket has completed reading the requested data into memory.
|
* Called when a socket has completed reading the requested data into memory.
|
||||||
* Not called if there is an error.
|
* Not called if there is an error.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,6 +15,7 @@
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import "ContentManager.h"
|
#import "ContentManager.h"
|
||||||
#import "IconCache.h"
|
#import "IconCache.h"
|
||||||
|
#import "FinishedIconCache.h"
|
||||||
#import "IconOverlayHandlers.h"
|
#import "IconOverlayHandlers.h"
|
||||||
#import "Finder/Finder.h"
|
#import "Finder/Finder.h"
|
||||||
|
|
||||||
|
@ -69,7 +70,6 @@
|
||||||
|
|
||||||
NSURL* url = [node previewItemURL];
|
NSURL* url = [node previewItemURL];
|
||||||
|
|
||||||
NSError *error;
|
|
||||||
BOOL isDir;
|
BOOL isDir;
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir] == NO) {
|
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir] == NO) {
|
||||||
NSLog(@"ERROR: Could not determine file type of %@", [url path]);
|
NSLog(@"ERROR: Could not determine file type of %@", [url path]);
|
||||||
|
@ -77,21 +77,32 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
NSNumber* imageIndex = [[ContentManager sharedInstance] iconByPath:[url path] isDirectory:isDir];
|
NSNumber* imageIndex = [[ContentManager sharedInstance] iconByPath:[url path] isDirectory:isDir];
|
||||||
// NSLog(@"2 The icon index is %d", [imageIndex intValue]);
|
//NSLog(@"2 The icon index is %d %@ %@", [imageIndex intValue], [url path], isDir ? @"isDir" : @"");
|
||||||
|
|
||||||
if ([imageIndex intValue] > 0)
|
if ([imageIndex intValue] > 0)
|
||||||
{
|
{
|
||||||
NSImage* icon = [arg1 _nsImage];
|
NSImage* icon = [arg1 _nsImage];
|
||||||
|
|
||||||
[icon lockFocus];
|
// Use the short term icon cache that possibly has the finished icon
|
||||||
|
FinishedIconCache *finishedIconCache = [FinishedIconCache sharedInstance];
|
||||||
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
|
NSImage *finishedImage = [finishedIconCache getIcon:[url path] overlayIconIndex:imageIndex width:[icon size].width height:[icon size].height];
|
||||||
|
if (finishedImage) {
|
||||||
|
//NSLog(@"X Got finished image from cache %@ %@", finishedImage, [url path]);
|
||||||
|
return [[[IKImageWrapper alloc] initWithNSImage:finishedImage] autorelease];;
|
||||||
|
} else {
|
||||||
|
//NSLog(@"X Need to redraw %@", [url path]);
|
||||||
|
}
|
||||||
|
|
||||||
NSImage* iconimage = [[IconCache sharedInstance] getIcon:[NSNumber numberWithInt:[imageIndex intValue]]];
|
NSImage* iconimage = [[IconCache sharedInstance] getIcon:[NSNumber numberWithInt:[imageIndex intValue]]];
|
||||||
|
|
||||||
if (iconimage != nil)
|
if (iconimage != nil)
|
||||||
{
|
{
|
||||||
|
[icon lockFocus];
|
||||||
|
|
||||||
|
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
|
||||||
|
|
||||||
CGRect destRect = CGRectMake(0, 0, [icon size].width, [icon size].height);
|
CGRect destRect = CGRectMake(0, 0, [icon size].width, [icon size].height);
|
||||||
|
|
||||||
CGImageRef cgImage = [iconimage CGImageForProposedRect:&destRect
|
CGImageRef cgImage = [iconimage CGImageForProposedRect:&destRect
|
||||||
context:[NSGraphicsContext currentContext]
|
context:[NSGraphicsContext currentContext]
|
||||||
hints:nil];
|
hints:nil];
|
||||||
|
@ -103,9 +114,11 @@
|
||||||
NSLog(@"No image given!!!!!11 %@", [url path]);
|
NSLog(@"No image given!!!!!11 %@", [url path]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[icon unlockFocus];
|
||||||
}
|
}
|
||||||
|
|
||||||
[icon unlockFocus];
|
// Insert into cache
|
||||||
|
[finishedIconCache registerIcon:icon withFileName:[url path] overlayIconIndex:imageIndex width:[icon size].width height:[icon size].height];
|
||||||
|
|
||||||
return [[[IKImageWrapper alloc] initWithNSImage:icon] autorelease];
|
return [[[IKImageWrapper alloc] initWithNSImage:icon] autorelease];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,251 +0,0 @@
|
||||||
//
|
|
||||||
// JSONKit.h
|
|
||||||
// http://github.com/johnezang/JSONKit
|
|
||||||
// Dual licensed under either the terms of the BSD License, or alternatively
|
|
||||||
// under the terms of the Apache License, Version 2.0, as specified below.
|
|
||||||
//
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2011, John Engelhart
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
* Neither the name of the Zang Industries 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2011 John Engelhart
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <TargetConditionals.h>
|
|
||||||
#include <AvailabilityMacros.h>
|
|
||||||
|
|
||||||
#ifdef __OBJC__
|
|
||||||
#import <Foundation/NSArray.h>
|
|
||||||
#import <Foundation/NSData.h>
|
|
||||||
#import <Foundation/NSDictionary.h>
|
|
||||||
#import <Foundation/NSError.h>
|
|
||||||
#import <Foundation/NSObjCRuntime.h>
|
|
||||||
#import <Foundation/NSString.h>
|
|
||||||
#endif // __OBJC__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// For Mac OS X < 10.5.
|
|
||||||
#ifndef NSINTEGER_DEFINED
|
|
||||||
#define NSINTEGER_DEFINED
|
|
||||||
#if defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
|
|
||||||
typedef long NSInteger;
|
|
||||||
typedef unsigned long NSUInteger;
|
|
||||||
#define NSIntegerMin LONG_MIN
|
|
||||||
#define NSIntegerMax LONG_MAX
|
|
||||||
#define NSUIntegerMax ULONG_MAX
|
|
||||||
#else // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
|
|
||||||
typedef int NSInteger;
|
|
||||||
typedef unsigned int NSUInteger;
|
|
||||||
#define NSIntegerMin INT_MIN
|
|
||||||
#define NSIntegerMax INT_MAX
|
|
||||||
#define NSUIntegerMax UINT_MAX
|
|
||||||
#endif // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
|
|
||||||
#endif // NSINTEGER_DEFINED
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _JSONKIT_H_
|
|
||||||
#define _JSONKIT_H_
|
|
||||||
|
|
||||||
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__APPLE_CC__) && (__APPLE_CC__ >= 5465)
|
|
||||||
#define JK_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
|
|
||||||
#else
|
|
||||||
#define JK_DEPRECATED_ATTRIBUTE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define JSONKIT_VERSION_MAJOR 1
|
|
||||||
#define JSONKIT_VERSION_MINOR 4
|
|
||||||
|
|
||||||
typedef NSUInteger JKFlags;
|
|
||||||
|
|
||||||
/*
|
|
||||||
JKParseOptionComments : Allow C style // and /_* ... *_/ (without a _, obviously) comments in JSON.
|
|
||||||
JKParseOptionUnicodeNewlines : Allow Unicode recommended (?:\r\n|[\n\v\f\r\x85\p{Zl}\p{Zp}]) newlines.
|
|
||||||
JKParseOptionLooseUnicode : Normally the decoder will stop with an error at any malformed Unicode.
|
|
||||||
This option allows JSON with malformed Unicode to be parsed without reporting an error.
|
|
||||||
Any malformed Unicode is replaced with \uFFFD, or "REPLACEMENT CHARACTER".
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum {
|
|
||||||
JKParseOptionNone = 0,
|
|
||||||
JKParseOptionStrict = 0,
|
|
||||||
JKParseOptionComments = (1 << 0),
|
|
||||||
JKParseOptionUnicodeNewlines = (1 << 1),
|
|
||||||
JKParseOptionLooseUnicode = (1 << 2),
|
|
||||||
JKParseOptionPermitTextAfterValidJSON = (1 << 3),
|
|
||||||
JKParseOptionValidFlags = (JKParseOptionComments | JKParseOptionUnicodeNewlines | JKParseOptionLooseUnicode | JKParseOptionPermitTextAfterValidJSON),
|
|
||||||
};
|
|
||||||
typedef JKFlags JKParseOptionFlags;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
JKSerializeOptionNone = 0,
|
|
||||||
JKSerializeOptionPretty = (1 << 0),
|
|
||||||
JKSerializeOptionEscapeUnicode = (1 << 1),
|
|
||||||
JKSerializeOptionEscapeForwardSlashes = (1 << 4),
|
|
||||||
JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode | JKSerializeOptionEscapeForwardSlashes),
|
|
||||||
};
|
|
||||||
typedef JKFlags JKSerializeOptionFlags;
|
|
||||||
|
|
||||||
#ifdef __OBJC__
|
|
||||||
|
|
||||||
typedef struct JKParseState JKParseState; // Opaque internal, private type.
|
|
||||||
|
|
||||||
// As a general rule of thumb, if you use a method that doesn't accept a JKParseOptionFlags argument, it defaults to JKParseOptionStrict
|
|
||||||
|
|
||||||
@interface JSONDecoder : NSObject {
|
|
||||||
JKParseState *parseState;
|
|
||||||
}
|
|
||||||
+ (id)decoder;
|
|
||||||
+ (id)decoderWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
|
|
||||||
- (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
|
|
||||||
- (void)clearCache;
|
|
||||||
|
|
||||||
// The parse... methods were deprecated in v1.4 in favor of the v1.4 objectWith... methods.
|
|
||||||
- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length: instead.
|
|
||||||
- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length:error: instead.
|
|
||||||
// The NSData MUST be UTF8 encoded JSON.
|
|
||||||
- (id)parseJSONData:(NSData *)jsonData JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData: instead.
|
|
||||||
- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData:error: instead.
|
|
||||||
|
|
||||||
// Methods that return immutable collection objects.
|
|
||||||
- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
|
|
||||||
- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
|
|
||||||
// The NSData MUST be UTF8 encoded JSON.
|
|
||||||
- (id)objectWithData:(NSData *)jsonData;
|
|
||||||
- (id)objectWithData:(NSData *)jsonData error:(NSError **)error;
|
|
||||||
|
|
||||||
// Methods that return mutable collection objects.
|
|
||||||
- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
|
|
||||||
- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
|
|
||||||
// The NSData MUST be UTF8 encoded JSON.
|
|
||||||
- (id)mutableObjectWithData:(NSData *)jsonData;
|
|
||||||
- (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
////////////
|
|
||||||
#pragma mark Deserializing methods
|
|
||||||
////////////
|
|
||||||
|
|
||||||
@interface NSString (JSONKitDeserializing)
|
|
||||||
- (id)objectFromJSONString;
|
|
||||||
- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
|
|
||||||
- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
|
|
||||||
- (id)mutableObjectFromJSONString;
|
|
||||||
- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
|
|
||||||
- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSData (JSONKitDeserializing)
|
|
||||||
// The NSData MUST be UTF8 encoded JSON.
|
|
||||||
- (id)objectFromJSONData;
|
|
||||||
- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
|
|
||||||
- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
|
|
||||||
- (id)mutableObjectFromJSONData;
|
|
||||||
- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
|
|
||||||
- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
////////////
|
|
||||||
#pragma mark Serializing methods
|
|
||||||
////////////
|
|
||||||
|
|
||||||
@interface NSString (JSONKitSerializing)
|
|
||||||
// Convenience methods for those that need to serialize the receiving NSString (i.e., instead of having to serialize a NSArray with a single NSString, you can "serialize to JSON" just the NSString).
|
|
||||||
// Normally, a string that is serialized to JSON has quotation marks surrounding it, which you may or may not want when serializing a single string, and can be controlled with includeQuotes:
|
|
||||||
// includeQuotes:YES `a "test"...` -> `"a \"test\"..."`
|
|
||||||
// includeQuotes:NO `a "test"...` -> `a \"test\"...`
|
|
||||||
- (NSData *)JSONData; // Invokes JSONDataWithOptions:JKSerializeOptionNone includeQuotes:YES
|
|
||||||
- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
|
|
||||||
- (NSString *)JSONString; // Invokes JSONStringWithOptions:JKSerializeOptionNone includeQuotes:YES
|
|
||||||
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSArray (JSONKitSerializing)
|
|
||||||
- (NSData *)JSONData;
|
|
||||||
- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
|
|
||||||
- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
|
|
||||||
- (NSString *)JSONString;
|
|
||||||
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
|
|
||||||
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSDictionary (JSONKitSerializing)
|
|
||||||
- (NSData *)JSONData;
|
|
||||||
- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
|
|
||||||
- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
|
|
||||||
- (NSString *)JSONString;
|
|
||||||
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
|
|
||||||
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
#ifdef __BLOCKS__
|
|
||||||
|
|
||||||
@interface NSArray (JSONKitSerializingBlockAdditions)
|
|
||||||
- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
|
|
||||||
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSDictionary (JSONKitSerializingBlockAdditions)
|
|
||||||
- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
|
|
||||||
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif // __OBJC__
|
|
||||||
|
|
||||||
#endif // _JSONKIT_H_
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,9 +7,9 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
0B13ECAE173C687400548DA1 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B13ECAD173C686A00548DA1 /* GCDAsyncSocket.m */; };
|
0B13ECAE173C687400548DA1 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B13ECAD173C686A00548DA1 /* GCDAsyncSocket.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
|
||||||
0B13ECAF173C687900548DA1 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BFC9ACB173C57E400CDD329 /* Security.framework */; };
|
0B13ECAF173C687900548DA1 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BFC9ACB173C57E400CDD329 /* Security.framework */; };
|
||||||
0BFAF21C16F8E6C10017EA7E /* JSONKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFAF21B16F8E6C10017EA7E /* JSONKit.m */; };
|
5BB74A8719DBF9BB001BAAAC /* FinishedIconCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BB74A8619DBF9BB001BAAAC /* FinishedIconCache.m */; };
|
||||||
692C18A516660C4700BF6A53 /* ContextMenuHandlers.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18A416660C4600BF6A53 /* ContextMenuHandlers.m */; };
|
692C18A516660C4700BF6A53 /* ContextMenuHandlers.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18A416660C4600BF6A53 /* ContextMenuHandlers.m */; };
|
||||||
692C18A9166617F500BF6A53 /* IconOverlayHandlers.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18A8166617F500BF6A53 /* IconOverlayHandlers.m */; };
|
692C18A9166617F500BF6A53 /* IconOverlayHandlers.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18A8166617F500BF6A53 /* IconOverlayHandlers.m */; };
|
||||||
692C18AC1666392700BF6A53 /* MenuManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18AB1666392700BF6A53 /* MenuManager.m */; };
|
692C18AC1666392700BF6A53 /* MenuManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18AB1666392700BF6A53 /* MenuManager.m */; };
|
||||||
|
@ -29,9 +29,9 @@
|
||||||
0B13ECAC173C686900548DA1 /* GCDAsyncSocket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDAsyncSocket.h; sourceTree = "<group>"; };
|
0B13ECAC173C686900548DA1 /* GCDAsyncSocket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDAsyncSocket.h; sourceTree = "<group>"; };
|
||||||
0B13ECAD173C686A00548DA1 /* GCDAsyncSocket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncSocket.m; sourceTree = "<group>"; };
|
0B13ECAD173C686A00548DA1 /* GCDAsyncSocket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncSocket.m; sourceTree = "<group>"; };
|
||||||
0B2BF60B176A43DB001246CD /* Finder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Finder.h; sourceTree = "<group>"; };
|
0B2BF60B176A43DB001246CD /* Finder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Finder.h; sourceTree = "<group>"; };
|
||||||
0BFAF21A16F8E6C10017EA7E /* JSONKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONKit.h; sourceTree = "<group>"; };
|
|
||||||
0BFAF21B16F8E6C10017EA7E /* JSONKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONKit.m; sourceTree = "<group>"; };
|
|
||||||
0BFC9ACB173C57E400CDD329 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
0BFC9ACB173C57E400CDD329 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||||
|
5BB74A8519DBF9BB001BAAAC /* FinishedIconCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FinishedIconCache.h; sourceTree = "<group>"; };
|
||||||
|
5BB74A8619DBF9BB001BAAAC /* FinishedIconCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FinishedIconCache.m; sourceTree = "<group>"; };
|
||||||
692C18A316660C4600BF6A53 /* ContextMenuHandlers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContextMenuHandlers.h; sourceTree = "<group>"; };
|
692C18A316660C4600BF6A53 /* ContextMenuHandlers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContextMenuHandlers.h; sourceTree = "<group>"; };
|
||||||
692C18A416660C4600BF6A53 /* ContextMenuHandlers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContextMenuHandlers.m; sourceTree = "<group>"; };
|
692C18A416660C4600BF6A53 /* ContextMenuHandlers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContextMenuHandlers.m; sourceTree = "<group>"; };
|
||||||
692C18A7166617F500BF6A53 /* IconOverlayHandlers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconOverlayHandlers.h; sourceTree = "<group>"; };
|
692C18A7166617F500BF6A53 /* IconOverlayHandlers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconOverlayHandlers.h; sourceTree = "<group>"; };
|
||||||
|
@ -104,7 +104,6 @@
|
||||||
children = (
|
children = (
|
||||||
0B2BF60A176A43DB001246CD /* Finder */,
|
0B2BF60A176A43DB001246CD /* Finder */,
|
||||||
0B08BAC21759627700C8351E /* GCDAsyncSocket */,
|
0B08BAC21759627700C8351E /* GCDAsyncSocket */,
|
||||||
0BFAF21916F8E6910017EA7E /* JSONKit */,
|
|
||||||
8C37DD99161593BD00016A95 /* FinderHook.h */,
|
8C37DD99161593BD00016A95 /* FinderHook.h */,
|
||||||
8C37DD9A161593BD00016A95 /* FinderHook.m */,
|
8C37DD9A161593BD00016A95 /* FinderHook.m */,
|
||||||
692C18A316660C4600BF6A53 /* ContextMenuHandlers.h */,
|
692C18A316660C4600BF6A53 /* ContextMenuHandlers.h */,
|
||||||
|
@ -117,6 +116,8 @@
|
||||||
69948B351636D50E0093B6CE /* ContentManager.m */,
|
69948B351636D50E0093B6CE /* ContentManager.m */,
|
||||||
8C99F6921622D145002D2135 /* IconCache.h */,
|
8C99F6921622D145002D2135 /* IconCache.h */,
|
||||||
8C99F6931622D145002D2135 /* IconCache.m */,
|
8C99F6931622D145002D2135 /* IconCache.m */,
|
||||||
|
5BB74A8519DBF9BB001BAAAC /* FinishedIconCache.h */,
|
||||||
|
5BB74A8619DBF9BB001BAAAC /* FinishedIconCache.m */,
|
||||||
692C18AA1666392700BF6A53 /* MenuManager.h */,
|
692C18AA1666392700BF6A53 /* MenuManager.h */,
|
||||||
692C18AB1666392700BF6A53 /* MenuManager.m */,
|
692C18AB1666392700BF6A53 /* MenuManager.m */,
|
||||||
);
|
);
|
||||||
|
@ -140,15 +141,6 @@
|
||||||
path = Finder;
|
path = Finder;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
0BFAF21916F8E6910017EA7E /* JSONKit */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
0BFAF21A16F8E6C10017EA7E /* JSONKit.h */,
|
|
||||||
0BFAF21B16F8E6C10017EA7E /* JSONKit.m */,
|
|
||||||
);
|
|
||||||
name = JSONKit;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
19C28FB6FE9D52B211CA2CBB /* Products */ = {
|
19C28FB6FE9D52B211CA2CBB /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -226,10 +218,10 @@
|
||||||
8C99F6941622D145002D2135 /* IconCache.m in Sources */,
|
8C99F6941622D145002D2135 /* IconCache.m in Sources */,
|
||||||
69948B361636D50E0093B6CE /* ContentManager.m in Sources */,
|
69948B361636D50E0093B6CE /* ContentManager.m in Sources */,
|
||||||
6993878616494C000044E4DF /* RequestManager.m in Sources */,
|
6993878616494C000044E4DF /* RequestManager.m in Sources */,
|
||||||
|
5BB74A8719DBF9BB001BAAAC /* FinishedIconCache.m in Sources */,
|
||||||
692C18A516660C4700BF6A53 /* ContextMenuHandlers.m in Sources */,
|
692C18A516660C4700BF6A53 /* ContextMenuHandlers.m in Sources */,
|
||||||
692C18A9166617F500BF6A53 /* IconOverlayHandlers.m in Sources */,
|
692C18A9166617F500BF6A53 /* IconOverlayHandlers.m in Sources */,
|
||||||
692C18AC1666392700BF6A53 /* MenuManager.m in Sources */,
|
692C18AC1666392700BF6A53 /* MenuManager.m in Sources */,
|
||||||
0BFAF21C16F8E6C10017EA7E /* JSONKit.m in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -302,8 +294,9 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
||||||
COPY_PHASE_STRIP = YES;
|
COPY_PHASE_STRIP = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
+ (RequestManager*)sharedInstance;
|
+ (RequestManager*)sharedInstance;
|
||||||
|
|
||||||
- (BOOL)isRegisteredPath:(NSString*)path;
|
- (BOOL)isRegisteredPath:(NSString*)path isDirectory:(BOOL)isDir;
|
||||||
- (void)askOnSocket:(NSString*)path query:(NSString*)verb;
|
- (void)askOnSocket:(NSString*)path query:(NSString*)verb;
|
||||||
- (NSNumber*)askForIcon:(NSString*)path isDirectory:(BOOL)isDir;
|
- (NSNumber*)askForIcon:(NSString*)path isDirectory:(BOOL)isDir;
|
||||||
- (void)menuItemClicked:(NSDictionary*)actionDictionary;
|
- (void)menuItemClicked:(NSDictionary*)actionDictionary;
|
||||||
|
|
|
@ -77,14 +77,20 @@ static RequestManager* sharedInstance = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)isRegisteredPath:(NSString*)path
|
- (BOOL)isRegisteredPath:(NSString*)path isDirectory:(BOOL)isDir
|
||||||
{
|
{
|
||||||
// check if the file in question is underneath a registered directory
|
// check if the file in question is underneath a registered directory
|
||||||
NSArray *regPathes = [_registeredPathes allKeys];
|
NSArray *regPathes = [_registeredPathes allKeys];
|
||||||
BOOL registered = NO;
|
BOOL registered = NO;
|
||||||
|
|
||||||
|
NSString* checkPath = [NSString stringWithString:path];
|
||||||
|
if (isDir) {
|
||||||
|
// append a slash
|
||||||
|
checkPath = [path stringByAppendingString:@"/"];
|
||||||
|
}
|
||||||
|
|
||||||
for( NSString *regPath in regPathes ) {
|
for( NSString *regPath in regPathes ) {
|
||||||
if( [path hasPrefix:regPath]) {
|
if( [checkPath hasPrefix:regPath]) {
|
||||||
// the path was registered
|
// the path was registered
|
||||||
registered = YES;
|
registered = YES;
|
||||||
break;
|
break;
|
||||||
|
@ -99,7 +105,7 @@ static RequestManager* sharedInstance = nil;
|
||||||
NSString *verb = @"RETRIEVE_FILE_STATUS";
|
NSString *verb = @"RETRIEVE_FILE_STATUS";
|
||||||
NSNumber *res = [NSNumber numberWithInt:0];
|
NSNumber *res = [NSNumber numberWithInt:0];
|
||||||
|
|
||||||
if( [self isRegisteredPath:path] ) {
|
if( [self isRegisteredPath:path isDirectory:isDir] ) {
|
||||||
if( _isConnected ) {
|
if( _isConnected ) {
|
||||||
if(isDir) {
|
if(isDir) {
|
||||||
verb = @"RETRIEVE_FOLDER_STATUS";
|
verb = @"RETRIEVE_FOLDER_STATUS";
|
||||||
|
@ -121,30 +127,37 @@ static RequestManager* sharedInstance = nil;
|
||||||
|
|
||||||
- (void)socket:(GCDAsyncSocket*)socket didReadData:(NSData*)data withTag:(long)tag
|
- (void)socket:(GCDAsyncSocket*)socket didReadData:(NSData*)data withTag:(long)tag
|
||||||
{
|
{
|
||||||
NSArray *chunks;
|
|
||||||
NSString *answer = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
NSString *answer = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||||
|
NSArray *chunks = nil;
|
||||||
if (answer != nil && [answer length] > 0) {
|
if (answer != nil && [answer length] > 0) {
|
||||||
// cut a trailing newline
|
// cut a trailing newline
|
||||||
answer = [answer substringToIndex:[answer length] - 1];
|
answer = [answer substringToIndex:[answer length] - 1];
|
||||||
chunks = [answer componentsSeparatedByString: @":"];
|
chunks = [answer componentsSeparatedByString: @":"];
|
||||||
}
|
}
|
||||||
NSLog(@"READ from socket (%ld): <%@>", tag, answer);
|
|
||||||
ContentManager *contentman = [ContentManager sharedInstance];
|
ContentManager *contentman = [ContentManager sharedInstance];
|
||||||
|
|
||||||
if( [chunks count] > 0 && tag == READ_TAG ) {
|
if( chunks && [chunks count] > 0 && tag == READ_TAG ) {
|
||||||
|
NSLog(@"READ from socket (%ld): <%@>", tag, answer);
|
||||||
if( [[chunks objectAtIndex:0] isEqualToString:@"STATUS"] ) {
|
if( [[chunks objectAtIndex:0] isEqualToString:@"STATUS"] ) {
|
||||||
[contentman setResultForPath:[chunks objectAtIndex:2] result:[chunks objectAtIndex:1]];
|
NSString *path = [chunks objectAtIndex:2];
|
||||||
|
if( [chunks count] > 3 ) {
|
||||||
|
for( int i = 2; i < [chunks count]-1; i++ ) {
|
||||||
|
path = [NSString stringWithFormat:@"%@:%@",
|
||||||
|
path, [chunks objectAtIndex:i+1] ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[contentman setResultForPath:path result:[chunks objectAtIndex:1]];
|
||||||
} else if( [[chunks objectAtIndex:0] isEqualToString:@"UPDATE_VIEW"] ) {
|
} else if( [[chunks objectAtIndex:0] isEqualToString:@"UPDATE_VIEW"] ) {
|
||||||
NSString *path = [chunks objectAtIndex:1];
|
NSString *path = [chunks objectAtIndex:1];
|
||||||
[contentman reFetchFileNameCacheForPath:path];
|
[contentman reFetchFileNameCacheForPath:path];
|
||||||
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_PATH"] ) {
|
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_PATH"] ) {
|
||||||
NSNumber *one = [NSNumber numberWithInt:1];
|
NSNumber *one = [NSNumber numberWithInt:1];
|
||||||
NSString *path = [chunks objectAtIndex:1];
|
NSString *path = [chunks objectAtIndex:1];
|
||||||
|
NSLog(@"Registering path: %@", path);
|
||||||
[_registeredPathes setObject:one forKey:path];
|
[_registeredPathes setObject:one forKey:path];
|
||||||
|
|
||||||
[contentman repaintAllWindows];
|
[contentman repaintAllWindows];
|
||||||
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"UNREGISTER_PATH"] ) {
|
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"UNREGISTER_PATH"] ) {
|
||||||
NSNumber *one = [NSNumber numberWithInt:1];
|
|
||||||
NSString *path = [chunks objectAtIndex:1];
|
NSString *path = [chunks objectAtIndex:1];
|
||||||
[_registeredPathes removeObjectForKey:path];
|
[_registeredPathes removeObjectForKey:path];
|
||||||
|
|
||||||
|
@ -155,8 +168,8 @@ static RequestManager* sharedInstance = nil;
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"Unknown command %@", [chunks objectAtIndex:0]);
|
NSLog(@"Unknown command %@", [chunks objectAtIndex:0]);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (tag != READ_TAG) {
|
||||||
NSLog(@"Received unknown tag %ld", tag);
|
NSLog(@"Received unknown tag %ld <%@>", tag, answer);
|
||||||
}
|
}
|
||||||
// Read on and on
|
// Read on and on
|
||||||
NSData* stop = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
|
NSData* stop = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
@ -170,16 +183,26 @@ static RequestManager* sharedInstance = nil;
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-(void)socket:(GCDAsyncSocket*)socket didConnectToUrl:(NSURL *)url {
|
||||||
|
NSLog(@"didConnectToUrl %@", url);
|
||||||
|
[self socketDidConnect:socket];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)socket:(GCDAsyncSocket*)socket didConnectToHost:(NSString*)host port:(UInt16)port
|
- (void)socket:(GCDAsyncSocket*)socket didConnectToHost:(NSString*)host port:(UInt16)port
|
||||||
{
|
{
|
||||||
NSLog( @"Connected to host successfully!");
|
[self socketDidConnect:socket];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our impl
|
||||||
|
- (void)socketDidConnect:(GCDAsyncSocket*)socket {
|
||||||
|
NSLog( @"Connected to sync client successfully!");
|
||||||
_isConnected = YES;
|
_isConnected = YES;
|
||||||
_isRunning = NO;
|
_isRunning = NO;
|
||||||
|
|
||||||
if( [_requestQueue count] > 0 ) {
|
if( [_requestQueue count] > 0 ) {
|
||||||
NSLog( @"We have to empty the queue");
|
NSLog( @"We have to empty the queue");
|
||||||
for( NSString *path in _requestQueue ) {
|
for( NSString *path in _requestQueue ) {
|
||||||
[self askOnSocket:path];
|
[self askOnSocket:path query:@"RETRIEVE_FILE_STATUS"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,12 +244,27 @@ static RequestManager* sharedInstance = nil;
|
||||||
{
|
{
|
||||||
if (!_isRunning)
|
if (!_isRunning)
|
||||||
{
|
{
|
||||||
NSLog(@"Connect Socket!");
|
|
||||||
NSError *err = nil;
|
NSError *err = nil;
|
||||||
if (![_socket connectToHost:@"localhost" onPort:34001 withTimeout:5 error:&err]) // Asynchronous!
|
BOOL useTcp = NO;
|
||||||
{
|
if (useTcp) {
|
||||||
// If there was an error, it's likely something like "already connected" or "no delegate set"
|
NSLog(@"Connect Socket");
|
||||||
NSLog(@"I goofed: %@", err);
|
if (![_socket connectToHost:@"localhost" onPort:34001 withTimeout:5 error:&err]) {
|
||||||
|
// If there was an error, it's likely something like "already connected" or "no delegate set"
|
||||||
|
NSLog(@"I goofed: %@", err);
|
||||||
|
}
|
||||||
|
} else if (!useTcp) {
|
||||||
|
NSURL *url = nil;
|
||||||
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||||
|
if ([paths count])
|
||||||
|
{
|
||||||
|
// file:///Users/guruz/Library/Caches/SyncStateHelper/ownCloud.socket
|
||||||
|
// FIXME Generify this and support all sockets there since multiple sync clients might be running
|
||||||
|
url =[NSURL fileURLWithPath:[[[paths objectAtIndex:0] stringByAppendingPathComponent:@"SyncStateHelper"] stringByAppendingPathComponent:@"ownCloud.socket"]];
|
||||||
|
}
|
||||||
|
if (url) {
|
||||||
|
NSLog(@"Connect Socket to %@", url);
|
||||||
|
[_socket connectToUrl:url withTimeout:5 error:&err];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isRunning = YES;
|
_isRunning = YES;
|
||||||
|
|
|
@ -14,7 +14,9 @@ if( UNIX AND NOT APPLE )
|
||||||
FOREACH(size 128x128 16x16 256x256 32x32 48x48 64x64 72x72)
|
FOREACH(size 128x128 16x16 256x256 32x32 48x48 64x64 72x72)
|
||||||
file(GLOB files "${size}/*.png")
|
file(GLOB files "${size}/*.png")
|
||||||
FOREACH( file ${files} )
|
FOREACH( file ${files} )
|
||||||
STRING(REPLACE "oC" ${APPLICATION_NAME} brandedName ${file})
|
# the GLOB returns a absolute path. Make it relative by replacing the current src dir by nothing
|
||||||
|
STRING(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/${size}/" "" shortFile ${file})
|
||||||
|
STRING(REPLACE "oC" ${APPLICATION_NAME} brandedName ${shortFile})
|
||||||
install(FILES ${file} DESTINATION ${ICON_DIR}/${size}/apps RENAME ${brandedName})
|
install(FILES ${file} DESTINATION ${ICON_DIR}/${size}/apps RENAME ${brandedName})
|
||||||
ENDFOREACH(file)
|
ENDFOREACH(file)
|
||||||
ENDFOREACH(size)
|
ENDFOREACH(size)
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
configure_file(fixbranding.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fixbranding.sh)
|
|
||||||
|
|
||||||
install(FILES ${APPLICATION_EXECUTABLE}.py DESTINATION ${DATADIR}/nautilus-python/extensions)
|
install(FILES syncstate.py DESTINATION ${DATADIR}/nautilus-python/extensions)
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
sed 's/oC_/@APPLICATION_EXECUTABLE@_/' ownCloud.py
|
|
||||||
mv ownCloud.py @APPLICATION_EXECUTABLE@.py
|
|
|
@ -1,4 +1,16 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import urllib
|
import urllib
|
||||||
|
@ -6,34 +18,45 @@ import socket
|
||||||
|
|
||||||
from gi.repository import GObject, Nautilus
|
from gi.repository import GObject, Nautilus
|
||||||
|
|
||||||
class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
|
class syncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
|
||||||
|
|
||||||
nautilusVFSFile_table = {}
|
nautilusVFSFile_table = {}
|
||||||
registered_paths = {}
|
registered_paths = {}
|
||||||
remainder = ''
|
remainder = ''
|
||||||
connected = False
|
connected = False
|
||||||
watch_id = 0
|
watch_id = 0
|
||||||
|
appname = 'ownCloud'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.connectToOwnCloud
|
self.connectToSocketServer
|
||||||
if not self.connected:
|
if not self.connected:
|
||||||
# try again in 5 seconds - attention, logic inverted!
|
# try again in 5 seconds - attention, logic inverted!
|
||||||
GObject.timeout_add(5000, self.connectToOwnCloud)
|
GObject.timeout_add(5000, self.connectToSocketServer)
|
||||||
|
|
||||||
def port(self):
|
def connectToSocketServer(self):
|
||||||
return 34001 # Fixme, read from config file.
|
do_reconnect = True
|
||||||
|
|
||||||
def connectToOwnCloud(self):
|
|
||||||
try:
|
try:
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
self.sock.connect(("localhost", self.port()))
|
postfix = "/"+self.appname+"/socket"
|
||||||
self.sock.settimeout(5)
|
sock_file = os.environ["XDG_RUNTIME_DIR"]+postfix
|
||||||
self.connected = True
|
print ("XXXX " + sock_file + " <=> " + postfix)
|
||||||
self.watch_id = GObject.io_add_watch(self.sock, GObject.IO_IN, self.handle_notify)
|
if sock_file != postfix:
|
||||||
except:
|
try:
|
||||||
print("Connect could not be established, try again later!")
|
print("Socket File: "+sock_file)
|
||||||
|
self.sock.connect(sock_file)
|
||||||
|
self.connected = True
|
||||||
|
print("Setting connected to %r" % self.connected )
|
||||||
|
self.watch_id = GObject.io_add_watch(self.sock, GObject.IO_IN, self.handle_notify)
|
||||||
|
do_reconnect = False
|
||||||
|
except Exception, e:
|
||||||
|
print("Could not connect to unix socket." + str(e))
|
||||||
|
else:
|
||||||
|
print("Sock-File not valid: "+sock_file)
|
||||||
|
except Exception, e:
|
||||||
|
print("Connect could not be established, try again later " + str(e))
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
return not self.connected
|
# print("Returning %r" % do_reconnect)
|
||||||
|
return do_reconnect
|
||||||
|
|
||||||
def sendCommand(self, cmd):
|
def sendCommand(self, cmd):
|
||||||
if self.connected:
|
if self.connected:
|
||||||
|
@ -43,7 +66,7 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
|
||||||
print("Sending failed.")
|
print("Sending failed.")
|
||||||
GObject.source_remove(self.watch_id)
|
GObject.source_remove(self.watch_id)
|
||||||
self.connected = False
|
self.connected = False
|
||||||
GObject.timeout_add(5000, self.connectToOwnCloud)
|
GObject.timeout_add(5000, self.connectToSocketServer)
|
||||||
|
|
||||||
def find_item_for_file(self, path):
|
def find_item_for_file(self, path):
|
||||||
if path in self.nautilusVFSFile_table:
|
if path in self.nautilusVFSFile_table:
|
||||||
|
@ -52,6 +75,7 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def askForOverlay(self, file):
|
def askForOverlay(self, file):
|
||||||
|
# print("Asking for overlay for "+file)
|
||||||
if os.path.isdir(file):
|
if os.path.isdir(file):
|
||||||
folderStatus = self.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
|
folderStatus = self.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
|
||||||
|
|
||||||
|
@ -60,28 +84,30 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
|
||||||
|
|
||||||
def invalidate_items_underneath(self, path):
|
def invalidate_items_underneath(self, path):
|
||||||
update_items = []
|
update_items = []
|
||||||
for p in self.nautilusVFSFile_table:
|
if not self.nautilusVFSFile_table:
|
||||||
if p == path or p.startswith(path):
|
self.askForOverlay(path)
|
||||||
item = self.nautilusVFSFile_table[p]
|
else:
|
||||||
update_items.append(item)
|
for p in self.nautilusVFSFile_table:
|
||||||
|
if p == path or p.startswith(path):
|
||||||
|
item = self.nautilusVFSFile_table[p]['item']
|
||||||
|
update_items.append(item)
|
||||||
|
|
||||||
for item in update_items:
|
for item in update_items:
|
||||||
item.invalidate_extension_info()
|
item.invalidate_extension_info()
|
||||||
# self.update_file_info(item)
|
|
||||||
|
|
||||||
# Handles a single line of server respoonse and sets the emblem
|
# Handles a single line of server respoonse and sets the emblem
|
||||||
def handle_server_response(self, l):
|
def handle_server_response(self, l):
|
||||||
Emblems = { 'OK' : 'oC_ok',
|
Emblems = { 'OK' : self.appname +'_ok',
|
||||||
'SYNC' : 'oC_sync',
|
'SYNC' : self.appname +'_sync',
|
||||||
'NEW' : 'oC_sync',
|
'NEW' : self.appname +'_sync',
|
||||||
'IGNORE' : 'oC_warn',
|
'IGNORE' : self.appname +'_warn',
|
||||||
'ERROR' : 'oC_error',
|
'ERROR' : self.appname +'_error',
|
||||||
'OK+SWM' : 'oC_ok_shared',
|
'OK+SWM' : self.appname +'_ok_shared',
|
||||||
'SYNC+SWM' : 'oC_sync_shared',
|
'SYNC+SWM' : self.appname +'_sync_shared',
|
||||||
'NEW+SWM' : 'oC_sync_shared',
|
'NEW+SWM' : self.appname +'_sync_shared',
|
||||||
'IGNORE+SWM': 'oC_warn_shared',
|
'IGNORE+SWM': self.appname +'_warn_shared',
|
||||||
'ERROR+SWM' : 'oC_error_shared',
|
'ERROR+SWM' : self.appname +'_error_shared',
|
||||||
'NOP' : 'oC_error'
|
'NOP' : self.appname +'_error'
|
||||||
}
|
}
|
||||||
|
|
||||||
print("Server response: "+l)
|
print("Server response: "+l)
|
||||||
|
@ -92,11 +118,16 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
|
||||||
# file = parts[1]
|
# file = parts[1]
|
||||||
# print "Action for " + file + ": "+parts[0]
|
# print "Action for " + file + ": "+parts[0]
|
||||||
if action == 'STATUS':
|
if action == 'STATUS':
|
||||||
emblem = Emblems[parts[1]]
|
newState = parts[1]
|
||||||
|
emblem = Emblems[newState]
|
||||||
if emblem:
|
if emblem:
|
||||||
item = self.find_item_for_file(parts[2])
|
itemStore = self.find_item_for_file(parts[2])
|
||||||
if item:
|
if itemStore:
|
||||||
item.add_emblem(emblem)
|
if( not itemStore['state'] or newState != itemStore['state'] ):
|
||||||
|
item = itemStore['item']
|
||||||
|
item.add_emblem(emblem)
|
||||||
|
# print "Setting emblem on " + parts[2]+ "<>"+emblem+"<>"
|
||||||
|
self.nautilusVFSFile_table[parts[2]] = {'item': item, 'state':newState}
|
||||||
|
|
||||||
elif action == 'UPDATE_VIEW':
|
elif action == 'UPDATE_VIEW':
|
||||||
# Search all items underneath this path and invalidate them
|
# Search all items underneath this path and invalidate them
|
||||||
|
@ -116,7 +147,7 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
self.connected = False
|
self.connected = False
|
||||||
GObject.source_remove(self.watch_id)
|
GObject.source_remove(self.watch_id)
|
||||||
GObject.timeout_add(5000, self.connectToOwnCloud)
|
GObject.timeout_add(5000, self.connectToSocketServer)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# print "We got unknown action " + action
|
# print "We got unknown action " + action
|
||||||
|
@ -158,7 +189,7 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
|
||||||
|
|
||||||
for reg_path in self.registered_paths:
|
for reg_path in self.registered_paths:
|
||||||
if filename.startswith(reg_path):
|
if filename.startswith(reg_path):
|
||||||
self.nautilusVFSFile_table[filename] = item
|
self.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
|
||||||
|
|
||||||
# item.add_string_attribute('share_state', "share state")
|
# item.add_string_attribute('share_state', "share state")
|
||||||
self.askForOverlay(filename)
|
self.askForOverlay(filename)
|
|
@ -36,17 +36,13 @@ extern HINSTANCE instanceHandle;
|
||||||
#define IDM_DISPLAY 0
|
#define IDM_DISPLAY 0
|
||||||
#define IDB_OK 101
|
#define IDB_OK 101
|
||||||
|
|
||||||
namespace {
|
|
||||||
static std::vector<std::wstring> s_watchedDirectories;
|
|
||||||
}
|
|
||||||
|
|
||||||
OCOverlay::OCOverlay(int state)
|
OCOverlay::OCOverlay(int state)
|
||||||
: _communicationSocket(0)
|
: _referenceCount(1)
|
||||||
, _referenceCount(1)
|
|
||||||
, _checker(new RemotePathChecker(PORT))
|
|
||||||
, _state(state)
|
, _state(state)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
static RemotePathChecker s_remotePathChecker;
|
||||||
|
_checker = &s_remotePathChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
OCOverlay::~OCOverlay(void)
|
OCOverlay::~OCOverlay(void)
|
||||||
|
@ -121,23 +117,13 @@ IFACEMETHODIMP OCOverlay::GetPriority(int *pPriority)
|
||||||
|
|
||||||
IFACEMETHODIMP OCOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib)
|
IFACEMETHODIMP OCOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib)
|
||||||
{
|
{
|
||||||
|
auto watchedDirectories = _checker->WatchedDirectories();
|
||||||
//if(!_IsOverlaysEnabled())
|
|
||||||
//{
|
|
||||||
// return MAKE_HRESULT(S_FALSE, 0, 0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
// FIXME: Use Registry instead, this will only trigger once
|
|
||||||
// and now follow any user changes in the client
|
|
||||||
if (s_watchedDirectories.empty()) {
|
|
||||||
s_watchedDirectories = _checker->WatchedDirectories();
|
|
||||||
}
|
|
||||||
|
|
||||||
wstring wpath(pwszPath);
|
wstring wpath(pwszPath);
|
||||||
wpath.append(L"\\");
|
//wpath.append(L"\\");
|
||||||
vector<wstring>::iterator it;
|
vector<wstring>::iterator it;
|
||||||
bool watched = false;
|
bool watched = false;
|
||||||
for (it = s_watchedDirectories.begin(); it != s_watchedDirectories.end(); ++it) {
|
for (it = watchedDirectories.begin(); it != watchedDirectories.end(); ++it) {
|
||||||
if (StringUtil::begins_with(wpath, *it)) {
|
if (StringUtil::begins_with(wpath, *it)) {
|
||||||
watched = true;
|
watched = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,14 +35,13 @@ public:
|
||||||
IFACEMETHODIMP_(ULONG) Release();
|
IFACEMETHODIMP_(ULONG) Release();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~OCOverlay(void);
|
~OCOverlay();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//bool _GenerateMessage(const wchar_t*, std::wstring*);
|
//bool _GenerateMessage(const wchar_t*, std::wstring*);
|
||||||
|
|
||||||
bool _IsOverlaysEnabled();
|
bool _IsOverlaysEnabled();
|
||||||
long _referenceCount;
|
long _referenceCount;
|
||||||
CommunicationSocket* _communicationSocket;
|
|
||||||
RemotePathChecker* _checker;
|
RemotePathChecker* _checker;
|
||||||
int _state;
|
int _state;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
@ -30,8 +31,8 @@ using namespace std;
|
||||||
|
|
||||||
#define DEFAULT_BUFLEN 4096
|
#define DEFAULT_BUFLEN 4096
|
||||||
|
|
||||||
CommunicationSocket::CommunicationSocket(int port)
|
CommunicationSocket::CommunicationSocket()
|
||||||
: _port(port), _clientSocket(INVALID_SOCKET)
|
: _pipe(INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,64 +44,39 @@ CommunicationSocket::~CommunicationSocket()
|
||||||
bool CommunicationSocket::Close()
|
bool CommunicationSocket::Close()
|
||||||
{
|
{
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
bool closed = (closesocket(_clientSocket) == 0);
|
if (_pipe == INVALID_HANDLE_VALUE) {
|
||||||
shutdown(_clientSocket, SD_BOTH);
|
return false;
|
||||||
_clientSocket = INVALID_SOCKET;
|
}
|
||||||
return closed;
|
CloseHandle(_pipe);
|
||||||
|
_pipe = INVALID_HANDLE_VALUE;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CommunicationSocket::Connect()
|
bool CommunicationSocket::Connect(const std::wstring &pipename)
|
||||||
{
|
{
|
||||||
WSADATA wsaData;
|
_pipe = CreateFile(pipename.data(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
|
||||||
HRESULT iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
if (_pipe == INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
if (iResult != NO_ERROR) {
|
}
|
||||||
int error = WSAGetLastError();
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
|
|
||||||
if (_clientSocket == INVALID_SOCKET) {
|
|
||||||
//int error = WSAGetLastError();
|
|
||||||
Close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in clientService;
|
|
||||||
|
|
||||||
clientService.sin_family = AF_INET;
|
|
||||||
clientService.sin_addr.s_addr = inet_addr(PLUG_IN_SOCKET_ADDRESS);
|
|
||||||
clientService.sin_port = htons(_port);
|
|
||||||
|
|
||||||
iResult = connect(_clientSocket, (SOCKADDR*)&clientService, sizeof(clientService));
|
|
||||||
DWORD timeout = 500; // ms
|
|
||||||
setsockopt(_clientSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(DWORD));
|
|
||||||
|
|
||||||
if (iResult == SOCKET_ERROR) {
|
|
||||||
//int error = WSAGetLastError();
|
|
||||||
Close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommunicationSocket::SendMsg(const wchar_t* message)
|
bool CommunicationSocket::SendMsg(const wchar_t* message)
|
||||||
{
|
{
|
||||||
const char* utf8_msg = StringUtil::toUtf8(message);
|
auto utf8_msg = StringUtil::toUtf8(message);
|
||||||
size_t result = send(_clientSocket, utf8_msg, (int)strlen(utf8_msg), 0);
|
|
||||||
delete[] utf8_msg;
|
|
||||||
|
|
||||||
if (result == SOCKET_ERROR) {
|
DWORD numBytesWritten = 0;
|
||||||
//int error = WSAGetLastError();
|
auto result = WriteFile( _pipe, utf8_msg.c_str(), DWORD(utf8_msg.size()), &numBytesWritten, NULL);
|
||||||
closesocket(_clientSocket);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
if (result) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Close();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommunicationSocket::ReadLine(wstring* response)
|
bool CommunicationSocket::ReadLine(wstring* response)
|
||||||
|
@ -109,21 +85,43 @@ bool CommunicationSocket::ReadLine(wstring* response)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<char> resp_utf8;
|
response->clear();
|
||||||
char buffer;
|
|
||||||
|
if (_pipe == INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int bytesRead = recv(_clientSocket, &buffer, 1, 0);
|
int lbPos = 0;
|
||||||
if (bytesRead <= 0) {
|
auto it = std::find(_buffer.begin() + lbPos, _buffer.end(), '\n');
|
||||||
response = 0;
|
if (it != _buffer.end()) {
|
||||||
|
*response = StringUtil::toUtf16(_buffer.data(), DWORD(it - _buffer.begin()));
|
||||||
|
_buffer.erase(_buffer.begin(), it + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<char, 128> resp_utf8;
|
||||||
|
DWORD numBytesRead = 0;
|
||||||
|
DWORD totalBytesAvailable = 0;
|
||||||
|
auto result = PeekNamedPipe(_pipe, NULL, 0, 0, &totalBytesAvailable, 0);
|
||||||
|
if (!result) {
|
||||||
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (totalBytesAvailable == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer == '\n') {
|
result = ReadFile(_pipe, resp_utf8.data(), DWORD(resp_utf8.size()), &numBytesRead, NULL);
|
||||||
resp_utf8.push_back(0);
|
if (!result) {
|
||||||
*response = StringUtil::toUtf16(&resp_utf8[0], resp_utf8.size());
|
Close();
|
||||||
return true;
|
return false;
|
||||||
} else {
|
}
|
||||||
resp_utf8.push_back(buffer);
|
if (numBytesRead <= 0) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
_buffer.insert(_buffer.end(), resp_utf8.begin(), resp_utf8.begin()+numBytesRead);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,23 +20,27 @@
|
||||||
#pragma warning (disable : 4251)
|
#pragma warning (disable : 4251)
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <WinSock2.h>
|
#include <WinSock2.h>
|
||||||
|
|
||||||
class __declspec(dllexport) CommunicationSocket
|
class __declspec(dllexport) CommunicationSocket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CommunicationSocket(int port);
|
CommunicationSocket();
|
||||||
~CommunicationSocket();
|
~CommunicationSocket();
|
||||||
|
|
||||||
bool Connect();
|
bool Connect(const std::wstring& pipename);
|
||||||
bool Close();
|
bool Close();
|
||||||
|
|
||||||
bool SendMsg(const wchar_t*);
|
bool SendMsg(const wchar_t*);
|
||||||
bool ReadLine(std::wstring*);
|
bool ReadLine(std::wstring*);
|
||||||
|
|
||||||
|
HANDLE Event() { return _pipe; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _port;
|
HANDLE _pipe;
|
||||||
SOCKET _clientSocket;
|
std::vector<char> _buffer;
|
||||||
|
bool _connected;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -20,88 +20,173 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <shlobj.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
RemotePathChecker::RemotePathChecker(int port)
|
|
||||||
: _port(port)
|
// This code is run in a thread
|
||||||
|
void RemotePathChecker::workerThreadLoop()
|
||||||
{
|
{
|
||||||
|
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
||||||
|
pipename += L"ownCloud";
|
||||||
|
|
||||||
|
bool connected = false;
|
||||||
|
CommunicationSocket socket;
|
||||||
|
std::unordered_set<std::wstring> asked;
|
||||||
|
|
||||||
|
while(!_stop) {
|
||||||
|
Sleep(50);
|
||||||
|
|
||||||
|
if (!connected) {
|
||||||
|
asked.clear();
|
||||||
|
if (!WaitNamedPipe(pipename.data(), 5 * 1000)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!socket.Connect(pipename)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
connected = true;
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
while (!_pending.empty() && !_stop) {
|
||||||
|
auto filePath = _pending.front();
|
||||||
|
_pending.pop();
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
if (!asked.count(filePath)) {
|
||||||
|
asked.insert(filePath);
|
||||||
|
socket.SendMsg(wstring(L"RETRIEVE_FILE_STATUS:" + filePath + L'\n').data());
|
||||||
|
}
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring response;
|
||||||
|
while (!_stop && socket.ReadLine(&response)) {
|
||||||
|
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||||
|
wstring responsePath = response.substr(14); // length of REGISTER_PATH:
|
||||||
|
|
||||||
|
{ std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_watchedDirectories.push_back(responsePath);
|
||||||
|
}
|
||||||
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, responsePath.data(), NULL);
|
||||||
|
} else if (StringUtil::begins_with(response, wstring(L"UNREGISTER_PATH:"))) {
|
||||||
|
wstring responsePath = response.substr(16); // length of UNREGISTER_PATH:
|
||||||
|
|
||||||
|
{ std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_watchedDirectories.erase(
|
||||||
|
std::remove(_watchedDirectories.begin(), _watchedDirectories.end(), responsePath),
|
||||||
|
_watchedDirectories.end());
|
||||||
|
|
||||||
|
// Remove any item from the cache
|
||||||
|
for (auto it = _cache.begin(); it != _cache.end() ; ) {
|
||||||
|
if (StringUtil::begins_with(it->first, responsePath)) {
|
||||||
|
it = _cache.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, responsePath.data(), NULL);
|
||||||
|
} else if (StringUtil::begins_with(response, wstring(L"STATUS:")) ||
|
||||||
|
StringUtil::begins_with(response, wstring(L"BROADCAST:"))) {
|
||||||
|
|
||||||
|
auto statusBegin = response.find(L':', 0);
|
||||||
|
assert(statusBegin != std::wstring::npos);
|
||||||
|
|
||||||
|
auto statusEnd = response.find(L':', statusBegin + 1);
|
||||||
|
if (statusEnd == std::wstring::npos) {
|
||||||
|
// the command do not contains two colon?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto responseStatus = response.substr(statusBegin+1, statusEnd - statusBegin-1);
|
||||||
|
auto responsePath = response.substr(statusEnd+1);
|
||||||
|
auto state = _StrToFileState(responseStatus);
|
||||||
|
auto erased = asked.erase(responsePath);
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
{ std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto &it = _cache[responsePath];
|
||||||
|
changed = it == state;
|
||||||
|
it = state;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket.Event() == INVALID_HANDLE_VALUE) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_cache.clear();
|
||||||
|
_watchedDirectories.clear();
|
||||||
|
_connected = connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stop) return;
|
||||||
|
|
||||||
|
HANDLE handles[2] = { _newQueries, socket.Event() };
|
||||||
|
WaitForMultipleObjects(2, handles, false, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
RemotePathChecker::RemotePathChecker()
|
||||||
|
: _connected(false)
|
||||||
|
, _newQueries(CreateEvent(NULL, true, true, NULL))
|
||||||
|
, _thread([this]{ this->workerThreadLoop(); })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RemotePathChecker::~RemotePathChecker()
|
||||||
|
{
|
||||||
|
_stop = true;
|
||||||
|
//_newQueries.notify_all();
|
||||||
|
SetEvent(_newQueries);
|
||||||
|
_thread.join();
|
||||||
|
CloseHandle(_newQueries);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<wstring> RemotePathChecker::WatchedDirectories()
|
vector<wstring> RemotePathChecker::WatchedDirectories()
|
||||||
{
|
{
|
||||||
vector<wstring> watchedDirectories;
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
wstring response;
|
return _watchedDirectories;
|
||||||
bool needed = false;
|
|
||||||
|
|
||||||
CommunicationSocket socket(_port);
|
|
||||||
socket.Connect();
|
|
||||||
|
|
||||||
while (socket.ReadLine(&response)) {
|
|
||||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
|
||||||
size_t pathBegin = response.find(L':', 0);
|
|
||||||
if (pathBegin == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// chop trailing '\n'
|
|
||||||
wstring responsePath = response.substr(pathBegin + 1, response.length()-1);
|
|
||||||
watchedDirectories.push_back(responsePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return watchedDirectories;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemotePathChecker::IsMonitoredPath(const wchar_t* filePath, int* state)
|
bool RemotePathChecker::IsMonitoredPath(const wchar_t* filePath, int* state)
|
||||||
{
|
{
|
||||||
wstring request;
|
assert(state); assert(filePath);
|
||||||
wstring response;
|
|
||||||
bool needed = false;
|
|
||||||
|
|
||||||
CommunicationSocket socket(_port);
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
socket.Connect();
|
if (!_connected) {
|
||||||
request = L"RETRIEVE_FILE_STATUS:";
|
return false;
|
||||||
request += filePath;
|
}
|
||||||
request += L'\n';
|
|
||||||
|
|
||||||
if (!socket.SendMsg(request.c_str())) {
|
auto path = std::wstring(filePath);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (socket.ReadLine(&response)) {
|
auto it = _cache.find(path);
|
||||||
// discard broadcast messages
|
if (it != _cache.end()) {
|
||||||
if (StringUtil::begins_with(response, wstring(L"STATUS:"))) {
|
*state = it->second;
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
size_t statusBegin = response.find(L':', 0);
|
_pending.push(filePath);
|
||||||
if (statusBegin == -1)
|
SetEvent(_newQueries);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
size_t statusEnd = response.find(L':', statusBegin + 1);
|
|
||||||
if (statusEnd == -1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
|
||||||
wstring responseStatus = response.substr(statusBegin+1, statusEnd - statusBegin-1);
|
|
||||||
wstring responsePath = response.substr(statusEnd+1);
|
|
||||||
if (responsePath == filePath) {
|
|
||||||
if (!state) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*state = _StrToFileState(responseStatus);
|
|
||||||
if (*state == StateNone) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
needed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return needed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemotePathChecker::_StrToFileState(const std::wstring &str)
|
RemotePathChecker::FileState RemotePathChecker::_StrToFileState(const std::wstring &str)
|
||||||
{
|
{
|
||||||
if (str == L"NOP" || str == L"NONE") {
|
if (str == L"NOP" || str == L"NONE") {
|
||||||
return StateNone;
|
return StateNone;
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
@ -29,14 +35,33 @@ public:
|
||||||
StateWarning, StateWarningSWM,
|
StateWarning, StateWarningSWM,
|
||||||
StateNone
|
StateNone
|
||||||
};
|
};
|
||||||
RemotePathChecker(int port);
|
RemotePathChecker();
|
||||||
|
~RemotePathChecker();
|
||||||
std::vector<std::wstring> WatchedDirectories();
|
std::vector<std::wstring> WatchedDirectories();
|
||||||
bool IsMonitoredPath(const wchar_t* filePath, int* state);
|
bool IsMonitoredPath(const wchar_t* filePath, int* state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _StrToFileState(const std::wstring &str);
|
FileState _StrToFileState(const std::wstring &str);
|
||||||
int _port;
|
std::mutex _mutex;
|
||||||
|
std::atomic<bool> _stop;
|
||||||
|
|
||||||
|
// Everything here is protected by the _mutex
|
||||||
|
|
||||||
|
/** The list of paths we need to query. The main thread fill this, and the worker thread
|
||||||
|
* send that to the socket. */
|
||||||
|
std::queue<std::wstring> _pending;
|
||||||
|
|
||||||
|
std::unordered_map<std::wstring, FileState> _cache;
|
||||||
|
std::vector<std::wstring> _watchedDirectories;
|
||||||
|
bool _connected;
|
||||||
|
|
||||||
|
|
||||||
|
// The main thread notifies when there are new items in _pending
|
||||||
|
//std::condition_variable _newQueries;
|
||||||
|
HANDLE _newQueries;
|
||||||
|
|
||||||
|
std::thread _thread;
|
||||||
|
void workerThreadLoop();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -11,22 +11,26 @@
|
||||||
* details.
|
* details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <locale>
|
||||||
|
#include <string>
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
|
|
||||||
char* StringUtil::toUtf8(const wchar_t *utf16, int len)
|
std::string StringUtil::toUtf8(const wchar_t *utf16, int len)
|
||||||
{
|
{
|
||||||
int newlen = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL);
|
if (len < 0) {
|
||||||
char* str = new char[newlen];
|
len = wcslen(utf16);
|
||||||
WideCharToMultiByte(CP_UTF8, 0, utf16, -1, str, newlen, NULL, NULL);
|
}
|
||||||
return str;
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
|
||||||
|
return converter.to_bytes(utf16, utf16+len);
|
||||||
}
|
}
|
||||||
|
|
||||||
wchar_t* StringUtil::toUtf16(const char *utf8, int len)
|
std::wstring StringUtil::toUtf16(const char *utf8, int len)
|
||||||
{
|
{
|
||||||
int newlen = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
|
if (len < 0) {
|
||||||
wchar_t* wstr = new wchar_t[newlen];
|
len = strlen(utf8);
|
||||||
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, newlen);
|
}
|
||||||
return wstr;
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
|
||||||
|
return converter.from_bytes(utf8, utf8+len);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,14 @@
|
||||||
|
|
||||||
class __declspec(dllexport) StringUtil {
|
class __declspec(dllexport) StringUtil {
|
||||||
public:
|
public:
|
||||||
static char* toUtf8(const wchar_t* utf16, int len = -1);
|
static std::string toUtf8(const wchar_t* utf16, int len = -1);
|
||||||
static wchar_t* toUtf16(const char* utf8, int len = -1);
|
static std::wstring toUtf16(const char* utf8, int len = -1);
|
||||||
|
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
static bool begins_with(const T& input, const T& match)
|
static bool begins_with(const T& input, const T& match)
|
||||||
{
|
{
|
||||||
return input.size() >= match.size()
|
return input.size() >= match.size()
|
||||||
&& equal(match.begin(), match.end(), input.begin());
|
&& std::equal(match.begin(), match.end(), input.begin());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
148882
src/3rdparty/sqlite3/sqlite3.c
vendored
Normal file
148882
src/3rdparty/sqlite3/sqlite3.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
7494
src/3rdparty/sqlite3/sqlite3.h
vendored
Normal file
7494
src/3rdparty/sqlite3/sqlite3.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
163
src/cmd/cmd.cpp
163
src/cmd/cmd.cpp
|
@ -24,8 +24,8 @@
|
||||||
|
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "clientproxy.h"
|
#include "clientproxy.h"
|
||||||
|
#include "mirallconfigfile.h" // ONLY ACCESS THE STATIC FUNCTIONS!
|
||||||
#include "creds/httpcredentials.h"
|
#include "creds/httpcredentials.h"
|
||||||
#include "csync.h"
|
|
||||||
#include "simplesslerrorhandler.h"
|
#include "simplesslerrorhandler.h"
|
||||||
#include "syncengine.h"
|
#include "syncengine.h"
|
||||||
#include "syncjournaldb.h"
|
#include "syncjournaldb.h"
|
||||||
|
@ -33,8 +33,12 @@
|
||||||
|
|
||||||
#include "cmd.h"
|
#include "cmd.h"
|
||||||
|
|
||||||
|
#include "theme.h"
|
||||||
#include "netrcparser.h"
|
#include "netrcparser.h"
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#else
|
#else
|
||||||
|
@ -67,7 +71,7 @@ public:
|
||||||
EchoDisabler()
|
EchoDisabler()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
GetConsoleMode(hStdin, &mode);
|
GetConsoleMode(hStdin, &mode);
|
||||||
SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));
|
SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));
|
||||||
#else
|
#else
|
||||||
|
@ -89,6 +93,7 @@ public:
|
||||||
private:
|
private:
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
DWORD mode = 0;
|
DWORD mode = 0;
|
||||||
|
HANDLE hStdin;
|
||||||
#else
|
#else
|
||||||
termios tios;
|
termios tios;
|
||||||
#endif
|
#endif
|
||||||
|
@ -105,21 +110,37 @@ QString queryPassword(const QString &user)
|
||||||
|
|
||||||
class HttpCredentialsText : public HttpCredentials {
|
class HttpCredentialsText : public HttpCredentials {
|
||||||
public:
|
public:
|
||||||
HttpCredentialsText(const QString& user, const QString& password) : HttpCredentials(user, password) {}
|
HttpCredentialsText(const QString& user, const QString& password)
|
||||||
|
: HttpCredentials(user, password),
|
||||||
|
_sslTrusted(false)
|
||||||
|
{}
|
||||||
|
|
||||||
QString queryPassword(bool *ok) {
|
QString queryPassword(bool *ok) {
|
||||||
if (ok) {
|
if (ok) {
|
||||||
*ok = true;
|
*ok = true;
|
||||||
}
|
}
|
||||||
return ::queryPassword(user());
|
return ::queryPassword(user());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSSLTrusted( bool isTrusted ) {
|
||||||
|
_sslTrusted = isTrusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sslIsTrusted() {
|
||||||
|
return _sslTrusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _sslTrusted;
|
||||||
};
|
};
|
||||||
|
|
||||||
void help()
|
void help()
|
||||||
{
|
{
|
||||||
const char* appName = APPLICATION_EXECUTABLE "cmd";
|
const char *binaryName = APPLICATION_EXECUTABLE "cmd";
|
||||||
std::cout << appName << " - command line " APPLICATION_NAME " client tool." << std::endl;
|
|
||||||
|
std::cout << binaryName << " - command line " APPLICATION_NAME " client tool" << std::endl;
|
||||||
std::cout << "" << std::endl;
|
std::cout << "" << std::endl;
|
||||||
std::cout << "Usage: " << appName << " <source_dir> <server_url>" << std::endl;
|
std::cout << "Usage: " << binaryName << " <source_dir> <server_url>" << std::endl;
|
||||||
std::cout << "" << std::endl;
|
std::cout << "" << std::endl;
|
||||||
std::cout << "A proxy can either be set manually using --httpproxy." << std::endl;
|
std::cout << "A proxy can either be set manually using --httpproxy." << std::endl;
|
||||||
std::cout << "Otherwise, the setting from a configured sync client will be used." << std::endl;
|
std::cout << "Otherwise, the setting from a configured sync client will be used." << std::endl;
|
||||||
|
@ -129,37 +150,52 @@ void help()
|
||||||
std::cout << " --httpproxy [proxy] Specify a http proxy to use." << std::endl;
|
std::cout << " --httpproxy [proxy] Specify a http proxy to use." << std::endl;
|
||||||
std::cout << " Proxy is http://server:port" << std::endl;
|
std::cout << " Proxy is http://server:port" << std::endl;
|
||||||
std::cout << " --trust Trust the SSL certification." << std::endl;
|
std::cout << " --trust Trust the SSL certification." << std::endl;
|
||||||
std::cout << " --exclude [file] exclude list file" << std::endl;
|
std::cout << " --exclude [file] Exclude list file" << std::endl;
|
||||||
std::cout << " --user, -u [name] Use [name] as the login name" << std::endl;
|
std::cout << " --user, -u [name] Use [name] as the login name" << std::endl;
|
||||||
std::cout << " --password, -p [pass] Use [pass] as password" << std::endl;
|
std::cout << " --password, -p [pass] Use [pass] as password" << std::endl;
|
||||||
std::cout << " -n Use netrc (5) for login" << std::endl;
|
std::cout << " -n Use netrc (5) for login" << std::endl;
|
||||||
std::cout << " --non-interactive Do not block execution with interaction" << std::endl;
|
std::cout << " --non-interactive Do not block execution with interaction" << std::endl;
|
||||||
|
std::cout << " --version, -v Display version and exit" << std::endl;
|
||||||
std::cout << "" << std::endl;
|
std::cout << "" << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showVersion() {
|
||||||
|
const char *binaryName = APPLICATION_EXECUTABLE "cmd";
|
||||||
|
std::cout << binaryName << " version " << qPrintable(Theme::instance()->version()) << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
void parseOptions( const QStringList& app_args, CmdOptions *options )
|
void parseOptions( const QStringList& app_args, CmdOptions *options )
|
||||||
{
|
{
|
||||||
QStringList args(app_args);
|
QStringList args(app_args);
|
||||||
|
|
||||||
if( args.count() < 3 ) {
|
int argCount = args.count();
|
||||||
|
|
||||||
|
if( argCount < 3 ) {
|
||||||
|
if (argCount >= 2) {
|
||||||
|
const QString option = args.at(1);
|
||||||
|
if (option == "-v" || option == "--version") {
|
||||||
|
showVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
help();
|
help();
|
||||||
}
|
}
|
||||||
|
|
||||||
options->target_url = args.takeLast();
|
options->target_url = args.takeLast();
|
||||||
// check if the remote.php/webdav tail was added and append if not.
|
// check if the remote.php/webdav tail was added and append if not.
|
||||||
if( !options->target_url.contains("remote.php/webdav")) {
|
if(!options->target_url.endsWith("/")) {
|
||||||
if(!options->target_url.endsWith("/")) {
|
options->target_url.append("/");
|
||||||
options->target_url.append("/");
|
}
|
||||||
}
|
if( !options->target_url.contains("remote.php/webdav/")) {
|
||||||
options->target_url.append("remote.php/webdav");
|
options->target_url.append("remote.php/webdav/");
|
||||||
}
|
}
|
||||||
if (options->target_url.startsWith("http"))
|
if (options->target_url.startsWith("http"))
|
||||||
options->target_url.replace(0, 4, "owncloud");
|
options->target_url.replace(0, 4, "owncloud");
|
||||||
options->source_dir = args.takeLast();
|
options->source_dir = args.takeLast();
|
||||||
if( !QFile::exists( options->source_dir )) {
|
if( !QFile::exists( options->source_dir )) {
|
||||||
std::cerr << "Source dir does not exists." << std::endl;
|
std::cerr << "Source dir '" << qPrintable(options->source_dir) << "' does not exist." << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +219,7 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
|
||||||
} else if( (option == "-u" || option == "--user") && !it.peekNext().startsWith("-") ) {
|
} else if( (option == "-u" || option == "--user") && !it.peekNext().startsWith("-") ) {
|
||||||
options->user = it.next();
|
options->user = it.next();
|
||||||
} else if( (option == "-p" || option == "--password") && !it.peekNext().startsWith("-") ) {
|
} else if( (option == "-p" || option == "--password") && !it.peekNext().startsWith("-") ) {
|
||||||
options->user = it.next();
|
options->password = it.next();
|
||||||
} else if( option == "--exclude" && !it.peekNext().startsWith("-") ) {
|
} else if( option == "--exclude" && !it.peekNext().startsWith("-") ) {
|
||||||
options->exclude = it.next();
|
options->exclude = it.next();
|
||||||
} else {
|
} else {
|
||||||
|
@ -208,43 +244,45 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
parseOptions( app.arguments(), &options );
|
parseOptions( app.arguments(), &options );
|
||||||
|
|
||||||
|
QUrl url = QUrl::fromUserInput(options.target_url);
|
||||||
|
|
||||||
QUrl url = QUrl::fromUserInput(options.target_url);
|
// Order of retrieval attempt (later attempts override earlier ones):
|
||||||
|
// 1. From URL
|
||||||
|
// 2. From options
|
||||||
|
// 3. From netrc (if enabled)
|
||||||
|
// 4. From prompt (if interactive)
|
||||||
|
|
||||||
// Fetch username and password. If empty, try to retrieve
|
QString user = url.userName();
|
||||||
// from URL and strip URL
|
QString password = url.password();
|
||||||
QString user;
|
|
||||||
QString password;
|
|
||||||
|
|
||||||
if (options.useNetrc) {
|
if (!options.user.isEmpty()) {
|
||||||
NetrcParser parser;
|
user = options.user;
|
||||||
if (parser.parse()) {
|
}
|
||||||
NetrcParser::LoginPair pair = parser.find(url.host());
|
|
||||||
user = pair.first;
|
|
||||||
password = pair.second;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
user = options.user;
|
|
||||||
if (user.isEmpty()) {
|
|
||||||
user = url.userName();
|
|
||||||
}
|
|
||||||
password = options.password;
|
|
||||||
if (password.isEmpty()) {
|
|
||||||
password = url.password();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.interactive) {
|
if (!options.password.isEmpty()) {
|
||||||
if (user.isEmpty()) {
|
password = options.password;
|
||||||
std::cout << "Please enter user name: ";
|
}
|
||||||
std::string s;
|
|
||||||
std::getline(std::cin, s);
|
if (options.useNetrc) {
|
||||||
user = QString::fromStdString(s);
|
NetrcParser parser;
|
||||||
}
|
if (parser.parse()) {
|
||||||
if (password.isEmpty()) {
|
NetrcParser::LoginPair pair = parser.find(url.host());
|
||||||
password = queryPassword(user);
|
user = pair.first;
|
||||||
}
|
password = pair.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.interactive) {
|
||||||
|
if (user.isEmpty()) {
|
||||||
|
std::cout << "Please enter user name: ";
|
||||||
|
std::string s;
|
||||||
|
std::getline(std::cin, s);
|
||||||
|
user = QString::fromStdString(s);
|
||||||
|
}
|
||||||
|
if (password.isEmpty()) {
|
||||||
|
password = queryPassword(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ### ensure URL is free of credentials
|
// ### ensure URL is free of credentials
|
||||||
if (url.userName().isEmpty()) {
|
if (url.userName().isEmpty()) {
|
||||||
|
@ -254,18 +292,25 @@ int main(int argc, char **argv) {
|
||||||
url.setPassword(password);
|
url.setPassword(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// take the unmodified url to pass to csync_create()
|
||||||
|
QByteArray remUrl = options.target_url.toUtf8();
|
||||||
|
|
||||||
Account account;
|
Account account;
|
||||||
|
|
||||||
// Find the folder and the original owncloud url
|
// Find the folder and the original owncloud url
|
||||||
QStringList splitted = url.path().split(account.davPath());
|
QStringList splitted = url.path().split(account.davPath());
|
||||||
url.setPath(splitted.value(0));
|
url.setPath(splitted.value(0));
|
||||||
|
|
||||||
url.setScheme(url.scheme().replace("owncloud", "http"));
|
url.setScheme(url.scheme().replace("owncloud", "http"));
|
||||||
QString folder = splitted.value(1);
|
QString folder = splitted.value(1);
|
||||||
|
|
||||||
SimpleSslErrorHandler *sslErrorHandler = new SimpleSslErrorHandler;
|
SimpleSslErrorHandler *sslErrorHandler = new SimpleSslErrorHandler;
|
||||||
|
|
||||||
HttpCredentials *cred = new HttpCredentialsText(user, password);
|
HttpCredentialsText *cred = new HttpCredentialsText(user, password);
|
||||||
|
|
||||||
|
if( options.trustSSL ) {
|
||||||
|
cred->setSSLTrusted(true);
|
||||||
|
}
|
||||||
account.setUrl(url);
|
account.setUrl(url);
|
||||||
account.setCredentials(cred);
|
account.setCredentials(cred);
|
||||||
account.setSslErrorHandler(sslErrorHandler);
|
account.setSslErrorHandler(sslErrorHandler);
|
||||||
|
@ -275,8 +320,9 @@ int main(int argc, char **argv) {
|
||||||
restart_sync:
|
restart_sync:
|
||||||
|
|
||||||
CSYNC *_csync_ctx;
|
CSYNC *_csync_ctx;
|
||||||
|
|
||||||
if( csync_create( &_csync_ctx, options.source_dir.toUtf8(),
|
if( csync_create( &_csync_ctx, options.source_dir.toUtf8(),
|
||||||
url.toEncoded().constData()) < 0 ) {
|
remUrl.constData()) < 0 ) {
|
||||||
qFatal("Unable to create csync-context!");
|
qFatal("Unable to create csync-context!");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -331,14 +377,29 @@ restart_sync:
|
||||||
clientProxy.setCSyncProxy(QUrl(url), _csync_ctx);
|
clientProxy.setCSyncProxy(QUrl(url), _csync_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exclude lists
|
||||||
|
QString systemExcludeListFn = MirallConfigFile::excludeFileFromSystem();
|
||||||
|
int loadedSystemExcludeList = false;
|
||||||
|
if (!systemExcludeListFn.isEmpty()) {
|
||||||
|
loadedSystemExcludeList = csync_add_exclude_list(_csync_ctx, systemExcludeListFn.toLocal8Bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
int loadedUserExcludeList = false;
|
||||||
if (!options.exclude.isEmpty()) {
|
if (!options.exclude.isEmpty()) {
|
||||||
csync_add_exclude_list(_csync_ctx, options.exclude.toLocal8Bit());
|
loadedUserExcludeList = csync_add_exclude_list(_csync_ctx, options.exclude.toLocal8Bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedSystemExcludeList != 0 && loadedUserExcludeList != 0) {
|
||||||
|
// Always make sure at least one list had been loaded
|
||||||
|
qFatal("Cannot load system exclude list or list supplied via --exclude");
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
cred->syncContextPreStart(_csync_ctx);
|
cred->syncContextPreStart(_csync_ctx);
|
||||||
|
|
||||||
Cmd cmd;
|
Cmd cmd;
|
||||||
SyncJournalDb db(options.source_dir);
|
SyncJournalDb db(options.source_dir);
|
||||||
|
|
||||||
SyncEngine engine(_csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
|
SyncEngine engine(_csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
|
||||||
QObject::connect(&engine, SIGNAL(finished()), &app, SLOT(quit()));
|
QObject::connect(&engine, SIGNAL(finished()), &app, SLOT(quit()));
|
||||||
QObject::connect(&engine, SIGNAL(transmissionProgress(Progress::Info)), &cmd, SLOT(transmissionProgressSlot()));
|
QObject::connect(&engine, SIGNAL(transmissionProgress(Progress::Info)), &cmd, SLOT(transmissionProgressSlot()));
|
||||||
|
|
|
@ -193,13 +193,7 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 main.cpp ${final_src})
|
add_executable( ${APPLICATION_EXECUTABLE} WIN32 main.cpp ${final_src})
|
||||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
|
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
|
||||||
else()
|
else()
|
||||||
if (Qt5Core_FOUND)
|
# set(CMAKE_INSTALL_PREFIX ".") # Examples use /Applications. hurmpf.
|
||||||
include(DeployQt5)
|
|
||||||
else(Qt5Core_FOUND)
|
|
||||||
include(DeployQt4)
|
|
||||||
endif(Qt5Core_FOUND)
|
|
||||||
|
|
||||||
set(CMAKE_INSTALL_PREFIX ".") # Examples use /Applications. hurmpf.
|
|
||||||
set(MACOSX_BUNDLE_ICON_FILE "ownCloud.icns")
|
set(MACOSX_BUNDLE_ICON_FILE "ownCloud.icns")
|
||||||
|
|
||||||
# we must add MACOSX_BUNDLE only if building a bundle
|
# we must add MACOSX_BUNDLE only if building a bundle
|
||||||
|
@ -208,8 +202,15 @@ else()
|
||||||
|
|
||||||
set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
|
set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
|
||||||
install(FILES ${mirall_I18N} DESTINATION ${QM_DIR})
|
install(FILES ${mirall_I18N} DESTINATION ${QM_DIR})
|
||||||
|
get_target_property(_qmake Qt5::qmake LOCATION)
|
||||||
|
execute_process(COMMAND ${_qmake} -query QT_INSTALL_TRANSLATIONS
|
||||||
|
OUTPUT_VARIABLE QT_TRANSLATIONS_DIR
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
file(GLOB qt_I18N ${QT_TRANSLATIONS_DIR}/qt_??.qm ${QT_TRANSLATIONS_DIR}/qt_??_??.qm)
|
file(GLOB qt_I18N ${QT_TRANSLATIONS_DIR}/qt_??.qm ${QT_TRANSLATIONS_DIR}/qt_??_??.qm)
|
||||||
install(FILES ${qt_I18N} DESTINATION ${QM_DIR})
|
install(FILES ${qt_I18N} DESTINATION ${QM_DIR})
|
||||||
|
file(GLOB qtbase_I18N ${QT_TRANSLATIONS_DIR}/qtbase_??.qm ${QT_TRANSLATIONS_DIR}/qt_??_??.qm)
|
||||||
|
install(FILES ${qtbase_I18N} DESTINATION ${QM_DIR})
|
||||||
file(GLOB qtkeychain_I18N ${QT_TRANSLATIONS_DIR}/qtkeychain*.qm)
|
file(GLOB qtkeychain_I18N ${QT_TRANSLATIONS_DIR}/qtkeychain*.qm)
|
||||||
install(FILES ${qtkeychain_I18N} DESTINATION ${QM_DIR})
|
install(FILES ${qtkeychain_I18N} DESTINATION ${QM_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
@ -221,6 +222,7 @@ qt5_use_modules(updater Widgets Network Xml)
|
||||||
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
|
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}
|
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}
|
||||||
)
|
)
|
||||||
|
# Only relevant for Linux? On OS X it by default properly checks in the bundle directory next to the exe
|
||||||
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
|
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
|
||||||
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE}" )
|
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE}" )
|
||||||
|
|
||||||
|
@ -233,19 +235,19 @@ install(TARGETS ${APPLICATION_EXECUTABLE}
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION bin
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION lib
|
||||||
ARCHIVE DESTINATION lib
|
ARCHIVE DESTINATION lib
|
||||||
BUNDLE DESTINATION bin
|
BUNDLE DESTINATION "."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#FIXME: find a nice solution to make the second if(BUILD_OWNCLOUD_OSX_BUNDLE) unnecessary
|
#FIXME: find a nice solution to make the second if(BUILD_OWNCLOUD_OSX_BUNDLE) unnecessary
|
||||||
# currently it needs to be done because the code right above needs to be executed no matter
|
# currently it needs to be done because the code right above needs to be executed no matter
|
||||||
# if building a bundle or not and the install_qt4_executable needs to be called afterwards
|
# if building a bundle or not and the install_qt4_executable needs to be called afterwards
|
||||||
if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
if(BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY)
|
||||||
if(Qt5Core_FOUND)
|
get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
|
||||||
install_qt5_executable(${OWNCLOUD_OSX_BUNDLE} "qtaccessiblewidgets;qsqlite;qcocoa")
|
install(CODE "
|
||||||
else(Qt5Core_FOUND)
|
message(STATUS \"Deploying (Qt) dependencies and fixing library pathes...\")
|
||||||
install_qt4_executable(${OWNCLOUD_OSX_BUNDLE} "qtaccessiblewidgets;qsqlite")
|
execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/admin/osx/macdeployqt.py\" ${CMAKE_INSTALL_PREFIX}/${OWNCLOUD_OSX_BUNDLE} ${QT_QMAKE_EXECUTABLE})
|
||||||
endif(Qt5Core_FOUND)
|
" COMPONENT RUNTIME)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT WIN32)
|
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT WIN32)
|
||||||
|
|
|
@ -383,7 +383,7 @@ void AccountSettings::slotResetCurrentFolder()
|
||||||
if( ret == QMessageBox::Yes ) {
|
if( ret == QMessageBox::Yes ) {
|
||||||
FolderMan *folderMan = FolderMan::instance();
|
FolderMan *folderMan = FolderMan::instance();
|
||||||
Folder *f = folderMan->folder(alias);
|
Folder *f = folderMan->folder(alias);
|
||||||
f->slotTerminateAndPauseSync();
|
f->slotTerminateSync();
|
||||||
f->wipe();
|
f->wipe();
|
||||||
folderMan->slotScheduleAllFolders();
|
folderMan->slotScheduleAllFolders();
|
||||||
}
|
}
|
||||||
|
@ -499,7 +499,7 @@ void AccountSettings::slotEnableCurrentFolder()
|
||||||
// message box can return at any time while the thread keeps running,
|
// message box can return at any time while the thread keeps running,
|
||||||
// so better check again after the user has responded.
|
// so better check again after the user has responded.
|
||||||
if ( f->isBusy() && terminate ) {
|
if ( f->isBusy() && terminate ) {
|
||||||
f->slotTerminateAndPauseSync();
|
f->slotTerminateSync();
|
||||||
}
|
}
|
||||||
f->setSyncPaused(!currentlyPaused); // toggle the pause setting
|
f->setSyncPaused(!currentlyPaused); // toggle the pause setting
|
||||||
folderMan->slotSetFolderPaused( alias, !currentlyPaused );
|
folderMan->slotSetFolderPaused( alias, !currentlyPaused );
|
||||||
|
@ -605,7 +605,7 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||||
item->setData( QVariant(true), FolderStatusDelegate::AddProgressSpace );
|
item->setData( QVariant(true), FolderStatusDelegate::AddProgressSpace );
|
||||||
|
|
||||||
if (!progress._currentDiscoveredFolder.isEmpty()) {
|
if (!progress._currentDiscoveredFolder.isEmpty()) {
|
||||||
item->setData( tr("Discovering %1").arg(progress._currentDiscoveredFolder) , FolderStatusDelegate::SyncProgressItemString );
|
item->setData( tr("Discovering '%1'").arg(progress._currentDiscoveredFolder) , FolderStatusDelegate::SyncProgressItemString );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -534,12 +534,15 @@ void Application::setupTranslations()
|
||||||
setProperty("ui_lang", lang);
|
setProperty("ui_lang", lang);
|
||||||
const QString qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
|
const QString qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
|
||||||
const QString qtTrFile = QLatin1String("qt_") + lang;
|
const QString qtTrFile = QLatin1String("qt_") + lang;
|
||||||
if (qtTranslator->load(qtTrFile, qtTrPath)) {
|
const QString qtBaseTrFile = QLatin1String("qtbase_") + lang;
|
||||||
qtTranslator->load(qtTrFile, trPath);
|
if (!qtTranslator->load(qtTrFile, qtTrPath)) {
|
||||||
|
if (!qtTranslator->load(qtTrFile, trPath)) {
|
||||||
|
qtTranslator->load(qtBaseTrFile, trPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const QString qtkeychainFile = QLatin1String("qt_") + lang;
|
const QString qtkeychainTrFile = QLatin1String("qtkeychain_") + lang;
|
||||||
if (!qtkeychainTranslator->load(qtkeychainFile, qtTrPath)) {
|
if (!qtkeychainTranslator->load(qtkeychainTrFile, qtTrPath)) {
|
||||||
qtkeychainTranslator->load(qtkeychainFile, trPath);
|
qtkeychainTranslator->load(qtkeychainTrFile, trPath);
|
||||||
}
|
}
|
||||||
if (!translator->isEmpty())
|
if (!translator->isEmpty())
|
||||||
installTranslator(translator);
|
installTranslator(translator);
|
||||||
|
|
|
@ -61,6 +61,7 @@ Folder::Folder(const QString &alias, const QString &path, const QString& secondP
|
||||||
, _csyncUnavail(false)
|
, _csyncUnavail(false)
|
||||||
, _wipeDb(false)
|
, _wipeDb(false)
|
||||||
, _proxyDirty(true)
|
, _proxyDirty(true)
|
||||||
|
, _forceSyncOnPollTimeout(false)
|
||||||
, _journal(path)
|
, _journal(path)
|
||||||
, _csync_ctx(0)
|
, _csync_ctx(0)
|
||||||
{
|
{
|
||||||
|
@ -270,10 +271,25 @@ void Folder::slotPollTimerTimeout()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quint64(_timeSinceLastSync.elapsed()) > MirallConfigFile().forceSyncInterval() ||
|
bool forceSyncIntervalExpired =
|
||||||
_lastEtag.isNull() ||
|
quint64(_timeSinceLastSync.elapsed()) > MirallConfigFile().forceSyncInterval();
|
||||||
!(_syncResult.status() == SyncResult::Success ||_syncResult.status() == SyncResult::Problem)) {
|
bool okSyncResult =
|
||||||
qDebug() << "** Force Sync now, state is " << _syncResult.statusString();
|
_syncResult.status() == SyncResult::Success ||
|
||||||
|
_syncResult.status() == SyncResult::Problem;
|
||||||
|
if (forceSyncIntervalExpired ||
|
||||||
|
_forceSyncOnPollTimeout ||
|
||||||
|
!okSyncResult) {
|
||||||
|
if (forceSyncIntervalExpired) {
|
||||||
|
qDebug() << "** Force Sync, because it has been " << _timeSinceLastSync.elapsed() << "ms "
|
||||||
|
<< "since the last sync";
|
||||||
|
}
|
||||||
|
if (_forceSyncOnPollTimeout) {
|
||||||
|
qDebug() << "** Force Sync, because it was requested";
|
||||||
|
}
|
||||||
|
if (!okSyncResult) {
|
||||||
|
qDebug() << "** Force Sync, because the last sync had status: " << _syncResult.statusString();
|
||||||
|
}
|
||||||
|
_forceSyncOnPollTimeout = false;
|
||||||
emit scheduleToSync(alias());
|
emit scheduleToSync(alias());
|
||||||
} else {
|
} else {
|
||||||
// do the ordinary etag check for the root folder.
|
// do the ordinary etag check for the root folder.
|
||||||
|
@ -315,12 +331,13 @@ void Folder::bubbleUpSyncResult()
|
||||||
int updatedItems = 0;
|
int updatedItems = 0;
|
||||||
int ignoredItems = 0;
|
int ignoredItems = 0;
|
||||||
int renamedItems = 0;
|
int renamedItems = 0;
|
||||||
|
int errorItems = 0;
|
||||||
|
|
||||||
SyncFileItem firstItemNew;
|
SyncFileItem firstItemNew;
|
||||||
SyncFileItem firstItemDeleted;
|
SyncFileItem firstItemDeleted;
|
||||||
SyncFileItem firstItemUpdated;
|
SyncFileItem firstItemUpdated;
|
||||||
SyncFileItem firstItemRenamed;
|
SyncFileItem firstItemRenamed;
|
||||||
Logger *logger = Logger::instance();
|
SyncFileItem firstItemError;
|
||||||
|
|
||||||
SyncRunFileLog syncFileLog;
|
SyncRunFileLog syncFileLog;
|
||||||
|
|
||||||
|
@ -336,7 +353,10 @@ void Folder::bubbleUpSyncResult()
|
||||||
// and process the item to the gui
|
// and process the item to the gui
|
||||||
if( item._status == SyncFileItem::FatalError || item._status == SyncFileItem::NormalError ) {
|
if( item._status == SyncFileItem::FatalError || item._status == SyncFileItem::NormalError ) {
|
||||||
slotSyncError( tr("%1: %2").arg(item._file, item._errorString) );
|
slotSyncError( tr("%1: %2").arg(item._file, item._errorString) );
|
||||||
logger->postOptionalGuiLog(item._file, item._errorString);
|
errorItems++;
|
||||||
|
if (firstItemError.isEmpty()) {
|
||||||
|
firstItemError = item;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// add new directories or remove gone away dirs to the watcher
|
// add new directories or remove gone away dirs to the watcher
|
||||||
if (item._isDirectory && item._instruction == CSYNC_INSTRUCTION_NEW ) {
|
if (item._isDirectory && item._instruction == CSYNC_INSTRUCTION_NEW ) {
|
||||||
|
@ -389,9 +409,9 @@ void Folder::bubbleUpSyncResult()
|
||||||
qDebug() << "Processing result list and logging took " << timer.elapsed() << " Milliseconds.";
|
qDebug() << "Processing result list and logging took " << timer.elapsed() << " Milliseconds.";
|
||||||
_syncResult.setWarnCount(ignoredItems);
|
_syncResult.setWarnCount(ignoredItems);
|
||||||
|
|
||||||
createGuiLog( firstItemNew._file, SyncFileStatus(SyncFileStatus::STATUS_NEW), newItems );
|
createGuiLog( firstItemNew._file, SyncFileStatus::STATUS_NEW, newItems );
|
||||||
createGuiLog( firstItemDeleted._file, SyncFileStatus(SyncFileStatus::STATUS_REMOVE), removedItems );
|
createGuiLog( firstItemDeleted._file, SyncFileStatus::STATUS_REMOVE, removedItems );
|
||||||
createGuiLog( firstItemUpdated._file, SyncFileStatus(SyncFileStatus::STATUS_UPDATED), updatedItems );
|
createGuiLog( firstItemUpdated._file, SyncFileStatus::STATUS_UPDATED, updatedItems );
|
||||||
|
|
||||||
if( !firstItemRenamed.isEmpty() ) {
|
if( !firstItemRenamed.isEmpty() ) {
|
||||||
SyncFileStatus status(SyncFileStatus::STATUS_RENAME);
|
SyncFileStatus status(SyncFileStatus::STATUS_RENAME);
|
||||||
|
@ -404,6 +424,8 @@ void Folder::bubbleUpSyncResult()
|
||||||
createGuiLog( firstItemRenamed._file, status, renamedItems, firstItemRenamed._renameTarget );
|
createGuiLog( firstItemRenamed._file, status, renamedItems, firstItemRenamed._renameTarget );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createGuiLog( firstItemError._file, SyncFileStatus::STATUS_ERROR, errorItems );
|
||||||
|
|
||||||
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,6 +476,13 @@ void Folder::createGuiLog( const QString& filename, SyncFileStatus status, int c
|
||||||
text = tr("%1 has been moved to %2.").arg(file).arg(renameTarget);
|
text = tr("%1 has been moved to %2.").arg(file).arg(renameTarget);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SyncFileStatus::STATUS_ERROR:
|
||||||
|
if( count > 1 ) {
|
||||||
|
text = tr("%1 and %2 other files could not be synced due to errors. See the log for details.", "%1 names a file.").arg(file).arg(count-1);
|
||||||
|
} else {
|
||||||
|
text = tr("%1 could not be synced due to an error. See the log for details.").arg(file);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -484,11 +513,90 @@ QString Folder::configFile()
|
||||||
return _configFile;
|
return _configFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void addErroredSyncItemPathsToList(const SyncFileItemVector& items, QSet<QString>* set) {
|
||||||
|
Q_FOREACH(const SyncFileItem &item, items) {
|
||||||
|
if (item.hasErrorStatus()) {
|
||||||
|
set->insert(item._file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
|
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
|
||||||
{
|
{
|
||||||
|
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithError);
|
||||||
_syncResult.setSyncFileItemVector(items);
|
_syncResult.setSyncFileItemVector(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Folder::slotAboutToPropagate(const SyncFileItemVector& items)
|
||||||
|
{
|
||||||
|
// empty the tainted list since the status generation code will use the _syncedItems
|
||||||
|
// (which imply the folder) to generate the syncing state icon now.
|
||||||
|
_stateTaintedFolders.clear();
|
||||||
|
|
||||||
|
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Folder::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
|
||||||
|
{
|
||||||
|
if (t == CSYNC_FTW_TYPE_DIR) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "ASKING ERROR FOLDERS" << fn;
|
||||||
|
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
|
||||||
|
s->set(SyncFileStatus::STATUS_ERROR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If sync is running, check _syncedItems, possibly give it STATUS_EVAL (=syncing down)
|
||||||
|
if (!_engine.isNull()) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "SYNC IS RUNNING, asking SyncEngine" << fn;
|
||||||
|
if (_engine->estimateState(fn, t, s)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qDebug() << Q_FUNC_INFO << "ASKING TAINTED FOLDERS" << fn;
|
||||||
|
if (Utility::doesSetContainPrefix(_stateTaintedFolders, fn)) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Folder is tainted, EVAL!" << fn;
|
||||||
|
s->set(SyncFileStatus::STATUS_EVAL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if ( t== CSYNC_FTW_TYPE_FILE) {
|
||||||
|
// check if errorList has the directory/file
|
||||||
|
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
|
||||||
|
s->set(SyncFileStatus::STATUS_ERROR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If sync running: _syncedItems -> SyncingState
|
||||||
|
if (!_engine.isNull()) {
|
||||||
|
if (_engine->estimateState(fn, t, s)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Folder::watcherSlot(QString fn)
|
||||||
|
{
|
||||||
|
// FIXME: On OS X we could not do this "if" since on OS X the file watcher ignores events for ourselves
|
||||||
|
// however to have the same behaviour atm on all platforms, we don't do it
|
||||||
|
if (!_engine.isNull()) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Sync running, IGNORE event for " << fn;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFileInfo fi(fn);
|
||||||
|
if (fi.isFile()) {
|
||||||
|
fn = fi.path(); // depending on OS, file watcher might be for dir or file
|
||||||
|
}
|
||||||
|
// Make it a relative path depending on the folder
|
||||||
|
QString relativePath = fn.remove(0, path().length());
|
||||||
|
qDebug() << Q_FUNC_INFO << fi.canonicalFilePath() << fn << relativePath;
|
||||||
|
_stateTaintedFolders.insert(relativePath);
|
||||||
|
|
||||||
|
// Notify the SocketAPI?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Folder::slotTerminateSync()
|
void Folder::slotTerminateSync()
|
||||||
{
|
{
|
||||||
qDebug() << "folder " << alias() << " Terminating!";
|
qDebug() << "folder " << alias() << " Terminating!";
|
||||||
|
@ -503,12 +611,6 @@ void Folder::slotTerminateSync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Folder::slotTerminateAndPauseSync()
|
|
||||||
{
|
|
||||||
slotTerminateSync();
|
|
||||||
FolderMan::instance()->slotSetFolderPaused(alias(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This removes the csync File database
|
// This removes the csync File database
|
||||||
// This is needed to provide a clean startup again in case another
|
// This is needed to provide a clean startup again in case another
|
||||||
// local folder is synced to the same ownCloud.
|
// local folder is synced to the same ownCloud.
|
||||||
|
@ -617,6 +719,8 @@ void Folder::startSync(const QStringList &pathList)
|
||||||
|
|
||||||
connect( _engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
|
connect( _engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
|
||||||
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
|
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
|
||||||
|
connect( _engine.data(), SIGNAL(aboutToPropagate(const SyncFileItemVector&)),
|
||||||
|
this, SLOT(slotAboutToPropagate(const SyncFileItemVector&)), Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
|
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
|
||||||
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
|
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
|
||||||
|
@ -683,8 +787,11 @@ void Folder::slotCsyncUnavailable()
|
||||||
|
|
||||||
void Folder::slotSyncFinished()
|
void Folder::slotSyncFinished()
|
||||||
{
|
{
|
||||||
qDebug() << "-> CSync Finished slot with error " << _csyncError << "warn count" << _syncResult.warnCount();
|
if( _csyncError ) {
|
||||||
|
qDebug() << "-> SyncEngine finished with ERROR, warn count is" << _syncResult.warnCount();
|
||||||
|
} else {
|
||||||
|
qDebug() << "-> SyncEngine finished without problem.";
|
||||||
|
}
|
||||||
bubbleUpSyncResult();
|
bubbleUpSyncResult();
|
||||||
|
|
||||||
bool anotherSyncNeeded = false;
|
bool anotherSyncNeeded = false;
|
||||||
|
@ -695,6 +802,15 @@ void Folder::slotSyncFinished()
|
||||||
// _watcher->setEventsEnabledDelayed(2000);
|
// _watcher->setEventsEnabledDelayed(2000);
|
||||||
|
|
||||||
|
|
||||||
|
// This is for sync state calculation
|
||||||
|
_stateLastSyncItemsWithError = _stateLastSyncItemsWithErrorNew;
|
||||||
|
_stateLastSyncItemsWithErrorNew.clear();
|
||||||
|
_stateTaintedFolders.clear(); // heuristic: assume the sync had been done, new file watches needed to taint dirs
|
||||||
|
if (_csyncError || _csyncUnavail) {
|
||||||
|
// Taint the whole sync dir, we cannot give reliable state information
|
||||||
|
_stateTaintedFolders.insert(QLatin1String("/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_csyncError) {
|
if (_csyncError) {
|
||||||
_syncResult.setStatus(SyncResult::Error);
|
_syncResult.setStatus(SyncResult::Error);
|
||||||
|
@ -725,9 +841,9 @@ void Folder::slotSyncFinished()
|
||||||
_pollTimer.start();
|
_pollTimer.start();
|
||||||
_timeSinceLastSync.restart();
|
_timeSinceLastSync.restart();
|
||||||
} else {
|
} else {
|
||||||
// Another sync is required. We will make sure that the poll timer occurs soon enough
|
// Another sync is required. We will make sure that the poll timer occurs soon enough.
|
||||||
// and we clear the etag to force a sync
|
qDebug() << "another sync was requested by the finished sync";
|
||||||
_lastEtag.clear();
|
_forceSyncOnPollTimeout = true;
|
||||||
QTimer::singleShot(1000, this, SLOT(slotPollTimerTimeout() ));
|
QTimer::singleShot(1000, this, SLOT(slotPollTimerTimeout() ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,6 +877,10 @@ void Folder::slotTransmissionProgress(const Progress::Info &pi)
|
||||||
// a job is completed: count the errors and forward to the ProgressDispatcher
|
// a job is completed: count the errors and forward to the ProgressDispatcher
|
||||||
void Folder::slotJobCompleted(const SyncFileItem &item)
|
void Folder::slotJobCompleted(const SyncFileItem &item)
|
||||||
{
|
{
|
||||||
|
if (item.hasErrorStatus()) {
|
||||||
|
_stateLastSyncItemsWithError.insert(item._file);
|
||||||
|
}
|
||||||
|
|
||||||
if (Progress::isWarningKind(item._status)) {
|
if (Progress::isWarningKind(item._status)) {
|
||||||
// Count all error conditions.
|
// Count all error conditions.
|
||||||
_syncResult.setWarnCount(_syncResult.warnCount()+1);
|
_syncResult.setWarnCount(_syncResult.warnCount()+1);
|
||||||
|
@ -793,7 +913,8 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
|
||||||
if (*cancel) {
|
if (*cancel) {
|
||||||
wipe();
|
wipe();
|
||||||
// speed up next sync
|
// speed up next sync
|
||||||
_lastEtag = QString();
|
_lastEtag.clear();
|
||||||
|
_forceSyncOnPollTimeout = true;
|
||||||
QTimer::singleShot(50, this, SLOT(slotPollTimerTimeout()));
|
QTimer::singleShot(50, this, SLOT(slotPollTimerTimeout()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QSet>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
@ -34,15 +35,12 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <qelapsedtimer.h>
|
#include <qelapsedtimer.h>
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
|
||||||
class QThread;
|
class QThread;
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
class SyncEngine;
|
class SyncEngine;
|
||||||
|
|
||||||
class FolderWatcher;
|
|
||||||
|
|
||||||
class Folder : public QObject
|
class Folder : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -119,12 +117,12 @@ public:
|
||||||
|
|
||||||
// Used by the Socket API
|
// Used by the Socket API
|
||||||
SyncJournalDb *journalDb() { return &_journal; }
|
SyncJournalDb *journalDb() { return &_journal; }
|
||||||
CSYNC *csyncContext() { return _csync_ctx; }
|
|
||||||
|
|
||||||
QStringList selectiveSyncBlackList() { return _selectiveSyncBlackList; }
|
QStringList selectiveSyncBlackList() { return _selectiveSyncBlackList; }
|
||||||
void setSelectiveSyncBlackList(const QStringList &blackList)
|
void setSelectiveSyncBlackList(const QStringList &blackList)
|
||||||
{ _selectiveSyncBlackList = blackList; }
|
{ _selectiveSyncBlackList = blackList; }
|
||||||
|
|
||||||
|
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void syncStateChange();
|
void syncStateChange();
|
||||||
|
@ -138,7 +136,6 @@ public slots:
|
||||||
* terminate the current sync run
|
* terminate the current sync run
|
||||||
*/
|
*/
|
||||||
void slotTerminateSync();
|
void slotTerminateSync();
|
||||||
void slotTerminateAndPauseSync();
|
|
||||||
|
|
||||||
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
|
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
|
||||||
|
|
||||||
|
@ -171,10 +168,13 @@ private slots:
|
||||||
void etagRetreived(const QString &);
|
void etagRetreived(const QString &);
|
||||||
void slotNetworkUnavailable();
|
void slotNetworkUnavailable();
|
||||||
|
|
||||||
void slotThreadTreeWalkResult(const SyncFileItemVector& );
|
void slotAboutToPropagate(const SyncFileItemVector& );
|
||||||
|
void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done
|
||||||
|
|
||||||
void slotEmitFinishedDelayed();
|
void slotEmitFinishedDelayed();
|
||||||
|
|
||||||
|
void watcherSlot(QString);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool init();
|
bool init();
|
||||||
|
|
||||||
|
@ -203,6 +203,12 @@ private:
|
||||||
QTimer _pollTimer;
|
QTimer _pollTimer;
|
||||||
QString _lastEtag;
|
QString _lastEtag;
|
||||||
QElapsedTimer _timeSinceLastSync;
|
QElapsedTimer _timeSinceLastSync;
|
||||||
|
bool _forceSyncOnPollTimeout;
|
||||||
|
|
||||||
|
// For the SocketAPI folder states
|
||||||
|
QSet<QString> _stateLastSyncItemsWithErrorNew; // gets moved to _stateLastSyncItemsWithError at end of sync
|
||||||
|
QSet<QString> _stateLastSyncItemsWithError;
|
||||||
|
QSet<QString> _stateTaintedFolders;
|
||||||
|
|
||||||
SyncJournalDb _journal;
|
SyncJournalDb _journal;
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,9 @@ void FolderMan::registerFolderMonitor( Folder *folder )
|
||||||
connect(fw, SIGNAL(folderChanged(QString)), _folderWatcherSignalMapper, SLOT(map()));
|
connect(fw, SIGNAL(folderChanged(QString)), _folderWatcherSignalMapper, SLOT(map()));
|
||||||
_folderWatcherSignalMapper->setMapping(fw, folder->alias());
|
_folderWatcherSignalMapper->setMapping(fw, folder->alias());
|
||||||
_folderWatchers.insert(folder->alias(), fw);
|
_folderWatchers.insert(folder->alias(), fw);
|
||||||
|
|
||||||
|
// This is at the moment only for the behaviour of the SocketApi.
|
||||||
|
connect(fw, SIGNAL(folderChanged(QString)), folder, SLOT(watcherSlot(QString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// register the folder with the socket API
|
// register the folder with the socket API
|
||||||
|
@ -441,6 +444,24 @@ void FolderMan::slotScheduleSync( const QString& alias )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The folder watcher fires a lot of bogus notifications during
|
||||||
|
// a sync operation, both for actual user files and the database
|
||||||
|
// and log. Never enqueue a folder for sync while it is syncing.
|
||||||
|
// We lose some genuine sync requests that way, but that can't be
|
||||||
|
// helped.
|
||||||
|
// ^^ FIXME: Note that this is not the case on OS X
|
||||||
|
if( _currentSyncFolder == alias ) {
|
||||||
|
qDebug() << "folder " << alias << " is currently syncing. NOT scheduling.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _socketApi ) {
|
||||||
|
// We want the SocketAPI to already now update so that it can show the EVAL icon
|
||||||
|
// for files/folders. Only do this when not syncing, else we might get a lot
|
||||||
|
// of those notifications.
|
||||||
|
_socketApi->slotUpdateFolderView(alias);
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "Schedule folder " << alias << " to sync!";
|
qDebug() << "Schedule folder " << alias << " to sync!";
|
||||||
|
|
||||||
if( ! _scheduleQueue.contains(alias) ) {
|
if( ! _scheduleQueue.contains(alias) ) {
|
||||||
|
@ -451,7 +472,9 @@ void FolderMan::slotScheduleSync( const QString& alias )
|
||||||
f->prepareToSync();
|
f->prepareToSync();
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Folder is not enabled, not scheduled!";
|
qDebug() << "Folder is not enabled, not scheduled!";
|
||||||
_socketApi->slotUpdateFolderView(f->alias());
|
if( _socketApi ) {
|
||||||
|
_socketApi->slotUpdateFolderView(f->alias());
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_scheduleQueue.enqueue(alias);
|
_scheduleQueue.enqueue(alias);
|
||||||
|
@ -513,8 +536,10 @@ void FolderMan::slotStartScheduledFolderSync()
|
||||||
|
|
||||||
// reread the excludes of the socket api
|
// reread the excludes of the socket api
|
||||||
// FIXME: the excludes need rework.
|
// FIXME: the excludes need rework.
|
||||||
_socketApi->slotClearExcludesList();
|
if( _socketApi ) {
|
||||||
_socketApi->slotReadExcludes();
|
_socketApi->slotClearExcludesList();
|
||||||
|
_socketApi->slotReadExcludes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -527,6 +552,8 @@ void FolderMan::slotFolderSyncStarted( )
|
||||||
/*
|
/*
|
||||||
* a folder indicates that its syncing is finished.
|
* a folder indicates that its syncing is finished.
|
||||||
* Start the next sync after the system had some milliseconds to breath.
|
* Start the next sync after the system had some milliseconds to breath.
|
||||||
|
* This delay is particularly useful to avoid late file change notifications
|
||||||
|
* (that we caused ourselves by syncing) from triggering another spurious sync.
|
||||||
*/
|
*/
|
||||||
void FolderMan::slotFolderSyncFinished( const SyncResult& )
|
void FolderMan::slotFolderSyncFinished( const SyncResult& )
|
||||||
{
|
{
|
||||||
|
@ -561,11 +588,11 @@ Folder *FolderMan::folderForPath(const QString &path)
|
||||||
const QString folderPath = QDir::cleanPath(folder->path())+QLatin1Char('/');
|
const QString folderPath = QDir::cleanPath(folder->path())+QLatin1Char('/');
|
||||||
|
|
||||||
if(absolutePath.startsWith(folderPath)) {
|
if(absolutePath.startsWith(folderPath)) {
|
||||||
qDebug() << "found folder: " << folder->path() << " for " << absolutePath;
|
//qDebug() << "found folder: " << folder->path() << " for " << absolutePath;
|
||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
qDebug() << "ERROR: could not find folder for " << absolutePath;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ bool FolderWatcher::pathIsIgnored( const QString& path )
|
||||||
|
|
||||||
QFileInfo fInfo(path);
|
QFileInfo fInfo(path);
|
||||||
if( fInfo.isHidden() ) {
|
if( fInfo.isHidden() ) {
|
||||||
qDebug() << "* Discarded as is hidden!";
|
qDebug() << "* Discarded as is hidden!" << fInfo.filePath();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,6 +232,9 @@ FolderWizardRemotePath::FolderWizardRemotePath()
|
||||||
_ui.setupUi(this);
|
_ui.setupUi(this);
|
||||||
_ui.warnFrame->hide();
|
_ui.warnFrame->hide();
|
||||||
|
|
||||||
|
_ui.folderTreeWidget->setSortingEnabled(true);
|
||||||
|
_ui.folderTreeWidget->sortByColumn(0, Qt::AscendingOrder);
|
||||||
|
|
||||||
connect(_ui.addFolderButton, SIGNAL(clicked()), SLOT(slotAddRemoteFolder()));
|
connect(_ui.addFolderButton, SIGNAL(clicked()), SLOT(slotAddRemoteFolder()));
|
||||||
connect(_ui.refreshButton, SIGNAL(clicked()), SLOT(slotRefreshFolders()));
|
connect(_ui.refreshButton, SIGNAL(clicked()), SLOT(slotRefreshFolders()));
|
||||||
connect(_ui.folderTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
connect(_ui.folderTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
||||||
|
|
|
@ -82,6 +82,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
// if the application is already running, notify it.
|
// if the application is already running, notify it.
|
||||||
if( app.isRunning() ) {
|
if( app.isRunning() ) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Already running, exiting...";
|
||||||
QStringList args = app.arguments();
|
QStringList args = app.arguments();
|
||||||
if ( args.size() > 1 && ! app.giveHelp() ) {
|
if ( args.size() > 1 && ! app.giveHelp() ) {
|
||||||
QString msg = args.join( QLatin1String("|") );
|
QString msg = args.join( QLatin1String("|") );
|
||||||
|
|
|
@ -50,7 +50,6 @@ ownCloudGui::ownCloudGui(Application *parent) :
|
||||||
_settingsDialog(new SettingsDialog(this)),
|
_settingsDialog(new SettingsDialog(this)),
|
||||||
#endif
|
#endif
|
||||||
_logBrowser(0),
|
_logBrowser(0),
|
||||||
_contextMenu(0),
|
|
||||||
_recentActionsMenu(0),
|
_recentActionsMenu(0),
|
||||||
_folderOpenActionMapper(new QSignalMapper(this)),
|
_folderOpenActionMapper(new QSignalMapper(this)),
|
||||||
_recentItemsMapper(new QSignalMapper(this)),
|
_recentItemsMapper(new QSignalMapper(this)),
|
||||||
|
@ -303,11 +302,11 @@ void ownCloudGui::setupContextMenu()
|
||||||
_recentActionsMenu->addAction(tr("None."));
|
_recentActionsMenu->addAction(tr("None."));
|
||||||
_recentActionsMenu->addAction(_actionRecent);
|
_recentActionsMenu->addAction(_actionRecent);
|
||||||
} else {
|
} else {
|
||||||
_contextMenu = new QMenu(_contextMenu);
|
_contextMenu.reset(new QMenu());
|
||||||
_recentActionsMenu = new QMenu(tr("Recent Changes"));
|
_recentActionsMenu = new QMenu(tr("Recent Changes"), _contextMenu.data());
|
||||||
// this must be called only once after creating the context menu, or
|
// this must be called only once after creating the context menu, or
|
||||||
// it will trigger a bug in Ubuntu's SNI bridge patch (11.10, 12.04).
|
// it will trigger a bug in Ubuntu's SNI bridge patch (11.10, 12.04).
|
||||||
_tray->setContextMenu(_contextMenu);
|
_tray->setContextMenu(_contextMenu.data());
|
||||||
}
|
}
|
||||||
_contextMenu->setTitle(Theme::instance()->appNameGUI() );
|
_contextMenu->setTitle(Theme::instance()->appNameGUI() );
|
||||||
_contextMenu->addAction(_actionOpenoC);
|
_contextMenu->addAction(_actionOpenoC);
|
||||||
|
@ -464,7 +463,7 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const Progress::Info
|
||||||
Q_UNUSED(folder);
|
Q_UNUSED(folder);
|
||||||
|
|
||||||
if (!progress._currentDiscoveredFolder.isEmpty()) {
|
if (!progress._currentDiscoveredFolder.isEmpty()) {
|
||||||
_actionStatus->setText( tr("Discovering %1")
|
_actionStatus->setText( tr("Discovering '%1'")
|
||||||
.arg( progress._currentDiscoveredFolder ));
|
.arg( progress._currentDiscoveredFolder ));
|
||||||
} else if (progress._totalSize == 0 ) {
|
} else if (progress._totalSize == 0 ) {
|
||||||
quint64 currentFile = progress._completedFileCount + progress._currentItems.count();
|
quint64 currentFile = progress._completedFileCount + progress._currentItems.count();
|
||||||
|
|
|
@ -85,7 +85,7 @@ private:
|
||||||
#endif
|
#endif
|
||||||
QPointer<LogBrowser>_logBrowser;
|
QPointer<LogBrowser>_logBrowser;
|
||||||
// tray's menu
|
// tray's menu
|
||||||
QMenu *_contextMenu;
|
QScopedPointer<QMenu> _contextMenu;
|
||||||
QMenu *_recentActionsMenu;
|
QMenu *_recentActionsMenu;
|
||||||
|
|
||||||
QAction *_actionLogin;
|
QAction *_actionLogin;
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
|
|
||||||
#include "ui_protocolwidget.h"
|
#include "ui_protocolwidget.h"
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
ProtocolWidget::ProtocolWidget(QWidget *parent) :
|
ProtocolWidget::ProtocolWidget(QWidget *parent) :
|
||||||
|
@ -183,6 +185,15 @@ void ProtocolWidget::slotOpenFile( QTreeWidgetItem *item, int )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ProtocolWidget::fixupFilename( const QString& name )
|
||||||
|
{
|
||||||
|
if( Utility::isMac() ) {
|
||||||
|
QString n(name);
|
||||||
|
return n.replace(QChar(':'), QChar('/'));
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& folder, const SyncFileItem& item)
|
QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& folder, const SyncFileItem& item)
|
||||||
{
|
{
|
||||||
QStringList columns;
|
QStringList columns;
|
||||||
|
@ -193,7 +204,7 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
|
||||||
QString message;
|
QString message;
|
||||||
|
|
||||||
columns << timeStr;
|
columns << timeStr;
|
||||||
columns << item._file;
|
columns << fixupFilename(item._file);
|
||||||
columns << folder;
|
columns << folder;
|
||||||
if (Progress::isWarningKind(item._status)) {
|
if (Progress::isWarningKind(item._status)) {
|
||||||
message= item._errorString;
|
message= item._errorString;
|
||||||
|
@ -205,7 +216,13 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
message = Progress::asResultString(item);
|
// if the error string is set, it's prefered because it is a usefull user message.
|
||||||
|
// at least should be...
|
||||||
|
if(item._errorString.isEmpty()) {
|
||||||
|
message = Progress::asResultString(item);
|
||||||
|
} else {
|
||||||
|
message = item._errorString;
|
||||||
|
}
|
||||||
columns << message;
|
columns << message;
|
||||||
if (Progress::isSizeDependent(item._instruction)) {
|
if (Progress::isSizeDependent(item._instruction)) {
|
||||||
columns << Utility::octetsToString( item._size );
|
columns << Utility::octetsToString( item._size );
|
||||||
|
@ -247,7 +264,7 @@ void ProtocolWidget::computeResyncButtonEnabled()
|
||||||
|
|
||||||
void ProtocolWidget::slotProgressInfo( const QString& folder, const Progress::Info& progress )
|
void ProtocolWidget::slotProgressInfo( const QString& folder, const Progress::Info& progress )
|
||||||
{
|
{
|
||||||
if( progress._completedFileCount == 0 ) {
|
if( progress._completedFileCount == ULLONG_MAX ) {
|
||||||
// The sync is restarting, clean the old items
|
// The sync is restarting, clean the old items
|
||||||
cleanIgnoreItems(folder);
|
cleanIgnoreItems(folder);
|
||||||
computeResyncButtonEnabled();
|
computeResyncButtonEnabled();
|
||||||
|
|
|
@ -56,6 +56,8 @@ private:
|
||||||
void setSyncResultStatus(const SyncResult& result );
|
void setSyncResultStatus(const SyncResult& result );
|
||||||
void cleanIgnoreItems( const QString& folder );
|
void cleanIgnoreItems( const QString& folder );
|
||||||
void computeResyncButtonEnabled();
|
void computeResyncButtonEnabled();
|
||||||
|
QString fixupFilename( const QString& name );
|
||||||
|
|
||||||
|
|
||||||
QTreeWidgetItem* createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item );
|
QTreeWidgetItem* createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item );
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,13 @@
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
SelectiveSyncTreeView::SelectiveSyncTreeView(Account *account, QWidget* parent)
|
SelectiveSyncTreeView::SelectiveSyncTreeView(Account *account, QWidget* parent)
|
||||||
: QTreeWidget(parent), _account(account)
|
: QTreeWidget(parent), _inserting(false), _account(account)
|
||||||
{
|
{
|
||||||
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||||
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
||||||
header()->hide();
|
header()->hide();
|
||||||
|
setSortingEnabled(true);
|
||||||
|
sortByColumn(0, Qt::AscendingOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectiveSyncTreeView::refreshFolders()
|
void SelectiveSyncTreeView::refreshFolders()
|
||||||
|
@ -260,7 +262,7 @@ void SelectiveSyncDialog::init(Account *account)
|
||||||
{
|
{
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
_treeView = new SelectiveSyncTreeView(account, this);
|
_treeView = new SelectiveSyncTreeView(account, this);
|
||||||
layout->addWidget(new QLabel(tr("Only checked folders will sync to this computer")));
|
layout->addWidget(new QLabel(tr("Unchecked folders will not be sync to this computer")));
|
||||||
layout->addWidget(_treeView);
|
layout->addWidget(_treeView);
|
||||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal);
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal);
|
||||||
QPushButton *button;
|
QPushButton *button;
|
||||||
|
@ -274,6 +276,7 @@ void SelectiveSyncDialog::init(Account *account)
|
||||||
void SelectiveSyncDialog::accept()
|
void SelectiveSyncDialog::accept()
|
||||||
{
|
{
|
||||||
if (_folder) {
|
if (_folder) {
|
||||||
|
auto oldBlackListSet = _folder->selectiveSyncBlackList().toSet();
|
||||||
QStringList blackList = _treeView->createBlackList();
|
QStringList blackList = _treeView->createBlackList();
|
||||||
_folder->setSelectiveSyncBlackList(blackList);
|
_folder->setSelectiveSyncBlackList(blackList);
|
||||||
|
|
||||||
|
@ -281,11 +284,19 @@ void SelectiveSyncDialog::accept()
|
||||||
QSettings settings(_folder->configFile(), QSettings::IniFormat);
|
QSettings settings(_folder->configFile(), QSettings::IniFormat);
|
||||||
settings.beginGroup(FolderMan::escapeAlias(_folder->alias()));
|
settings.beginGroup(FolderMan::escapeAlias(_folder->alias()));
|
||||||
settings.setValue("blackList", blackList);
|
settings.setValue("blackList", blackList);
|
||||||
|
|
||||||
FolderMan *folderMan = FolderMan::instance();
|
FolderMan *folderMan = FolderMan::instance();
|
||||||
if (_folder->isBusy()) {
|
if (_folder->isBusy()) {
|
||||||
_folder->slotTerminateAndPauseSync();
|
_folder->slotTerminateSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//The part that changed should not be read from the DB on next sync because there might be new folders
|
||||||
|
// (the ones that are no longer in the blacklist)
|
||||||
|
auto blackListSet = blackList.toSet();
|
||||||
|
auto changes = (oldBlackListSet - blackListSet) + (blackListSet - oldBlackListSet);
|
||||||
|
foreach(const auto &it, changes) {
|
||||||
|
_folder->journalDb()->avoidReadFromDbOnNextSync(it);
|
||||||
|
}
|
||||||
|
|
||||||
folderMan->slotScheduleSync(_folder->alias());
|
folderMan->slotScheduleSync(_folder->alias());
|
||||||
}
|
}
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
|
|
|
@ -46,7 +46,7 @@ private:
|
||||||
QString _folderPath;
|
QString _folderPath;
|
||||||
QString _rootName;
|
QString _rootName;
|
||||||
QStringList _oldBlackList;
|
QStringList _oldBlackList;
|
||||||
bool _inserting = false; // set to true when we are inserting new items on the list
|
bool _inserting; // set to true when we are inserting new items on the list
|
||||||
Account *_account;
|
Account *_account;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,11 @@ SettingsDialogMac::SettingsDialogMac(ownCloudGui *gui, QWidget *parent)
|
||||||
closeWindowAction->setShortcut(QKeySequence("Ctrl+W"));
|
closeWindowAction->setShortcut(QKeySequence("Ctrl+W"));
|
||||||
connect(closeWindowAction, SIGNAL(triggered()), SLOT(close()));
|
connect(closeWindowAction, SIGNAL(triggered()), SLOT(close()));
|
||||||
addAction(closeWindowAction);
|
addAction(closeWindowAction);
|
||||||
|
// People perceive this as a Window, so also make Ctrl+H work
|
||||||
|
QAction *hideWindowAction = new QAction(this);
|
||||||
|
hideWindowAction->setShortcut(QKeySequence("Ctrl+H"));
|
||||||
|
connect(hideWindowAction, SIGNAL(triggered()), SLOT(hide()));
|
||||||
|
addAction(hideWindowAction);
|
||||||
|
|
||||||
setObjectName("SettingsMac"); // required as group for saveGeometry call
|
setObjectName("SettingsMac"); // required as group for saveGeometry call
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,15 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// This is the version that is returned when the client asks for the VERSION.
|
// This is the version that is returned when the client asks for the VERSION.
|
||||||
// The first number should be changed if there is an incompatible change that breaks old clients.
|
// The first number should be changed if there is an incompatible change that breaks old clients.
|
||||||
|
@ -54,147 +63,56 @@ CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path
|
||||||
int csync_exclude_load(const char *fname, c_strlist_t **list);
|
int csync_exclude_load(const char *fname, c_strlist_t **list);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
const int PORT = 34001;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
#define DEBUG qDebug() << "SocketApi: "
|
#define DEBUG qDebug() << "SocketApi: "
|
||||||
|
|
||||||
namespace SocketApiHelper {
|
|
||||||
|
|
||||||
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief recursiveFolderStatus
|
|
||||||
* @param fileName - the relative file name to examine
|
|
||||||
* @return the resulting status
|
|
||||||
*
|
|
||||||
* The resulting status can only be either SYNC which means all files
|
|
||||||
* are in sync, ERROR if an error occured, or EVAL if something needs
|
|
||||||
* to be synced underneath this dir.
|
|
||||||
*/
|
|
||||||
// compute the file status of a directory recursively. It returns either
|
|
||||||
// "all in sync" or "needs update" or "error", no more details.
|
|
||||||
SyncFileStatus recursiveFolderStatus(Folder *folder, const QString& fileName, c_strlist_t *excludes )
|
|
||||||
{
|
|
||||||
QDir dir(folder->path() + fileName);
|
|
||||||
|
|
||||||
const QStringList dirEntries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot );
|
|
||||||
|
|
||||||
SyncFileStatus result(SyncFileStatus::STATUS_SYNC);
|
|
||||||
|
|
||||||
foreach( const QString entry, dirEntries ) {
|
|
||||||
QString normalizedFile = QString(fileName + QLatin1Char('/') + entry).normalized(QString::NormalizationForm_C);
|
|
||||||
QFileInfo fi(entry);
|
|
||||||
SyncFileStatus sfs;
|
|
||||||
|
|
||||||
if( fi.isDir() ) {
|
|
||||||
sfs = recursiveFolderStatus(folder, normalizedFile, excludes );
|
|
||||||
} else {
|
|
||||||
QString fs( normalizedFile );
|
|
||||||
if( fileName.isEmpty() ) {
|
|
||||||
// toplevel, no slash etc. needed.
|
|
||||||
fs = entry.normalized(QString::NormalizationForm_C);
|
|
||||||
}
|
|
||||||
sfs = fileStatus(folder, fs, excludes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( sfs.tag() == SyncFileStatus::STATUS_STAT_ERROR || sfs.tag() == SyncFileStatus::STATUS_ERROR ) {
|
|
||||||
return SyncFileStatus::STATUS_ERROR;
|
|
||||||
} else if( sfs.tag() == SyncFileStatus::STATUS_EVAL || sfs.tag() == SyncFileStatus::STATUS_NEW) {
|
|
||||||
result.set(SyncFileStatus::STATUS_EVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get status about a single file.
|
|
||||||
*/
|
|
||||||
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes )
|
|
||||||
{
|
|
||||||
// FIXME: Find a way for STATUS_ERROR
|
|
||||||
|
|
||||||
QString file = folder->path();
|
|
||||||
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
|
|
||||||
|
|
||||||
bool isSyncRootFolder = true;
|
|
||||||
if( fileName != QLatin1String("/") && !fileName.isEmpty() ) {
|
|
||||||
file = folder->path() + fileName;
|
|
||||||
isSyncRootFolder = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( fileName.endsWith(QLatin1Char('/')) ) {
|
|
||||||
fileName.truncate(fileName.length()-1);
|
|
||||||
qDebug() << "Removed trailing slash: " << fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFileInfo fi(file);
|
|
||||||
|
|
||||||
if( !fi.exists() ) {
|
|
||||||
qDebug() << "OO File " << file << " is not existing";
|
|
||||||
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// file is ignored?
|
|
||||||
if( fi.isSymLink() ) {
|
|
||||||
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
|
||||||
}
|
|
||||||
int type = CSYNC_FTW_TYPE_FILE;
|
|
||||||
if( fi.isDir() ) {
|
|
||||||
type = CSYNC_FTW_TYPE_DIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// '\' is ignored, so convert to unix path before passing the path in.
|
|
||||||
QString unixFileName = QDir::fromNativeSeparators(fileName);
|
|
||||||
|
|
||||||
CSYNC_EXCLUDE_TYPE excl = csync_excluded_no_ctx(excludes, unixFileName.toUtf8(), type);
|
|
||||||
if( excl != CSYNC_NOT_EXCLUDED ) {
|
|
||||||
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Problem: for the sync dir itself we do not have a record in the sync journal
|
|
||||||
// so the next check must not be used for the sync root folder.
|
|
||||||
SyncJournalFileRecord rec = folder->journalDb()->getFileRecord(unixFileName);
|
|
||||||
if( !isSyncRootFolder && !rec.isValid() ) {
|
|
||||||
return SyncFileStatus(SyncFileStatus::STATUS_NEW);
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
|
|
||||||
if( type == CSYNC_FTW_TYPE_DIR ) {
|
|
||||||
// compute recursive status of the directory
|
|
||||||
status = recursiveFolderStatus( folder, fileName, excludes );
|
|
||||||
} else if( FileSystem::getModTime(fi.absoluteFilePath()) != Utility::qDateTimeToTime_t(rec._modtime) ) {
|
|
||||||
// file was locally modified.
|
|
||||||
status.set(SyncFileStatus::STATUS_EVAL);
|
|
||||||
} else {
|
|
||||||
status.set(SyncFileStatus::STATUS_SYNC);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rec._remotePerm.contains("S")) {
|
|
||||||
// FIXME! that should be an additional flag
|
|
||||||
status.setSharedWithMe(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketApi::SocketApi(QObject* parent)
|
SocketApi::SocketApi(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, _localServer(new QTcpServer(this))
|
|
||||||
, _excludes(0)
|
, _excludes(0)
|
||||||
{
|
{
|
||||||
// setup socket
|
QString socketPath;
|
||||||
DEBUG << "Establishing SocketAPI server at" << PORT;
|
|
||||||
if (!_localServer->listen(QHostAddress::LocalHost, PORT)) {
|
if (Utility::isWindows()) {
|
||||||
DEBUG << "Failed to bind to port" << PORT;
|
socketPath = QLatin1String("\\\\.\\pipe\\")
|
||||||
|
+ Theme::instance()->appName();
|
||||||
|
} else if (Utility::isMac()) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
// Always using Qt5 on OS X
|
||||||
|
QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
|
||||||
|
socketPath = runtimeDir + "/SyncStateHelper/" + Theme::instance()->appName() + ".socket";
|
||||||
|
// We use the generic SyncStateHelper name on OS X since the different branded clients
|
||||||
|
// should unfortunately not mention that they are ownCloud :-)
|
||||||
|
#endif
|
||||||
|
} else if( Utility::isLinux() ) {
|
||||||
|
QString runtimeDir;
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||||
|
#else
|
||||||
|
runtimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
|
||||||
|
#endif
|
||||||
|
socketPath = runtimeDir + "/" + Theme::instance()->appName() + "/socket";
|
||||||
|
} else {
|
||||||
|
DEBUG << "An unexpected system detected";
|
||||||
}
|
}
|
||||||
connect(_localServer, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
|
|
||||||
|
QLocalServer::removeServer(socketPath);
|
||||||
|
QFileInfo info(socketPath);
|
||||||
|
if (!info.dir().exists()) {
|
||||||
|
bool result = info.dir().mkpath(".");
|
||||||
|
DEBUG << "creating" << info.dir().path() << result;
|
||||||
|
if( result ) {
|
||||||
|
QFile::setPermissions(socketPath,
|
||||||
|
QFile::Permissions(QFile::ReadOwner+QFile::WriteOwner+QFile::ExeOwner));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!_localServer.listen(socketPath)) {
|
||||||
|
DEBUG << "can't start server" << socketPath;
|
||||||
|
} else {
|
||||||
|
DEBUG << "server started, listening at " << socketPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(&_localServer, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
|
||||||
|
|
||||||
// folder watcher
|
// folder watcher
|
||||||
connect(FolderMan::instance(), SIGNAL(folderSyncStateChange(QString)), this, SLOT(slotUpdateFolderView(QString)));
|
connect(FolderMan::instance(), SIGNAL(folderSyncStateChange(QString)), this, SLOT(slotUpdateFolderView(QString)));
|
||||||
|
@ -207,7 +125,7 @@ SocketApi::SocketApi(QObject* parent)
|
||||||
SocketApi::~SocketApi()
|
SocketApi::~SocketApi()
|
||||||
{
|
{
|
||||||
DEBUG << "dtor";
|
DEBUG << "dtor";
|
||||||
_localServer->close();
|
_localServer.close();
|
||||||
slotClearExcludesList();
|
slotClearExcludesList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +152,7 @@ void SocketApi::slotReadExcludes()
|
||||||
|
|
||||||
void SocketApi::slotNewConnection()
|
void SocketApi::slotNewConnection()
|
||||||
{
|
{
|
||||||
QTcpSocket* socket = _localServer->nextPendingConnection();
|
SocketType* socket = _localServer.nextPendingConnection();
|
||||||
|
|
||||||
if( ! socket ) {
|
if( ! socket ) {
|
||||||
return;
|
return;
|
||||||
|
@ -266,14 +184,14 @@ void SocketApi::onLostConnection()
|
||||||
{
|
{
|
||||||
DEBUG << "Lost connection " << sender();
|
DEBUG << "Lost connection " << sender();
|
||||||
|
|
||||||
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
|
SocketType* socket = qobject_cast<SocketType*>(sender());
|
||||||
_listeners.removeAll(socket);
|
_listeners.removeAll(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SocketApi::slotReadSocket()
|
void SocketApi::slotReadSocket()
|
||||||
{
|
{
|
||||||
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
|
SocketType* socket = qobject_cast<SocketType*>(sender());
|
||||||
Q_ASSERT(socket);
|
Q_ASSERT(socket);
|
||||||
|
|
||||||
while(socket->canReadLine()) {
|
while(socket->canReadLine()) {
|
||||||
|
@ -281,12 +199,12 @@ void SocketApi::slotReadSocket()
|
||||||
QString command = line.split(":").first();
|
QString command = line.split(":").first();
|
||||||
QString function = QString(QLatin1String("command_")).append(command);
|
QString function = QString(QLatin1String("command_")).append(command);
|
||||||
|
|
||||||
QString functionWithArguments = function + QLatin1String("(QString,QTcpSocket*)");
|
QString functionWithArguments = function + QLatin1String("(QString,SocketType*)");
|
||||||
int indexOfMethod = this->metaObject()->indexOfMethod(functionWithArguments.toAscii());
|
int indexOfMethod = this->metaObject()->indexOfMethod(functionWithArguments.toAscii());
|
||||||
|
|
||||||
QString argument = line.remove(0, command.length()+1).trimmed();
|
QString argument = line.remove(0, command.length()+1).trimmed();
|
||||||
if(indexOfMethod != -1) {
|
if(indexOfMethod != -1) {
|
||||||
QMetaObject::invokeMethod(this, function.toAscii(), Q_ARG(QString, argument), Q_ARG(QTcpSocket*, socket));
|
QMetaObject::invokeMethod(this, function.toAscii(), Q_ARG(QString, argument), Q_ARG(SocketType*, socket));
|
||||||
} else {
|
} else {
|
||||||
DEBUG << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
|
DEBUG << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
|
||||||
}
|
}
|
||||||
|
@ -306,11 +224,30 @@ void SocketApi::slotUnregisterPath( const QString& alias )
|
||||||
Folder *f = FolderMan::instance()->folder(alias);
|
Folder *f = FolderMan::instance()->folder(alias);
|
||||||
if (f) {
|
if (f) {
|
||||||
broadcastMessage(QLatin1String("UNREGISTER_PATH"), f->path(), QString::null, true );
|
broadcastMessage(QLatin1String("UNREGISTER_PATH"), f->path(), QString::null, true );
|
||||||
|
|
||||||
|
if( _dbQueries.contains(f)) {
|
||||||
|
SqlQuery *h = _dbQueries[f];
|
||||||
|
if( h ) {
|
||||||
|
h->finish();
|
||||||
|
}
|
||||||
|
_dbQueries.remove(f);
|
||||||
|
}
|
||||||
|
if( _openDbs.contains(f) ) {
|
||||||
|
SqlDatabase *db = _openDbs[f];
|
||||||
|
if( db ) {
|
||||||
|
db->close();
|
||||||
|
}
|
||||||
|
_openDbs.remove(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::slotUpdateFolderView(const QString& alias)
|
void SocketApi::slotUpdateFolderView(const QString& alias)
|
||||||
{
|
{
|
||||||
|
if (_listeners.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Folder *f = FolderMan::instance()->folder(alias);
|
Folder *f = FolderMan::instance()->folder(alias);
|
||||||
if (f) {
|
if (f) {
|
||||||
// do only send UPDATE_VIEW for a couple of status
|
// do only send UPDATE_VIEW for a couple of status
|
||||||
|
@ -320,17 +257,23 @@ void SocketApi::slotUpdateFolderView(const QString& alias)
|
||||||
f->syncResult().status() == SyncResult::Problem ||
|
f->syncResult().status() == SyncResult::Problem ||
|
||||||
f->syncResult().status() == SyncResult::Error ||
|
f->syncResult().status() == SyncResult::Error ||
|
||||||
f->syncResult().status() == SyncResult::SetupError ) {
|
f->syncResult().status() == SyncResult::SetupError ) {
|
||||||
if( Utility::isWindows() ) {
|
|
||||||
Utility::winShellChangeNotify( f->path() );
|
broadcastMessage(QLatin1String("STATUS"), f->path() ,
|
||||||
} else {
|
this->fileStatus(f, "", _excludes).toSocketAPIString());
|
||||||
broadcastMessage(QLatin1String("UPDATE_VIEW"), f->path() );
|
|
||||||
}
|
broadcastMessage(QLatin1String("UPDATE_VIEW"), f->path() );
|
||||||
|
} else {
|
||||||
|
qDebug() << "Not sending UPDATE_VIEW for" << alias << "because status() is" << f->syncResult().status();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::slotJobCompleted(const QString &folder, const SyncFileItem &item)
|
void SocketApi::slotJobCompleted(const QString &folder, const SyncFileItem &item)
|
||||||
{
|
{
|
||||||
|
if (_listeners.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Folder *f = FolderMan::instance()->folder(folder);
|
Folder *f = FolderMan::instance()->folder(folder);
|
||||||
if (!f) {
|
if (!f) {
|
||||||
return;
|
return;
|
||||||
|
@ -347,6 +290,10 @@ void SocketApi::slotJobCompleted(const QString &folder, const SyncFileItem &item
|
||||||
|
|
||||||
void SocketApi::slotSyncItemDiscovered(const QString &folder, const SyncFileItem &item)
|
void SocketApi::slotSyncItemDiscovered(const QString &folder, const SyncFileItem &item)
|
||||||
{
|
{
|
||||||
|
if (_listeners.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (item._instruction == CSYNC_INSTRUCTION_NONE) {
|
if (item._instruction == CSYNC_INSTRUCTION_NONE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -364,18 +311,20 @@ void SocketApi::slotSyncItemDiscovered(const QString &folder, const SyncFileItem
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SocketApi::sendMessage(QTcpSocket *socket, const QString& message, bool doWait)
|
void SocketApi::sendMessage(SocketType *socket, const QString& message, bool doWait)
|
||||||
{
|
{
|
||||||
DEBUG << "Sending message: " << message;
|
DEBUG << "Sending message: " << message;
|
||||||
QString localMessage = message;
|
QString localMessage = message;
|
||||||
if( ! localMessage.endsWith(QLatin1Char('\n'))) {
|
if( ! localMessage.endsWith(QLatin1Char('\n'))) {
|
||||||
localMessage.append(QLatin1Char('\n'));
|
localMessage.append(QLatin1Char('\n'));
|
||||||
}
|
}
|
||||||
qint64 sent = socket->write(localMessage.toUtf8());
|
|
||||||
|
QByteArray bytesToSend = localMessage.toUtf8();
|
||||||
|
qint64 sent = socket->write(bytesToSend);
|
||||||
if( doWait ) {
|
if( doWait ) {
|
||||||
socket->waitForBytesWritten(1000);
|
socket->waitForBytesWritten(1000);
|
||||||
}
|
}
|
||||||
if( sent != localMessage.toUtf8().length() ) {
|
if( sent != bytesToSend.length() ) {
|
||||||
qDebug() << "WARN: Could not send all data on socket for " << localMessage;
|
qDebug() << "WARN: Could not send all data on socket for " << localMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,13 +343,14 @@ void SocketApi::broadcastMessage( const QString& verb, const QString& path, cons
|
||||||
msg.append(QDir::toNativeSeparators(path));
|
msg.append(QDir::toNativeSeparators(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << msg;
|
// sendMessage already has a debug output
|
||||||
foreach(QTcpSocket *socket, _listeners) {
|
//DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << msg;
|
||||||
|
foreach(SocketType *socket, _listeners) {
|
||||||
sendMessage(socket, msg, doWait);
|
sendMessage(socket, msg, doWait);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, QTcpSocket* socket)
|
void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, SocketType* socket)
|
||||||
{
|
{
|
||||||
// This command is the same as RETRIEVE_FILE_STATUS
|
// This command is the same as RETRIEVE_FILE_STATUS
|
||||||
|
|
||||||
|
@ -408,7 +358,7 @@ void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, QTcpSock
|
||||||
command_RETRIEVE_FILE_STATUS(argument, socket);
|
command_RETRIEVE_FILE_STATUS(argument, socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QTcpSocket* socket)
|
void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, SocketType* socket)
|
||||||
{
|
{
|
||||||
if( !socket ) {
|
if( !socket ) {
|
||||||
qDebug() << "No valid socket object.";
|
qDebug() << "No valid socket object.";
|
||||||
|
@ -426,7 +376,7 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QTcpSocket
|
||||||
statusString = QLatin1String("NOP");
|
statusString = QLatin1String("NOP");
|
||||||
} else {
|
} else {
|
||||||
const QString file = argument.mid(syncFolder->path().length());
|
const QString file = argument.mid(syncFolder->path().length());
|
||||||
SyncFileStatus fileStatus = SocketApiHelper::fileStatus(syncFolder, file, _excludes);
|
SyncFileStatus fileStatus = this->fileStatus(syncFolder, file, _excludes);
|
||||||
|
|
||||||
statusString = fileStatus.toSocketAPIString();
|
statusString = fileStatus.toSocketAPIString();
|
||||||
}
|
}
|
||||||
|
@ -436,10 +386,186 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QTcpSocket
|
||||||
sendMessage(socket, message);
|
sendMessage(socket, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::command_VERSION(const QString&, QTcpSocket* socket)
|
void SocketApi::command_VERSION(const QString&, SocketType* socket)
|
||||||
{
|
{
|
||||||
sendMessage(socket, QLatin1String("VERSION:" MIRALL_VERSION_STRING ":" MIRALL_SOCKET_API_VERSION));
|
sendMessage(socket, QLatin1String("VERSION:" MIRALL_VERSION_STRING ":" MIRALL_SOCKET_API_VERSION));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SqlQuery* SocketApi::getSqlQuery( Folder *folder )
|
||||||
|
{
|
||||||
|
if( !folder ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _dbQueries.contains(folder) ) {
|
||||||
|
return _dbQueries[folder];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No valid sql query object yet for this folder */
|
||||||
|
int rc;
|
||||||
|
const QString sql("SELECT inode, mode, modtime, type, md5, fileid, remotePerm FROM "
|
||||||
|
"metadata WHERE phash=?1");
|
||||||
|
QString dbFileName = folder->journalDb()->databaseFilePath();
|
||||||
|
|
||||||
|
QFileInfo fi(dbFileName);
|
||||||
|
if( fi.exists() ) {
|
||||||
|
SqlDatabase *db = new SqlDatabase;
|
||||||
|
|
||||||
|
if( db && db->open(dbFileName) ) {
|
||||||
|
_openDbs.insert(folder, db);
|
||||||
|
|
||||||
|
SqlQuery *query = new SqlQuery(*db);
|
||||||
|
rc = query->prepare(sql);
|
||||||
|
|
||||||
|
if( rc != SQLITE_OK ) {
|
||||||
|
delete query;
|
||||||
|
qDebug() << "Unable to prepare the query statement:" << rc;
|
||||||
|
return 0; // do not insert into hash
|
||||||
|
}
|
||||||
|
_dbQueries.insert( folder, query);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Journal to query does not yet exist.";
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncJournalFileRecord SocketApi::dbFileRecord_capi( Folder *folder, QString fileName )
|
||||||
|
{
|
||||||
|
if( !(folder && folder->journalDb()) ) {
|
||||||
|
return SyncJournalFileRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( fileName.startsWith( folder->path() )) {
|
||||||
|
fileName.remove(0, folder->path().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlQuery *query = getSqlQuery(folder);
|
||||||
|
SyncJournalFileRecord rec;
|
||||||
|
|
||||||
|
if( query ) {
|
||||||
|
qlonglong phash = SyncJournalDb::getPHash( fileName );
|
||||||
|
query->bindValue(1, phash);
|
||||||
|
// int column_count = sqlite3_column_count(stmt);
|
||||||
|
|
||||||
|
if (query->next()) {
|
||||||
|
rec._path = fileName;
|
||||||
|
rec._inode = query->int64Value(0);
|
||||||
|
rec._mode = query->intValue(1);
|
||||||
|
rec._modtime = Utility::qDateTimeFromTime_t( query->int64Value(2));
|
||||||
|
rec._type = query->intValue(3);
|
||||||
|
rec._etag = query->baValue(4);
|
||||||
|
rec._fileId = query->baValue(5);
|
||||||
|
rec._remotePerm = query->baValue(6);
|
||||||
|
}
|
||||||
|
query->reset();
|
||||||
|
}
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get status about a single file.
|
||||||
|
*/
|
||||||
|
SyncFileStatus SocketApi::fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes )
|
||||||
|
{
|
||||||
|
QString file = folder->path();
|
||||||
|
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
|
||||||
|
|
||||||
|
if( fileName != QLatin1String("/") && !fileName.isEmpty() ) {
|
||||||
|
file = folder->path() + fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( fileName.endsWith(QLatin1Char('/')) ) {
|
||||||
|
fileName.truncate(fileName.length()-1);
|
||||||
|
qDebug() << "Removed trailing slash: " << fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fi(file);
|
||||||
|
|
||||||
|
if( !fi.exists() ) {
|
||||||
|
qDebug() << "OO File " << file << " is not existing";
|
||||||
|
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// file is ignored?
|
||||||
|
if( fi.isSymLink() ) {
|
||||||
|
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
|
||||||
|
if( fi.isDir() ) {
|
||||||
|
type = CSYNC_FTW_TYPE_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// '\' is ignored, so convert to unix path before passing the path in.
|
||||||
|
QString unixFileName = QDir::fromNativeSeparators(fileName);
|
||||||
|
|
||||||
|
CSYNC_EXCLUDE_TYPE excl = csync_excluded_no_ctx(excludes, unixFileName.toUtf8(), type);
|
||||||
|
if( excl != CSYNC_NOT_EXCLUDED ) {
|
||||||
|
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
|
||||||
|
if (type == CSYNC_FTW_TYPE_DIR) {
|
||||||
|
if (folder->estimateState(fileName, type, &status)) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Folder estimated status for" << fileName << "to" << status.toSocketAPIString();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
if (fileName == "") {
|
||||||
|
// sync folder itself
|
||||||
|
if (folder->syncResult().status() == SyncResult::Undefined
|
||||||
|
|| folder->syncResult().status() == SyncResult::NotYetStarted
|
||||||
|
|| folder->syncResult().status() == SyncResult::SyncPrepare
|
||||||
|
|| folder->syncResult().status() == SyncResult::SyncRunning
|
||||||
|
|| folder->syncResult().status() == SyncResult::Paused) {
|
||||||
|
status.set(SyncFileStatus::STATUS_EVAL);
|
||||||
|
return status;
|
||||||
|
} else if (folder->syncResult().status() == SyncResult::Success
|
||||||
|
|| folder->syncResult().status() == SyncResult::Problem) {
|
||||||
|
status.set(SyncFileStatus::STATUS_SYNC);
|
||||||
|
return status;
|
||||||
|
} else if (folder->syncResult().status() == SyncResult::Error
|
||||||
|
|| folder->syncResult().status() == SyncResult::SetupError
|
||||||
|
|| folder->syncResult().status() == SyncResult::SyncAbortRequested) {
|
||||||
|
status.set(SyncFileStatus::STATUS_ERROR);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SyncJournalFileRecord rec = dbFileRecord_capi(folder, unixFileName );
|
||||||
|
if (rec.isValid()) {
|
||||||
|
status.set(SyncFileStatus::STATUS_SYNC);
|
||||||
|
if (rec._remotePerm.contains("S")) {
|
||||||
|
status.setSharedWithMe(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status.set(SyncFileStatus::STATUS_EVAL);
|
||||||
|
}
|
||||||
|
} else if (type == CSYNC_FTW_TYPE_FILE) {
|
||||||
|
if (folder->estimateState(fileName, type, &status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
SyncJournalFileRecord rec = dbFileRecord_capi(folder, unixFileName );
|
||||||
|
if (rec.isValid()) {
|
||||||
|
if (rec._remotePerm.contains("S")) {
|
||||||
|
status.setSharedWithMe(true);
|
||||||
|
}
|
||||||
|
if( FileSystem::getModTime(fi.absoluteFilePath()) == Utility::qDateTimeToTime_t(rec._modtime) ) {
|
||||||
|
status.set(SyncFileStatus::STATUS_SYNC);
|
||||||
|
return status;
|
||||||
|
} else {
|
||||||
|
status.set(SyncFileStatus::STATUS_EVAL);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status.set(SyncFileStatus::STATUS_NEW);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Mirall
|
} // namespace Mirall
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,16 @@ extern "C" {
|
||||||
#include <std/c_string.h>
|
#include <std/c_string.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
#include <QWeakPointer>
|
#include <QWeakPointer>
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
#include <QTcpServer>
|
#include <QTcpServer>
|
||||||
|
#include <QLocalServer>
|
||||||
|
|
||||||
#include "syncfileitem.h"
|
#include "syncfileitem.h"
|
||||||
|
#include "syncjournalfilerecord.h"
|
||||||
|
#include "ownsql.h"
|
||||||
|
|
||||||
class QUrl;
|
class QUrl;
|
||||||
class QLocalSocket;
|
class QLocalSocket;
|
||||||
|
@ -32,6 +37,11 @@ class QStringList;
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
|
typedef QLocalSocket SocketType;
|
||||||
|
|
||||||
|
class SyncFileStatus;
|
||||||
|
class Folder;
|
||||||
|
|
||||||
class SocketApi : public QObject
|
class SocketApi : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -54,18 +64,29 @@ private slots:
|
||||||
void slotSyncItemDiscovered(const QString &, const SyncFileItem &);
|
void slotSyncItemDiscovered(const QString &, const SyncFileItem &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendMessage(QTcpSocket* socket, const QString& message, bool doWait = false);
|
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes );
|
||||||
|
SyncJournalFileRecord dbFileRecord( Folder *folder, QString fileName );
|
||||||
|
SyncJournalFileRecord dbFileRecord_capi( Folder *folder, QString fileName );
|
||||||
|
SyncFileStatus recursiveFolderStatus(Folder *folder, const QString& fileName, c_strlist_t *excludes );
|
||||||
|
SqlQuery *getSqlQuery( Folder *folder );
|
||||||
|
|
||||||
|
void sendMessage(SocketType* socket, const QString& message, bool doWait = false);
|
||||||
void broadcastMessage(const QString& verb, const QString &path, const QString &status = QString::null, bool doWait = false);
|
void broadcastMessage(const QString& verb, const QString &path, const QString &status = QString::null, bool doWait = false);
|
||||||
|
|
||||||
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString& argument, QTcpSocket* socket);
|
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString& argument, SocketType* socket);
|
||||||
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString& argument, QTcpSocket* socket);
|
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString& argument, SocketType* socket);
|
||||||
|
|
||||||
Q_INVOKABLE void command_VERSION(const QString& argument, QTcpSocket* socket);
|
Q_INVOKABLE void command_VERSION(const QString& argument, SocketType* socket);
|
||||||
|
|
||||||
private:
|
#ifdef SOCKETAPI_TCP
|
||||||
QTcpServer *_localServer;
|
QTcpServer _localServer;
|
||||||
QList<QTcpSocket*> _listeners;
|
#else
|
||||||
|
QLocalServer _localServer;
|
||||||
|
#endif
|
||||||
|
QList<SocketType*> _listeners;
|
||||||
c_strlist_t *_excludes;
|
c_strlist_t *_excludes;
|
||||||
|
QHash<Folder*, SqlQuery*> _dbQueries;
|
||||||
|
QHash<Folder*, SqlDatabase*> _openDbs;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,11 +149,10 @@ bool SslErrorDialog::checkFailingCertsKnown( const QList<QSslError> &errors )
|
||||||
}
|
}
|
||||||
msg += QL("</div></body></html>");
|
msg += QL("</div></body></html>");
|
||||||
|
|
||||||
qDebug() << "# # # # # # ";
|
//qDebug() << "# # # # # # ";
|
||||||
qDebug() << msg;
|
//qDebug() << msg;
|
||||||
QTextDocument *doc = new QTextDocument(0);
|
QTextDocument *doc = new QTextDocument(0);
|
||||||
QString style = styleSheet();
|
QString style = styleSheet();
|
||||||
qDebug() << "Style: " << style;
|
|
||||||
doc->addResource( QTextDocument::StyleSheetResource, QUrl( QL("format.css") ), style);
|
doc->addResource( QTextDocument::StyleSheetResource, QUrl( QL("format.css") ), style);
|
||||||
doc->setHtml( msg );
|
doc->setHtml( msg );
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,11 @@ QString Updater::getSystemInfo()
|
||||||
// To test, cmake with -DAPPLICATION_UPDATE_URL="http://127.0.0.1:8080/test.rss"
|
// To test, cmake with -DAPPLICATION_UPDATE_URL="http://127.0.0.1:8080/test.rss"
|
||||||
Updater *Updater::create()
|
Updater *Updater::create()
|
||||||
{
|
{
|
||||||
QUrl updateBaseUrl = addQueryParams(QUrl(QLatin1String(APPLICATION_UPDATE_URL)));
|
QUrl updateBaseUrl(QString::fromLocal8Bit(qgetenv("OCC_UPDATE_URL")));
|
||||||
|
if (updateBaseUrl.isEmpty()) {
|
||||||
|
updateBaseUrl = QUrl(QLatin1String(APPLICATION_UPDATE_URL));
|
||||||
|
}
|
||||||
|
updateBaseUrl = addQueryParams(updateBaseUrl);
|
||||||
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
||||||
updateBaseUrl.addQueryItem( QLatin1String("sparkle"), QLatin1String("true"));
|
updateBaseUrl.addQueryItem( QLatin1String("sparkle"), QLatin1String("true"));
|
||||||
return new SparkleUpdater(updateBaseUrl.toString());
|
return new SparkleUpdater(updateBaseUrl.toString());
|
||||||
|
|
|
@ -57,6 +57,7 @@ set(libsync_SRCS
|
||||||
syncresult.cpp
|
syncresult.cpp
|
||||||
theme.cpp
|
theme.cpp
|
||||||
utility.cpp
|
utility.cpp
|
||||||
|
ownsql.cpp
|
||||||
creds/dummycredentials.cpp
|
creds/dummycredentials.cpp
|
||||||
creds/abstractcredentials.cpp
|
creds/abstractcredentials.cpp
|
||||||
creds/credentialsfactory.cpp
|
creds/credentialsfactory.cpp
|
||||||
|
|
|
@ -154,8 +154,10 @@ public:
|
||||||
QNetworkAccessManager* networkAccessManager();
|
QNetworkAccessManager* networkAccessManager();
|
||||||
|
|
||||||
QuotaInfo *quotaInfo();
|
QuotaInfo *quotaInfo();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void stateChanged(int state);
|
void stateChanged(int state);
|
||||||
|
void propagatorNetworkActivity();
|
||||||
|
|
||||||
protected Q_SLOTS:
|
protected Q_SLOTS:
|
||||||
void slotHandleErrors(QNetworkReply*,QList<QSslError>);
|
void slotHandleErrors(QNetworkReply*,QList<QSslError>);
|
||||||
|
|
|
@ -66,7 +66,11 @@ int getauth(const char *prompt,
|
||||||
// qDebug() << "OOO Password requested!";
|
// qDebug() << "OOO Password requested!";
|
||||||
qstrncpy( buf, pwd.toUtf8().constData(), len );
|
qstrncpy( buf, pwd.toUtf8().constData(), len );
|
||||||
} else {
|
} else {
|
||||||
re = handleNeonSSLProblems(prompt, buf, len, echo, verify, userdata);
|
if( http_credentials->sslIsTrusted() ) {
|
||||||
|
qstrcpy( buf, "yes" ); // Certificate is fine!
|
||||||
|
} else {
|
||||||
|
re = handleNeonSSLProblems(prompt, buf, len, echo, verify, userdata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return re;
|
return re;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
virtual QString queryPassword(bool *ok) = 0;
|
virtual QString queryPassword(bool *ok) = 0;
|
||||||
void invalidateToken(Account *account) Q_DECL_OVERRIDE;
|
void invalidateToken(Account *account) Q_DECL_OVERRIDE;
|
||||||
QString fetchUser(Account *account);
|
QString fetchUser(Account *account);
|
||||||
|
virtual bool sslIsTrusted() { return false; }
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
#include <csync_private.h>
|
#include <csync_private.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
bool DiscoveryJob::isInBlackList(const QString& path) const
|
bool DiscoveryJob::isInSelectiveSyncBlackList(const QString& path) const
|
||||||
{
|
{
|
||||||
if (_selectiveSyncBlackList.isEmpty()) {
|
if (_selectiveSyncBlackList.isEmpty()) {
|
||||||
// If there is no black list, everything is allowed
|
// If there is no black list, everything is allowed
|
||||||
|
@ -35,19 +37,24 @@ bool DiscoveryJob::isInBlackList(const QString& path) const
|
||||||
|
|
||||||
auto it = std::lower_bound(_selectiveSyncBlackList.begin(), _selectiveSyncBlackList.end(), pathSlash);
|
auto it = std::lower_bound(_selectiveSyncBlackList.begin(), _selectiveSyncBlackList.end(), pathSlash);
|
||||||
|
|
||||||
|
if (it != _selectiveSyncBlackList.end() && *it == pathSlash) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (it == _selectiveSyncBlackList.begin()) {
|
if (it == _selectiveSyncBlackList.begin()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
--it;
|
--it;
|
||||||
if (pathSlash.startsWith(*it + QLatin1Char('/'))) {
|
Q_ASSERT(it->endsWith(QLatin1Char('/'))); // SyncEngine::setSelectiveSyncBlackList makes sure of that
|
||||||
|
if (pathSlash.startsWith(*it)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DiscoveryJob::isInWhiteListCallBack(void *data, const char *path)
|
int DiscoveryJob::isInSelectiveSyncBlackListCallBack(void *data, const char *path)
|
||||||
{
|
{
|
||||||
return static_cast<DiscoveryJob*>(data)->isInBlackList(QString::fromUtf8(path));
|
return static_cast<DiscoveryJob*>(data)->isInSelectiveSyncBlackList(QString::fromUtf8(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscoveryJob::update_job_update_callback (bool local,
|
void DiscoveryJob::update_job_update_callback (bool local,
|
||||||
|
@ -65,15 +72,15 @@ void DiscoveryJob::update_job_update_callback (bool local,
|
||||||
updateJob->lastUpdateProgressCallbackCall.restart();
|
updateJob->lastUpdateProgressCallbackCall.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString path = QString::fromUtf8(dirUrl).section('/', -1);
|
QString path(QUrl::fromPercentEncoding(QByteArray(dirUrl)).section('/', -1));
|
||||||
emit updateJob->folderDiscovered(local, path);
|
emit updateJob->folderDiscovered(local, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscoveryJob::start() {
|
void DiscoveryJob::start() {
|
||||||
_selectiveSyncBlackList.sort();
|
_selectiveSyncBlackList.sort();
|
||||||
_csync_ctx->checkBlackListHook = isInWhiteListCallBack;
|
_csync_ctx->checkSelectiveSyncBlackListHook = isInSelectiveSyncBlackListCallBack;
|
||||||
_csync_ctx->checkBlackListData = this;
|
_csync_ctx->checkSelectiveSyncBlackListData = this;
|
||||||
|
|
||||||
_csync_ctx->callbacks.update_callback = update_job_update_callback;
|
_csync_ctx->callbacks.update_callback = update_job_update_callback;
|
||||||
_csync_ctx->callbacks.update_callback_userdata = this;
|
_csync_ctx->callbacks.update_callback_userdata = this;
|
||||||
|
@ -85,8 +92,8 @@ void DiscoveryJob::start() {
|
||||||
lastUpdateProgressCallbackCall.invalidate();
|
lastUpdateProgressCallbackCall.invalidate();
|
||||||
int ret = csync_update(_csync_ctx);
|
int ret = csync_update(_csync_ctx);
|
||||||
|
|
||||||
_csync_ctx->checkBlackListHook = 0;
|
_csync_ctx->checkSelectiveSyncBlackListHook = 0;
|
||||||
_csync_ctx->checkBlackListData = 0;
|
_csync_ctx->checkSelectiveSyncBlackListData = 0;
|
||||||
|
|
||||||
_csync_ctx->callbacks.update_callback = 0;
|
_csync_ctx->callbacks.update_callback = 0;
|
||||||
_csync_ctx->callbacks.update_callback_userdata = 0;
|
_csync_ctx->callbacks.update_callback_userdata = 0;
|
||||||
|
|
|
@ -40,8 +40,8 @@ class DiscoveryJob : public QObject {
|
||||||
* return true if the given path should be synced,
|
* return true if the given path should be synced,
|
||||||
* false if the path should be ignored
|
* false if the path should be ignored
|
||||||
*/
|
*/
|
||||||
bool isInBlackList(const QString &path) const;
|
bool isInSelectiveSyncBlackList(const QString &path) const;
|
||||||
static int isInWhiteListCallBack(void *, const char *);
|
static int isInSelectiveSyncBlackListCallBack(void *, const char *);
|
||||||
|
|
||||||
static void update_job_update_callback (bool local,
|
static void update_job_update_callback (bool local,
|
||||||
const char *dirname,
|
const char *dirname,
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
|
@ -85,6 +86,7 @@ void Logger::log(Log log)
|
||||||
} else {
|
} else {
|
||||||
// msg += "ownCloud - ";
|
// msg += "ownCloud - ";
|
||||||
}
|
}
|
||||||
|
msg += QString().sprintf("%p ", (void*)QThread::currentThread());
|
||||||
msg += log.message;
|
msg += log.message;
|
||||||
// _logs.append(log);
|
// _logs.append(log);
|
||||||
// std::cout << qPrintable(log.message) << std::endl;
|
// std::cout << qPrintable(log.message) << std::endl;
|
||||||
|
|
|
@ -205,15 +205,16 @@ QString MirallConfigFile::configPathWithAppName() const
|
||||||
return QFileInfo( configFile() ).dir().absolutePath().append("/");
|
return QFileInfo( configFile() ).dir().absolutePath().append("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const QLatin1String exclFile("sync-exclude.lst");
|
||||||
|
|
||||||
QString MirallConfigFile::excludeFile(Scope scope) const
|
QString MirallConfigFile::excludeFile(Scope scope) const
|
||||||
{
|
{
|
||||||
// prefer sync-exclude.lst, but if it does not exist, check for
|
// prefer sync-exclude.lst, but if it does not exist, check for
|
||||||
// exclude.lst for compatibility reasons in the user writeable
|
// exclude.lst for compatibility reasons in the user writeable
|
||||||
// directories.
|
// directories.
|
||||||
const QString exclFile("sync-exclude.lst");
|
|
||||||
QFileInfo fi;
|
|
||||||
|
|
||||||
if (scope != SystemScope) {
|
if (scope != SystemScope) {
|
||||||
|
QFileInfo fi;
|
||||||
fi.setFile( configPath(), exclFile );
|
fi.setFile( configPath(), exclFile );
|
||||||
|
|
||||||
if( ! fi.isReadable() ) {
|
if( ! fi.isReadable() ) {
|
||||||
|
@ -222,32 +223,37 @@ QString MirallConfigFile::excludeFile(Scope scope) const
|
||||||
if( ! fi.isReadable() ) {
|
if( ! fi.isReadable() ) {
|
||||||
fi.setFile( configPath(), exclFile );
|
fi.setFile( configPath(), exclFile );
|
||||||
}
|
}
|
||||||
|
return fi.absoluteFilePath();
|
||||||
|
} else if (scope != UserScope) {
|
||||||
|
return MirallConfigFile::excludeFileFromSystem();
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return QString(); // unreachable
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (scope != UserScope) {
|
QString MirallConfigFile::excludeFileFromSystem()
|
||||||
// Check alternative places...
|
{
|
||||||
if( ! fi.isReadable() ) {
|
QFileInfo fi;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
fi.setFile( QCoreApplication::applicationDirPath(), exclFile );
|
fi.setFile( QCoreApplication::applicationDirPath(), exclFile );
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
fi.setFile( QString( SYSCONFDIR "/%1").arg(Theme::instance()->appName()), exclFile );
|
fi.setFile( QString( SYSCONFDIR "/%1").arg(Theme::instance()->appName()), exclFile );
|
||||||
if ( ! fi.exists() ) {
|
if ( ! fi.exists() ) {
|
||||||
// Prefer to return the preferred path! Only use the fallback location
|
// Prefer to return the preferred path! Only use the fallback location
|
||||||
// if the other path does not exist and the fallback is valid.
|
// if the other path does not exist and the fallback is valid.
|
||||||
QFileInfo nextToBinary( QCoreApplication::applicationDirPath(), exclFile );
|
QFileInfo nextToBinary( QCoreApplication::applicationDirPath(), exclFile );
|
||||||
if (nextToBinary.exists()) {
|
if (nextToBinary.exists()) {
|
||||||
fi = nextToBinary;
|
fi = nextToBinary;
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
// exec path is inside the bundle
|
|
||||||
fi.setFile( QCoreApplication::applicationDirPath(),
|
|
||||||
QLatin1String("../Resources/") + exclFile );
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// exec path is inside the bundle
|
||||||
|
fi.setFile( QCoreApplication::applicationDirPath(),
|
||||||
|
QLatin1String("../Resources/") + exclFile );
|
||||||
|
#endif
|
||||||
return fi.absoluteFilePath();
|
return fi.absoluteFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
QString configPathWithAppName() const;
|
QString configPathWithAppName() const;
|
||||||
QString configFile() const;
|
QString configFile() const;
|
||||||
QString excludeFile(Scope scope) const;
|
QString excludeFile(Scope scope) const;
|
||||||
|
static QString excludeFileFromSystem(); // doesn't access config dir
|
||||||
|
|
||||||
bool exists();
|
bool exists();
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ Q_DECLARE_METATYPE(QTimer*)
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
|
bool AbstractNetworkJob::preOc7WasDetected = false;
|
||||||
|
|
||||||
AbstractNetworkJob::AbstractNetworkJob(Account *account, const QString &path, QObject *parent)
|
AbstractNetworkJob::AbstractNetworkJob(Account *account, const QString &path, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, _duration(0)
|
, _duration(0)
|
||||||
|
@ -48,8 +50,22 @@ AbstractNetworkJob::AbstractNetworkJob(Account *account, const QString &path, QO
|
||||||
, _path(path)
|
, _path(path)
|
||||||
{
|
{
|
||||||
_timer.setSingleShot(true);
|
_timer.setSingleShot(true);
|
||||||
_timer.setInterval(10*1000); // default to 10 seconds.
|
if (!AbstractNetworkJob::preOc7WasDetected) {
|
||||||
|
_timer.setInterval(10*1000); // default to 10 seconds.
|
||||||
|
} else {
|
||||||
|
qDebug() << "Pre-oc7 server detected, adjusting timeout values";
|
||||||
|
_timer.setInterval(60*1000); // long PROPFINDs in oc6 might take too long
|
||||||
|
}
|
||||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
|
connect(&_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
|
||||||
|
|
||||||
|
connect(this, SIGNAL(networkActivity()), SLOT(resetTimeout()));
|
||||||
|
|
||||||
|
// Network activity on the propagator jobs (GET/PUT) keeps all requests alive.
|
||||||
|
// This is a workaround for OC instances which only support one
|
||||||
|
// parallel up and download
|
||||||
|
if (_account) {
|
||||||
|
connect(_account, SIGNAL(propagatorNetworkActivity()), SLOT(resetTimeout()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractNetworkJob::setReply(QNetworkReply *reply)
|
void AbstractNetworkJob::setReply(QNetworkReply *reply)
|
||||||
|
@ -80,11 +96,6 @@ void AbstractNetworkJob::setIgnoreCredentialFailure(bool ignore)
|
||||||
_ignoreCredentialFailure = ignore;
|
_ignoreCredentialFailure = ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractNetworkJob::setAccount(Account *account)
|
|
||||||
{
|
|
||||||
_account = account;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractNetworkJob::setPath(const QString &path)
|
void AbstractNetworkJob::setPath(const QString &path)
|
||||||
{
|
{
|
||||||
_path = path;
|
_path = path;
|
||||||
|
@ -93,6 +104,8 @@ void AbstractNetworkJob::setPath(const QString &path)
|
||||||
void AbstractNetworkJob::setupConnections(QNetworkReply *reply)
|
void AbstractNetworkJob::setupConnections(QNetworkReply *reply)
|
||||||
{
|
{
|
||||||
connect(reply, SIGNAL(finished()), SLOT(slotFinished()));
|
connect(reply, SIGNAL(finished()), SLOT(slotFinished()));
|
||||||
|
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(networkActivity()));
|
||||||
|
connect(reply, SIGNAL(uploadProgress(qint64,qint64)), SIGNAL(networkActivity()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply* AbstractNetworkJob::addTimer(QNetworkReply *reply)
|
QNetworkReply* AbstractNetworkJob::addTimer(QNetworkReply *reply)
|
||||||
|
@ -179,9 +192,11 @@ QString AbstractNetworkJob::responseTimestamp()
|
||||||
return _responseTimestamp;
|
return _responseTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractNetworkJob::~AbstractNetworkJob() {
|
AbstractNetworkJob::~AbstractNetworkJob()
|
||||||
if (_reply)
|
{
|
||||||
|
if (_reply) {
|
||||||
_reply->deleteLater();
|
_reply->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractNetworkJob::start()
|
void AbstractNetworkJob::start()
|
||||||
|
@ -195,11 +210,14 @@ void AbstractNetworkJob::start()
|
||||||
|
|
||||||
void AbstractNetworkJob::slotTimeout()
|
void AbstractNetworkJob::slotTimeout()
|
||||||
{
|
{
|
||||||
qDebug() << this << "Timeout" ;
|
qDebug() << this << "Timeout";
|
||||||
reply()->abort();
|
if (reply()) {
|
||||||
|
reply()->abort();
|
||||||
|
} else {
|
||||||
|
qDebug() << "reply was NULL";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************************************/
|
/*********************************************************************************************/
|
||||||
|
|
||||||
RequestEtagJob::RequestEtagJob(Account *account, const QString &path, QObject *parent)
|
RequestEtagJob::RequestEtagJob(Account *account, const QString &path, QObject *parent)
|
||||||
|
@ -419,7 +437,9 @@ bool CheckServerJob::finished()
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
QByteArray body = reply()->readAll();
|
QByteArray body = reply()->readAll();
|
||||||
if( body.isEmpty() ) {
|
int httpStatus = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
if( body.isEmpty() || httpStatus != 200) {
|
||||||
|
qDebug() << "error: status.php replied " << httpStatus << body;
|
||||||
emit instanceNotFound(reply());
|
emit instanceNotFound(reply());
|
||||||
} else {
|
} else {
|
||||||
QVariantMap status = QtJson::parse(QString::fromUtf8(body), success).toMap();
|
QVariantMap status = QtJson::parse(QString::fromUtf8(body), success).toMap();
|
||||||
|
@ -432,6 +452,12 @@ bool CheckServerJob::finished()
|
||||||
if( status.contains("installed")
|
if( status.contains("installed")
|
||||||
&& status.contains("version")
|
&& status.contains("version")
|
||||||
&& status.contains("versionstring") ) {
|
&& status.contains("versionstring") ) {
|
||||||
|
|
||||||
|
QString versionString = status.value("version").toString();
|
||||||
|
if (versionString.contains('.') && versionString.split('.')[0].toInt() < 7) {
|
||||||
|
AbstractNetworkJob::preOc7WasDetected = true;
|
||||||
|
}
|
||||||
|
|
||||||
emit instanceFound(reply()->url(), status);
|
emit instanceFound(reply()->url(), status);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "No proper answer on " << requestedUrl;
|
qDebug() << "No proper answer on " << requestedUrl;
|
||||||
|
|
|
@ -55,15 +55,14 @@ public:
|
||||||
|
|
||||||
virtual void start();
|
virtual void start();
|
||||||
|
|
||||||
void setAccount(Account *account);
|
|
||||||
Account* account() const { return _account; }
|
Account* account() const { return _account; }
|
||||||
|
|
||||||
void setPath(const QString &path);
|
void setPath(const QString &path);
|
||||||
QString path() const { return _path; }
|
QString path() const { return _path; }
|
||||||
|
|
||||||
void setReply(QNetworkReply *reply);
|
void setReply(QNetworkReply *reply);
|
||||||
QNetworkReply* reply() const { return _reply; }
|
QNetworkReply* reply() const { return _reply; }
|
||||||
|
|
||||||
|
|
||||||
void setIgnoreCredentialFailure(bool ignore);
|
void setIgnoreCredentialFailure(bool ignore);
|
||||||
bool ignoreCredentialFailure() const { return _ignoreCredentialFailure; }
|
bool ignoreCredentialFailure() const { return _ignoreCredentialFailure; }
|
||||||
|
|
||||||
|
@ -75,6 +74,7 @@ public slots:
|
||||||
void resetTimeout();
|
void resetTimeout();
|
||||||
signals:
|
signals:
|
||||||
void networkError(QNetworkReply *reply);
|
void networkError(QNetworkReply *reply);
|
||||||
|
void networkActivity();
|
||||||
protected:
|
protected:
|
||||||
void setupConnections(QNetworkReply *reply);
|
void setupConnections(QNetworkReply *reply);
|
||||||
QNetworkReply* davRequest(const QByteArray& verb, const QString &relPath,
|
QNetworkReply* davRequest(const QByteArray& verb, const QString &relPath,
|
||||||
|
@ -94,6 +94,9 @@ protected:
|
||||||
QElapsedTimer _durationTimer;
|
QElapsedTimer _durationTimer;
|
||||||
quint64 _duration;
|
quint64 _duration;
|
||||||
|
|
||||||
|
// Timeout workarounds (Because of PHP session locking)
|
||||||
|
static bool preOc7WasDetected;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotFinished();
|
void slotFinished();
|
||||||
virtual void slotTimeout();
|
virtual void slotTimeout();
|
||||||
|
|
|
@ -51,14 +51,15 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
||||||
_item._errorString += tr("; Restoration Failed: ") + errorString;
|
_item._errorString += tr("; Restoration Failed: ") + errorString;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_item._errorString = errorString;
|
if( _item._errorString.isEmpty() ) {
|
||||||
|
_item._errorString = errorString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( _propagator->_abortRequested.fetchAndAddRelaxed(0) ) {
|
if( _propagator->_abortRequested.fetchAndAddRelaxed(0) &&
|
||||||
|
(status == SyncFileItem::NormalError || status == SyncFileItem::FatalError)) {
|
||||||
// an abort request is ongoing. Change the status to Soft-Error
|
// an abort request is ongoing. Change the status to Soft-Error
|
||||||
|
|
||||||
status = SyncFileItem::SoftError;
|
status = SyncFileItem::SoftError;
|
||||||
_item._errorString = tr("Operation was canceled by user interaction.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_item._status = status;
|
_item._status = status;
|
||||||
|
@ -76,7 +77,7 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
||||||
}
|
}
|
||||||
retries = defaultRetriesCount.fetchAndAddAcquire(0);
|
retries = defaultRetriesCount.fetchAndAddAcquire(0);
|
||||||
}
|
}
|
||||||
SyncJournalBlacklistRecord record(_item, retries);;
|
SyncJournalBlacklistRecord record(_item, retries);
|
||||||
|
|
||||||
switch( status ) {
|
switch( status ) {
|
||||||
case SyncFileItem::SoftError:
|
case SyncFileItem::SoftError:
|
||||||
|
@ -96,9 +97,13 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
||||||
break;
|
break;
|
||||||
case SyncFileItem::Success:
|
case SyncFileItem::Success:
|
||||||
case SyncFileItem::Restoration:
|
case SyncFileItem::Restoration:
|
||||||
if( _item._blacklistedInDb ) {
|
if( _item._hasBlacklistEntry ) {
|
||||||
// wipe blacklist entry.
|
// wipe blacklist entry.
|
||||||
_propagator->_journal->wipeBlacklistEntry(_item._file);
|
_propagator->_journal->wipeBlacklistEntry(_item._file);
|
||||||
|
// remove a blacklist entry in case the file was moved.
|
||||||
|
if( _item._originalFile != _item._file ) {
|
||||||
|
_propagator->_journal->wipeBlacklistEntry(_item._originalFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SyncFileItem::Conflict:
|
case SyncFileItem::Conflict:
|
||||||
|
@ -380,10 +385,10 @@ bool OwncloudPropagator::localFileNameClash( const QString& relFile )
|
||||||
// returns false.
|
// returns false.
|
||||||
} else {
|
} else {
|
||||||
QString realFileName = QString::fromWCharArray( FindFileData.cFileName );
|
QString realFileName = QString::fromWCharArray( FindFileData.cFileName );
|
||||||
qDebug() << Q_FUNC_INFO << "Real file name is " << realFileName;
|
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
|
|
||||||
if( ! file.endsWith(realFileName, Qt::CaseSensitive) ) {
|
if( ! file.endsWith(realFileName, Qt::CaseSensitive) ) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Detected case clash between" << file << "and" << realFileName;
|
||||||
re = true;
|
re = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ QString ownCloudTheme::about() const
|
||||||
"<p>Copyright ownCloud, Inc.</p>"
|
"<p>Copyright ownCloud, Inc.</p>"
|
||||||
"<p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>"
|
"<p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>"
|
||||||
"ownCloud and the ownCloud Logo are registered trademarks of ownCloud, "
|
"ownCloud and the ownCloud Logo are registered trademarks of ownCloud, "
|
||||||
"Inc. in the United States, other countries, or both</p>"
|
"Inc. in the United States, other countries, or both.</p>"
|
||||||
)
|
)
|
||||||
.arg(MIRALL_VERSION_STRING)
|
.arg(MIRALL_VERSION_STRING)
|
||||||
.arg("http://" MIRALL_STRINGIFY(APPLICATION_DOMAIN))
|
.arg("http://" MIRALL_STRINGIFY(APPLICATION_DOMAIN))
|
||||||
|
|
292
src/libsync/ownsql.cpp
Normal file
292
src/libsync/ownsql.cpp
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
/*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 <QDateTime>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "ownsql.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
#define SQLITE_SLEEP_TIME_USEC 100000
|
||||||
|
#define SQLITE_REPEAT_COUNT 20
|
||||||
|
|
||||||
|
#define SQLITE_DO(A) if(1) { \
|
||||||
|
_errId = (A); if(_errId != SQLITE_OK) { _error= QString::fromUtf8(sqlite3_errmsg(_db)); \
|
||||||
|
} }
|
||||||
|
|
||||||
|
namespace Mirall {
|
||||||
|
|
||||||
|
SqlDatabase::SqlDatabase()
|
||||||
|
:_db(0),
|
||||||
|
_errId(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlDatabase::isOpen()
|
||||||
|
{
|
||||||
|
return _db != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlDatabase::open( const QString& filename )
|
||||||
|
{
|
||||||
|
if(isOpen()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int flag = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_NOMUTEX;
|
||||||
|
SQLITE_DO( sqlite3_open_v2(filename.toUtf8().constData(), &_db, flag, 0) );
|
||||||
|
|
||||||
|
if( _errId != SQLITE_OK ) {
|
||||||
|
close(); // FIXME: Correct?
|
||||||
|
_db = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_busy_timeout(_db, 5000);
|
||||||
|
|
||||||
|
return isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SqlDatabase::error() const
|
||||||
|
{
|
||||||
|
const QString err(_error);
|
||||||
|
// _error.clear();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlDatabase::close()
|
||||||
|
{
|
||||||
|
if( _db ) {
|
||||||
|
SQLITE_DO(sqlite3_close(_db) );
|
||||||
|
_db = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlDatabase::transaction()
|
||||||
|
{
|
||||||
|
if( ! _db ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SQLITE_DO(sqlite3_exec(_db, "BEGIN", 0, 0, 0));
|
||||||
|
return _errId == SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlDatabase::commit()
|
||||||
|
{
|
||||||
|
if( ! _db ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SQLITE_DO(sqlite3_exec(_db, "COMMIT", 0, 0, 0));
|
||||||
|
return _errId == SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3* SqlDatabase::sqliteDb()
|
||||||
|
{
|
||||||
|
return _db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================================================================================== */
|
||||||
|
|
||||||
|
SqlQuery::SqlQuery( SqlDatabase db )
|
||||||
|
:_db(db.sqliteDb()),
|
||||||
|
_stmt(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlQuery::~SqlQuery()
|
||||||
|
{
|
||||||
|
if( _stmt ) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlQuery::SqlQuery(const QString& sql, SqlDatabase db)
|
||||||
|
:_db(db.sqliteDb()),
|
||||||
|
_stmt(0)
|
||||||
|
{
|
||||||
|
prepare(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SqlQuery::prepare( const QString& sql)
|
||||||
|
{
|
||||||
|
QString s(sql);
|
||||||
|
_sql = s.trimmed();
|
||||||
|
if(_stmt ) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
if(!_sql.isEmpty() ) {
|
||||||
|
int n = 0;
|
||||||
|
int rc;
|
||||||
|
do {
|
||||||
|
rc = sqlite3_prepare_v2(_db, _sql.toUtf8().constData(), -1, &_stmt, 0);
|
||||||
|
if( (rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED) ) {
|
||||||
|
n++;
|
||||||
|
Mirall::Utility::usleep(SQLITE_SLEEP_TIME_USEC);
|
||||||
|
}
|
||||||
|
} while( (n < SQLITE_REPEAT_COUNT) && ((rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED)));
|
||||||
|
_errId = rc;
|
||||||
|
|
||||||
|
if( _errId != SQLITE_OK ) {
|
||||||
|
qDebug() << "Sqlite prepare statement error:" << _error << "in"<<_sql;
|
||||||
|
}
|
||||||
|
// Q_ASSERT(_errId == SQLITE_OK);
|
||||||
|
}
|
||||||
|
return _errId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlQuery::isSelect()
|
||||||
|
{
|
||||||
|
return (!_sql.isEmpty() && _sql.startsWith("SELECT", Qt::CaseInsensitive));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlQuery::isPragma()
|
||||||
|
{
|
||||||
|
return (!_sql.isEmpty() && _sql.startsWith("PRAGMA", Qt::CaseInsensitive));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlQuery::exec()
|
||||||
|
{
|
||||||
|
// Don't do anything for selects, that is how we use the lib :-|
|
||||||
|
if(_stmt && !isSelect() && !isPragma() ) {
|
||||||
|
int rc, n = 0;
|
||||||
|
do {
|
||||||
|
rc = sqlite3_step(_stmt);
|
||||||
|
if( rc == SQLITE_LOCKED ) {
|
||||||
|
rc = sqlite3_reset(_stmt); /* This will also return SQLITE_LOCKED */
|
||||||
|
n++;
|
||||||
|
Mirall::Utility::usleep(SQLITE_SLEEP_TIME_USEC);
|
||||||
|
} else if( rc == SQLITE_BUSY ) {
|
||||||
|
Mirall::Utility::usleep(SQLITE_SLEEP_TIME_USEC);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
} while( (n < SQLITE_REPEAT_COUNT) && ((rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED)));
|
||||||
|
_errId = rc;
|
||||||
|
|
||||||
|
return (_errId == SQLITE_DONE); // either SQLITE_ROW or SQLITE_DONE
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlQuery::next()
|
||||||
|
{
|
||||||
|
SQLITE_DO(sqlite3_step(_stmt));
|
||||||
|
return _errId == SQLITE_ROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlQuery::bindValue(int pos, const QVariant& value)
|
||||||
|
{
|
||||||
|
int res = -1;
|
||||||
|
if( _stmt ) {
|
||||||
|
switch (value.type()) {
|
||||||
|
case QVariant::Int:
|
||||||
|
case QVariant::Bool:
|
||||||
|
res = sqlite3_bind_int(_stmt, pos, value.toInt());
|
||||||
|
break;
|
||||||
|
case QVariant::Double:
|
||||||
|
res = sqlite3_bind_double(_stmt, pos, value.toDouble());
|
||||||
|
break;
|
||||||
|
case QVariant::UInt:
|
||||||
|
case QVariant::LongLong:
|
||||||
|
res = sqlite3_bind_int64(_stmt, pos, value.toLongLong());
|
||||||
|
break;
|
||||||
|
case QVariant::DateTime: {
|
||||||
|
const QDateTime dateTime = value.toDateTime();
|
||||||
|
const QString str = dateTime.toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz"));
|
||||||
|
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
|
||||||
|
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QVariant::Time: {
|
||||||
|
const QTime time = value.toTime();
|
||||||
|
const QString str = time.toString(QLatin1String("hh:mm:ss.zzz"));
|
||||||
|
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
|
||||||
|
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QVariant::String: {
|
||||||
|
if( !value.toString().isNull() ) {
|
||||||
|
// lifetime of string == lifetime of its qvariant
|
||||||
|
const QString *str = static_cast<const QString*>(value.constData());
|
||||||
|
res = sqlite3_bind_text16(_stmt, pos, str->utf16(),
|
||||||
|
(str->size()) * sizeof(QChar), SQLITE_TRANSIENT);
|
||||||
|
} else {
|
||||||
|
// unbound value create a null entry.
|
||||||
|
res = SQLITE_OK;
|
||||||
|
}
|
||||||
|
break; }
|
||||||
|
default: {
|
||||||
|
QString str = value.toString();
|
||||||
|
// SQLITE_TRANSIENT makes sure that sqlite buffers the data
|
||||||
|
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
|
||||||
|
(str.size()) * sizeof(QChar), SQLITE_TRANSIENT);
|
||||||
|
break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res != SQLITE_OK) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "ERROR" << value.toString() << res;
|
||||||
|
}
|
||||||
|
Q_ASSERT( res == SQLITE_OK );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SqlQuery::stringValue(int index)
|
||||||
|
{
|
||||||
|
return QString::fromUtf16(static_cast<const ushort*>(sqlite3_column_text16(_stmt, index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int SqlQuery::intValue(int index)
|
||||||
|
{
|
||||||
|
return sqlite3_column_int(_stmt, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 SqlQuery::int64Value(int index)
|
||||||
|
{
|
||||||
|
return sqlite3_column_int64(_stmt, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SqlQuery::baValue(int index)
|
||||||
|
{
|
||||||
|
return QByteArray( static_cast<const char*>(sqlite3_column_blob(_stmt, index)),
|
||||||
|
sqlite3_column_bytes(_stmt, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SqlQuery::error() const
|
||||||
|
{
|
||||||
|
return _error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SqlQuery::lastQuery() const
|
||||||
|
{
|
||||||
|
return _sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SqlQuery::numRowsAffected()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlQuery::finish()
|
||||||
|
{
|
||||||
|
SQLITE_DO(sqlite3_finalize(_stmt));
|
||||||
|
_stmt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlQuery::reset()
|
||||||
|
{
|
||||||
|
SQLITE_DO(sqlite3_reset(_stmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Mirall
|
83
src/libsync/ownsql.h
Normal file
83
src/libsync/ownsql.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 OWNSQL_H
|
||||||
|
#define OWNSQL_H
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "owncloudlib.h"
|
||||||
|
|
||||||
|
namespace Mirall {
|
||||||
|
|
||||||
|
class OWNCLOUDSYNC_EXPORT SqlDatabase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SqlDatabase();
|
||||||
|
|
||||||
|
bool isOpen();
|
||||||
|
bool open( const QString& filename );
|
||||||
|
bool transaction();
|
||||||
|
bool commit();
|
||||||
|
void close();
|
||||||
|
QString error() const;
|
||||||
|
sqlite3* sqliteDb();
|
||||||
|
|
||||||
|
private:
|
||||||
|
sqlite3 *_db;
|
||||||
|
QString _error; // last error string
|
||||||
|
int _errId;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class OWNCLOUDSYNC_EXPORT SqlQuery
|
||||||
|
{
|
||||||
|
Q_DISABLE_COPY(SqlQuery)
|
||||||
|
public:
|
||||||
|
explicit SqlQuery();
|
||||||
|
explicit SqlQuery(SqlDatabase db);
|
||||||
|
explicit SqlQuery(const QString& sql, SqlDatabase db);
|
||||||
|
|
||||||
|
~SqlQuery();
|
||||||
|
QString error() const;
|
||||||
|
|
||||||
|
QString stringValue(int index);
|
||||||
|
int intValue(int index);
|
||||||
|
quint64 int64Value(int index);
|
||||||
|
QByteArray baValue(int index);
|
||||||
|
|
||||||
|
bool isSelect();
|
||||||
|
bool isPragma();
|
||||||
|
bool exec();
|
||||||
|
int prepare( const QString& sql );
|
||||||
|
bool next();
|
||||||
|
void bindValue(int pos, const QVariant& value);
|
||||||
|
QString lastQuery() const;
|
||||||
|
int numRowsAffected();
|
||||||
|
void reset();
|
||||||
|
void finish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
sqlite3 *_db;
|
||||||
|
sqlite3_stmt *_stmt;
|
||||||
|
QString _error;
|
||||||
|
int _errId;
|
||||||
|
QString _sql;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Mirall
|
||||||
|
|
||||||
|
#endif // OWNSQL_H
|
|
@ -94,7 +94,7 @@ void PUTFileJob::start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(reply(), SIGNAL(uploadProgress(qint64,qint64)), this, SIGNAL(uploadProgress(qint64,qint64)));
|
connect(reply(), SIGNAL(uploadProgress(qint64,qint64)), this, SIGNAL(uploadProgress(qint64,qint64)));
|
||||||
connect(reply(), SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(resetTimeout()));
|
connect(this, SIGNAL(networkActivity()), account(), SIGNAL(propagatorNetworkActivity()));
|
||||||
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
@ -310,6 +310,7 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
||||||
// Precondition Failed: Maybe the bad etag is in the database, we need to clear the
|
// Precondition Failed: Maybe the bad etag is in the database, we need to clear the
|
||||||
// parent folder etag so we won't read from DB next sync.
|
// parent folder etag so we won't read from DB next sync.
|
||||||
_propagator->_journal->avoidReadFromDbOnNextSync(_item._file);
|
_propagator->_journal->avoidReadFromDbOnNextSync(_item._file);
|
||||||
|
_propagator->_anotherSyncNeeded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
done(classifyError(err, _item._httpErrorCode), errorString);
|
done(classifyError(err, _item._httpErrorCode), errorString);
|
||||||
|
@ -448,6 +449,12 @@ GETFileJob::GETFileJob(Account* account, const QUrl& url, QFile *device,
|
||||||
|
|
||||||
|
|
||||||
void GETFileJob::start() {
|
void GETFileJob::start() {
|
||||||
|
if (_resumeStart > 0) {
|
||||||
|
_headers["Range"] = "bytes=" + QByteArray::number(_resumeStart) +'-';
|
||||||
|
_headers["Accept-Ranges"] = "bytes";
|
||||||
|
qDebug() << "Retry with range " << _headers["Range"];
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkRequest req;
|
QNetworkRequest req;
|
||||||
for(QMap<QByteArray, QByteArray>::const_iterator it = _headers.begin(); it != _headers.end(); ++it) {
|
for(QMap<QByteArray, QByteArray>::const_iterator it = _headers.begin(); it != _headers.end(); ++it) {
|
||||||
req.setRawHeader(it.key(), it.value());
|
req.setRawHeader(it.key(), it.value());
|
||||||
|
@ -469,15 +476,22 @@ void GETFileJob::start() {
|
||||||
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(slotMetaDataChanged()));
|
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(slotMetaDataChanged()));
|
||||||
connect(reply(), SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
|
connect(reply(), SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
|
||||||
connect(reply(), SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));
|
connect(reply(), SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));
|
||||||
|
connect(this, SIGNAL(networkActivity()), account(), SIGNAL(propagatorNetworkActivity()));
|
||||||
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GETFileJob::slotMetaDataChanged()
|
void GETFileJob::slotMetaDataChanged()
|
||||||
{
|
{
|
||||||
if (reply()->error() != QNetworkReply::NoError
|
int httpStatus = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|| reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 != 2) {
|
|
||||||
// We will handle the error when the job is finished.
|
// If the status code isn't 2xx, don't write the reply body to the file.
|
||||||
|
// For any error: handle it when the job is finished, not here.
|
||||||
|
if (httpStatus / 100 != 2) {
|
||||||
|
_device->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (reply()->error() != QNetworkReply::NoError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_etag = get_etag_from_reply(reply());
|
_etag = get_etag_from_reply(reply());
|
||||||
|
@ -547,16 +561,17 @@ void GETFileJob::slotReadyRead()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 w = _device->write(buffer.constData(), r);
|
if (_device->isOpen()) {
|
||||||
if (w != r) {
|
qint64 w = _device->write(buffer.constData(), r);
|
||||||
_errorString = _device->errorString();
|
if (w != r) {
|
||||||
_errorStatus = SyncFileItem::NormalError;
|
_errorString = _device->errorString();
|
||||||
qDebug() << "Error while writing to file" << w << r << _errorString;
|
_errorStatus = SyncFileItem::NormalError;
|
||||||
reply()->abort();
|
qDebug() << "Error while writing to file" << w << r << _errorString;
|
||||||
return;
|
reply()->abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resetTimeout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GETFileJob::slotTimeout()
|
void GETFileJob::slotTimeout()
|
||||||
|
@ -626,18 +641,13 @@ void PropagateDownloadFileQNAM::start()
|
||||||
|
|
||||||
QMap<QByteArray, QByteArray> headers;
|
QMap<QByteArray, QByteArray> headers;
|
||||||
|
|
||||||
quint64 startSize = 0;
|
quint64 startSize = _tmpFile.size();
|
||||||
if (_tmpFile.size() > 0) {
|
if (startSize > 0) {
|
||||||
quint64 done = _tmpFile.size();
|
if (startSize == _item._size) {
|
||||||
if (done == _item._size) {
|
|
||||||
qDebug() << "File is already complete, no need to download";
|
qDebug() << "File is already complete, no need to download";
|
||||||
downloadFinished();
|
downloadFinished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
headers["Range"] = "bytes=" + QByteArray::number(done) +'-';
|
|
||||||
headers["Accept-Ranges"] = "bytes";
|
|
||||||
qDebug() << "Retry with range " << headers["Range"];
|
|
||||||
startSize = done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_item._directDownloadUrl.isEmpty()) {
|
if (_item._directDownloadUrl.isEmpty()) {
|
||||||
|
@ -647,14 +657,27 @@ void PropagateDownloadFileQNAM::start()
|
||||||
&_tmpFile, headers, expectedEtagForResume, startSize);
|
&_tmpFile, headers, expectedEtagForResume, startSize);
|
||||||
} else {
|
} else {
|
||||||
// We were provided a direct URL, use that one
|
// We were provided a direct URL, use that one
|
||||||
|
qDebug() << Q_FUNC_INFO << "directDownloadUrl given for " << _item._file << _item._directDownloadUrl;
|
||||||
|
|
||||||
|
// Direct URLs don't support resuming, so clear an existing tmp file
|
||||||
|
if (startSize > 0) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "resuming not supported for directDownloadUrl, deleting temporary";
|
||||||
|
_tmpFile.close();
|
||||||
|
if (!_tmpFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) {
|
||||||
|
done(SyncFileItem::NormalError, _tmpFile.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_item._directDownloadCookies.isEmpty()) {
|
if (!_item._directDownloadCookies.isEmpty()) {
|
||||||
headers["Cookie"] = _item._directDownloadCookies.toUtf8();
|
headers["Cookie"] = _item._directDownloadCookies.toUtf8();
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl url = QUrl::fromUserInput(_item._directDownloadUrl);
|
QUrl url = QUrl::fromUserInput(_item._directDownloadUrl);
|
||||||
_job = new GETFileJob(AccountManager::instance()->account(),
|
_job = new GETFileJob(AccountManager::instance()->account(),
|
||||||
url,
|
url,
|
||||||
&_tmpFile, headers);
|
&_tmpFile, headers);
|
||||||
qDebug() << Q_FUNC_INFO << "directDownloadUrl given for " << _item._file << _item._directDownloadUrl;
|
|
||||||
}
|
}
|
||||||
_job->setTimeout(_propagator->httpTimeout() * 1000);
|
_job->setTimeout(_propagator->httpTimeout() * 1000);
|
||||||
connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotGetFinished()));
|
connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotGetFinished()));
|
||||||
|
@ -677,18 +700,34 @@ void PropagateDownloadFileQNAM::slotGetFinished()
|
||||||
|
|
||||||
QNetworkReply::NetworkError err = job->reply()->error();
|
QNetworkReply::NetworkError err = job->reply()->error();
|
||||||
if (err != QNetworkReply::NoError) {
|
if (err != QNetworkReply::NoError) {
|
||||||
if (_tmpFile.size() == 0) {
|
_item._httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
// don't keep the temporary file if it is empty.
|
|
||||||
|
// If we sent a 'Range' header and get 416 back, we want to retry
|
||||||
|
// without the header.
|
||||||
|
bool badRangeHeader = job->resumeStart() > 0 && _item._httpErrorCode == 416;
|
||||||
|
if (badRangeHeader) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "server replied 416 to our range request, trying again without";
|
||||||
|
_propagator->_anotherSyncNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't keep the temporary file if it is empty or we
|
||||||
|
// used a bad range header.
|
||||||
|
if (_tmpFile.size() == 0 || badRangeHeader) {
|
||||||
_tmpFile.close();
|
_tmpFile.close();
|
||||||
_tmpFile.remove();
|
_tmpFile.remove();
|
||||||
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
|
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
|
||||||
}
|
}
|
||||||
_item._httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
_propagator->_activeJobs--;
|
_propagator->_activeJobs--;
|
||||||
SyncFileItem::Status status = job->errorStatus();
|
SyncFileItem::Status status = job->errorStatus();
|
||||||
if (status == SyncFileItem::NoStatus) {
|
if (status == SyncFileItem::NoStatus) {
|
||||||
status = classifyError(err, _item._httpErrorCode);
|
status = classifyError(err, _item._httpErrorCode);
|
||||||
}
|
}
|
||||||
|
if (badRangeHeader) {
|
||||||
|
// Can't do this in classifyError() because 416 without a
|
||||||
|
// Range header should result in NormalError.
|
||||||
|
status = SyncFileItem::SoftError;
|
||||||
|
}
|
||||||
done(status, job->errorString());
|
done(status, job->errorString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -759,6 +798,7 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
existingFile.refresh();
|
||||||
// Maybe we downloaded a newer version of the file than we thought we would...
|
// Maybe we downloaded a newer version of the file than we thought we would...
|
||||||
// Get up to date information for the journal.
|
// Get up to date information for the journal.
|
||||||
FileSystem::setModTime(fn, _item._modtime);
|
FileSystem::setModTime(fn, _item._modtime);
|
||||||
|
|
|
@ -247,7 +247,11 @@ void PropagateLocalRename::start()
|
||||||
qDebug() << "MOVE " << _propagator->_localDir + _item._file << " => " << _propagator->_localDir + _item._renameTarget;
|
qDebug() << "MOVE " << _propagator->_localDir + _item._file << " => " << _propagator->_localDir + _item._renameTarget;
|
||||||
QFile file(_propagator->_localDir + _item._file);
|
QFile file(_propagator->_localDir + _item._file);
|
||||||
|
|
||||||
if (_propagator->localFileNameClash(_item._renameTarget)) {
|
if (QString::compare(_item._file, _item._renameTarget, Qt::CaseInsensitive) != 0
|
||||||
|
&& _propagator->localFileNameClash(_item._renameTarget)) {
|
||||||
|
// Only use localFileNameClash for the destination if we know that the source was not
|
||||||
|
// the one conflicting (renaming A.txt -> a.txt is OK)
|
||||||
|
|
||||||
// Fixme: the file that is the reason for the clash could be named here,
|
// Fixme: the file that is the reason for the clash could be named here,
|
||||||
// it would have to come out the localFileNameClash function
|
// it would have to come out the localFileNameClash function
|
||||||
done(SyncFileItem::NormalError, tr( "File %1 can not be renamed to %2 because of a local file name clash")
|
done(SyncFileItem::NormalError, tr( "File %1 can not be renamed to %2 because of a local file name clash")
|
||||||
|
@ -311,9 +315,6 @@ void PropagateRemoteRename::start()
|
||||||
if (updateErrorFromSession(rc)) {
|
if (updateErrorFromSession(rc)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!updateMTimeAndETag(uri2.data(), _item._modtime))
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Wed, 15 Nov 1995 06:25:24 GMT
|
// Wed, 15 Nov 1995 06:25:24 GMT
|
||||||
QDateTime dt = QDateTime::currentDateTimeUtc();
|
QDateTime dt = QDateTime::currentDateTimeUtc();
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "discoveryphase.h"
|
#include "discoveryphase.h"
|
||||||
#include "creds/abstractcredentials.h"
|
#include "creds/abstractcredentials.h"
|
||||||
#include "csync_util.h"
|
#include "csync_util.h"
|
||||||
|
#include "syncfilestatus.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -196,12 +198,12 @@ bool SyncEngine::checkBlacklisting( SyncFileItem *item )
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncJournalBlacklistRecord entry = _journal->blacklistEntry(item->_file);
|
SyncJournalBlacklistRecord entry = _journal->blacklistEntry(item->_file);
|
||||||
item->_blacklistedInDb = false;
|
item->_hasBlacklistEntry = false;
|
||||||
|
|
||||||
// if there is a valid entry in the blacklist table and the retry count is
|
// if there is a valid entry in the blacklist table and the retry count is
|
||||||
// already null or smaller than 0, the file is blacklisted.
|
// already null or smaller than 0, the file is blacklisted.
|
||||||
if( entry.isValid() ) {
|
if( entry.isValid() ) {
|
||||||
item->_blacklistedInDb = true;
|
item->_hasBlacklistEntry = true;
|
||||||
|
|
||||||
if( entry._retryCount <= 0 ) {
|
if( entry._retryCount <= 0 ) {
|
||||||
re = true;
|
re = true;
|
||||||
|
@ -288,7 +290,7 @@ void SyncEngine::deleteStaleBlacklistEntries()
|
||||||
// Find all blacklisted paths that we want to preserve.
|
// Find all blacklisted paths that we want to preserve.
|
||||||
QSet<QString> blacklist_file_paths;
|
QSet<QString> blacklist_file_paths;
|
||||||
foreach(const SyncFileItem& it, _syncedItems) {
|
foreach(const SyncFileItem& it, _syncedItems) {
|
||||||
if (it._status == SyncFileItem::FileIgnored)
|
if (it._hasBlacklistEntry)
|
||||||
blacklist_file_paths.insert(it._file);
|
blacklist_file_paths.insert(it._file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +403,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||||
int re = 0;
|
int re = 0;
|
||||||
switch(file->instruction) {
|
switch(file->instruction) {
|
||||||
case CSYNC_INSTRUCTION_NONE:
|
case CSYNC_INSTRUCTION_NONE:
|
||||||
if (file->should_update_etag && !item._isDirectory) {
|
if (remote && item._should_update_etag && !item._isDirectory && item._instruction == CSYNC_INSTRUCTION_NONE) {
|
||||||
// Update the database now already (new fileid or etag or remotePerm)
|
// Update the database now already (new fileid or etag or remotePerm)
|
||||||
// Those are files that were detected as "resolved conflict".
|
// Those are files that were detected as "resolved conflict".
|
||||||
// They should have been a conflict because they both were new, or both
|
// They should have been a conflict because they both were new, or both
|
||||||
|
@ -523,37 +525,41 @@ void SyncEngine::startSync()
|
||||||
|
|
||||||
csync_resume(_csync_ctx);
|
csync_resume(_csync_ctx);
|
||||||
|
|
||||||
|
int fileRecordCount = -1;
|
||||||
if (!_journal->exists()) {
|
if (!_journal->exists()) {
|
||||||
qDebug() << "=====sync looks new (no DB exists), activating recursive PROPFIND if csync supports it";
|
qDebug() << "=====sync looks new (no DB exists)";
|
||||||
|
} else {
|
||||||
|
qDebug() << "=====sync with existing DB";
|
||||||
|
}
|
||||||
|
|
||||||
|
fileRecordCount = _journal->getFileRecordCount(); // this creates the DB if it does not exist yet
|
||||||
|
bool isUpdateFrom_1_5 = _journal->isUpdateFrom_1_5();
|
||||||
|
|
||||||
|
if( fileRecordCount == -1 ) {
|
||||||
|
qDebug() << "No way to create a sync journal!";
|
||||||
|
emit csyncError(tr("Unable to initialize a sync journal."));
|
||||||
|
finalize();
|
||||||
|
return;
|
||||||
|
// database creation error!
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileRecordCount >= 1 && isUpdateFrom_1_5) {
|
||||||
|
qDebug() << "detected update from 1.5" << fileRecordCount << isUpdateFrom_1_5;
|
||||||
|
// Disable the read from DB to be sure to re-read all the fileid and etags.
|
||||||
|
csync_set_read_from_db(_csync_ctx, false);
|
||||||
|
} else {
|
||||||
|
csync_set_read_from_db(_csync_ctx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool usingSelectiveSync = (!_selectiveSyncBlackList.isEmpty());
|
||||||
|
qDebug() << (usingSelectiveSync ? "====Using Selective Sync" : "====NOT Using Selective Sync");
|
||||||
|
if (fileRecordCount >= 0 && fileRecordCount < 50 && !usingSelectiveSync) {
|
||||||
|
qDebug() << "===== Activating recursive PROPFIND (currently" << fileRecordCount << "file records)";
|
||||||
bool no_recursive_propfind = false;
|
bool no_recursive_propfind = false;
|
||||||
csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind);
|
csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind);
|
||||||
} else {
|
} else {
|
||||||
// retrieve the file count from the db and close it afterwards because
|
bool no_recursive_propfind = true;
|
||||||
// csync_update also opens the database.
|
csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind);
|
||||||
int fileRecordCount = 0;
|
|
||||||
fileRecordCount = _journal->getFileRecordCount();
|
|
||||||
bool isUpdateFrom_1_5 = _journal->isUpdateFrom_1_5();
|
|
||||||
_journal->close();
|
|
||||||
|
|
||||||
if( fileRecordCount == -1 ) {
|
|
||||||
qDebug() << "No way to create a sync journal!";
|
|
||||||
emit csyncError(tr("Unable to initialize a sync journal."));
|
|
||||||
finalize();
|
|
||||||
return;
|
|
||||||
// database creation error!
|
|
||||||
} else if ( fileRecordCount < 50 ) {
|
|
||||||
qDebug() << "=====sync DB has only" << fileRecordCount << "items, enable recursive PROPFIND if csync supports it";
|
|
||||||
bool no_recursive_propfind = false;
|
|
||||||
csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind);
|
|
||||||
} else {
|
|
||||||
qDebug() << "=====sync with existing DB";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileRecordCount > 1 && isUpdateFrom_1_5) {
|
|
||||||
qDebug() << "detected update from 1.5";
|
|
||||||
// Disable the read from DB to be sure to re-read all the fileid and etags.
|
|
||||||
csync_set_read_from_db(_csync_ctx, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
csync_set_userdata(_csync_ctx, this);
|
csync_set_userdata(_csync_ctx, this);
|
||||||
|
@ -609,6 +615,17 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||||
}
|
}
|
||||||
qDebug() << "<<#### Discovery end #################################################### " << _stopWatch.addLapTime(QLatin1String("Discovery Finished"));
|
qDebug() << "<<#### Discovery end #################################################### " << _stopWatch.addLapTime(QLatin1String("Discovery Finished"));
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!_journal->isConnected()) {
|
||||||
|
qDebug() << "Bailing out, DB failure";
|
||||||
|
emit csyncError(tr("Cannot open the sync journal"));
|
||||||
|
finalize();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Commits a possibly existing (should not though) transaction and starts a new one for the propagate phase
|
||||||
|
_journal->commitIfNeededAndStartNewTransaction("Post discovery");
|
||||||
|
}
|
||||||
|
|
||||||
if( csync_reconcile(_csync_ctx) < 0 ) {
|
if( csync_reconcile(_csync_ctx) < 0 ) {
|
||||||
handleSyncError(_csync_ctx, "csync_reconcile");
|
handleSyncError(_csync_ctx, "csync_reconcile");
|
||||||
return;
|
return;
|
||||||
|
@ -646,17 +663,11 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||||
// make sure everything is allowed
|
// make sure everything is allowed
|
||||||
checkForPermission();
|
checkForPermission();
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if (!_journal->isConnected()) {
|
|
||||||
qDebug() << "Bailing out, DB failure";
|
|
||||||
emit csyncError(tr("Cannot open the sync journal"));
|
|
||||||
finalize();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// To announce the beginning of the sync
|
// To announce the beginning of the sync
|
||||||
emit aboutToPropagate(_syncedItems);
|
emit aboutToPropagate(_syncedItems);
|
||||||
|
_progressInfo._completedFileCount = ULLONG_MAX; // indicate the start with max
|
||||||
emit transmissionProgress(_progressInfo);
|
emit transmissionProgress(_progressInfo);
|
||||||
|
_progressInfo._completedFileCount = 0;
|
||||||
|
|
||||||
if (!_hasNoneFiles && _hasRemoveFile) {
|
if (!_hasNoneFiles && _hasRemoveFile) {
|
||||||
qDebug() << Q_FUNC_INFO << "All the files are going to be changed, asking the user";
|
qDebug() << Q_FUNC_INFO << "All the files are going to be changed, asking the user";
|
||||||
|
@ -678,14 +689,16 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||||
Q_ASSERT(session);
|
Q_ASSERT(session);
|
||||||
|
|
||||||
// post update phase script: allow to tweak stuff by a custom script in debug mode.
|
// post update phase script: allow to tweak stuff by a custom script in debug mode.
|
||||||
#ifndef NDEBUG
|
|
||||||
if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) {
|
if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) {
|
||||||
|
#ifndef NDEBUG
|
||||||
QString script = qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT");
|
QString script = qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT");
|
||||||
|
|
||||||
qDebug() << "OOO => Post Update Script: " << script;
|
qDebug() << "OOO => Post Update Script: " << script;
|
||||||
QProcess::execute(script.toUtf8());
|
QProcess::execute(script.toUtf8());
|
||||||
}
|
#else
|
||||||
|
qDebug() << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG";
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// do a database commit
|
// do a database commit
|
||||||
_journal->commit("post treewalk");
|
_journal->commit("post treewalk");
|
||||||
|
@ -817,6 +830,11 @@ QString SyncEngine::adjustRenamedPath(const QString& original)
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Make sure that we are allowed to do what we do by checking the permissions and the selective sync list
|
||||||
|
*
|
||||||
|
*/
|
||||||
void SyncEngine::checkForPermission()
|
void SyncEngine::checkForPermission()
|
||||||
{
|
{
|
||||||
for (SyncFileItemVector::iterator it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
|
for (SyncFileItemVector::iterator it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
|
||||||
|
@ -826,6 +844,25 @@ void SyncEngine::checkForPermission()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not propagate anything in the server if it is in the selective sync blacklist
|
||||||
|
const QString path = it->destination() + QLatin1Char('/');
|
||||||
|
if (std::binary_search(_selectiveSyncBlackList.constBegin(), _selectiveSyncBlackList.constEnd(),
|
||||||
|
path)) {
|
||||||
|
it->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||||
|
it->_status = SyncFileItem::FileIgnored;
|
||||||
|
it->_errorString = tr("Ignored because of the \"choose what to sync\" blacklist");
|
||||||
|
|
||||||
|
if (it->_isDirectory) {
|
||||||
|
for (SyncFileItemVector::iterator it_next = it + 1; it_next != _syncedItems.end() && it_next->_file.startsWith(path); ++it_next) {
|
||||||
|
it = it_next;
|
||||||
|
it->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||||
|
it->_status = SyncFileItem::FileIgnored;
|
||||||
|
it->_errorString = tr("Ignored because of the \"choose what to sync\" blacklist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch(it->_instruction) {
|
switch(it->_instruction) {
|
||||||
case CSYNC_INSTRUCTION_NEW: {
|
case CSYNC_INSTRUCTION_NEW: {
|
||||||
int slashPos = it->_file.lastIndexOf('/');
|
int slashPos = it->_file.lastIndexOf('/');
|
||||||
|
@ -840,7 +877,6 @@ void SyncEngine::checkForPermission()
|
||||||
it->_status = SyncFileItem::NormalError;
|
it->_status = SyncFileItem::NormalError;
|
||||||
it->_errorString = tr("Not allowed because you don't have permission to add sub-directories in that directory");
|
it->_errorString = tr("Not allowed because you don't have permission to add sub-directories in that directory");
|
||||||
|
|
||||||
const QString path = it->_file + QLatin1Char('/');
|
|
||||||
for (SyncFileItemVector::iterator it_next = it + 1; it_next != _syncedItems.end() && it_next->_file.startsWith(path); ++it_next) {
|
for (SyncFileItemVector::iterator it_next = it + 1; it_next != _syncedItems.end() && it_next->_file.startsWith(path); ++it_next) {
|
||||||
it = it_next;
|
it = it_next;
|
||||||
it->_instruction = CSYNC_INSTRUCTION_ERROR;
|
it->_instruction = CSYNC_INSTRUCTION_ERROR;
|
||||||
|
@ -882,7 +918,8 @@ void SyncEngine::checkForPermission()
|
||||||
if (perms.isNull()) {
|
if (perms.isNull()) {
|
||||||
// No permissions set
|
// No permissions set
|
||||||
break;
|
break;
|
||||||
} if (!perms.contains("D")) {
|
}
|
||||||
|
if (!perms.contains("D")) {
|
||||||
qDebug() << "checkForPermission: RESTORING" << it->_file;
|
qDebug() << "checkForPermission: RESTORING" << it->_file;
|
||||||
it->_should_update_etag = true;
|
it->_should_update_etag = true;
|
||||||
it->_instruction = CSYNC_INSTRUCTION_NEW;
|
it->_instruction = CSYNC_INSTRUCTION_NEW;
|
||||||
|
@ -892,7 +929,6 @@ void SyncEngine::checkForPermission()
|
||||||
|
|
||||||
if (it->_isDirectory) {
|
if (it->_isDirectory) {
|
||||||
// restore all sub items
|
// restore all sub items
|
||||||
const QString path = it->_file + QLatin1Char('/');
|
|
||||||
for (SyncFileItemVector::iterator it_next = it + 1;
|
for (SyncFileItemVector::iterator it_next = it + 1;
|
||||||
it_next != _syncedItems.end() && it_next->_file.startsWith(path); ++it_next) {
|
it_next != _syncedItems.end() && it_next->_file.startsWith(path); ++it_next) {
|
||||||
it = it_next;
|
it = it_next;
|
||||||
|
@ -912,6 +948,26 @@ void SyncEngine::checkForPermission()
|
||||||
it->_errorString = tr("Not allowed to remove, restoring");
|
it->_errorString = tr("Not allowed to remove, restoring");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if(perms.contains("S") && perms.contains("D")) {
|
||||||
|
// this is a top level shared dir which can be removed to unshare it,
|
||||||
|
// regardless if it is a read only share or not.
|
||||||
|
// To avoid that we try to restore files underneath this dir which have
|
||||||
|
// not delete permission we fast forward the iterator and leave the
|
||||||
|
// delete jobs intact. It is not physically tried to remove this files
|
||||||
|
// underneath, propagator sees that.
|
||||||
|
if( it->_isDirectory ) {
|
||||||
|
SyncFileItemVector::iterator it_prev = it - 1;
|
||||||
|
|
||||||
|
// put a more descriptive message if really a top level share dir is removed.
|
||||||
|
if( it_prev != _syncedItems.begin() && !(path.startsWith(it_prev->_file)) ) {
|
||||||
|
it->_errorString = tr("Local files and share folder removed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SyncFileItemVector::iterator it_next = it + 1;
|
||||||
|
it_next != _syncedItems.end() && it_next->_file.startsWith(path); ++it_next) {
|
||||||
|
it = it_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -983,7 +1039,6 @@ void SyncEngine::checkForPermission()
|
||||||
|
|
||||||
|
|
||||||
if (it->_isDirectory) {
|
if (it->_isDirectory) {
|
||||||
const QString path = it->_renameTarget + QLatin1Char('/');
|
|
||||||
for (SyncFileItemVector::iterator it_next = it + 1;
|
for (SyncFileItemVector::iterator it_next = it + 1;
|
||||||
it_next != _syncedItems.end() && it_next->destination().startsWith(path); ++it_next) {
|
it_next != _syncedItems.end() && it_next->destination().startsWith(path); ++it_next) {
|
||||||
it = it_next;
|
it = it_next;
|
||||||
|
@ -1014,6 +1069,29 @@ QByteArray SyncEngine::getPermissions(const QString& file) const
|
||||||
return _remotePerms.value(file);
|
return _remotePerms.value(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncEngine::setSelectiveSyncBlackList(const QStringList& list)
|
||||||
|
{
|
||||||
|
_selectiveSyncBlackList = list;
|
||||||
|
for (int i = 0; i < _selectiveSyncBlackList.count(); ++i) {
|
||||||
|
if (!_selectiveSyncBlackList.at(i).endsWith(QLatin1Char('/'))) {
|
||||||
|
_selectiveSyncBlackList[i].append(QLatin1Char('/'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyncEngine::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
|
||||||
|
{
|
||||||
|
Q_UNUSED(t);
|
||||||
|
Q_FOREACH(const SyncFileItem &item, _syncedItems) {
|
||||||
|
//qDebug() << Q_FUNC_INFO << fn << item._file << fn.startsWith(item._file) << item._file.startsWith(fn);
|
||||||
|
if (item._file.startsWith(fn)) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Setting" << fn << " to STATUS_EVAL";
|
||||||
|
s->set(SyncFileStatus::STATUS_EVAL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void SyncEngine::abort()
|
void SyncEngine::abort()
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "syncfileitem.h"
|
#include "syncfileitem.h"
|
||||||
#include "progressdispatcher.h"
|
#include "progressdispatcher.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
#include "syncfilestatus.h"
|
||||||
|
|
||||||
class QProcess;
|
class QProcess;
|
||||||
|
|
||||||
|
@ -61,12 +62,13 @@ public:
|
||||||
|
|
||||||
Utility::StopWatch &stopWatch() { return _stopWatch; }
|
Utility::StopWatch &stopWatch() { return _stopWatch; }
|
||||||
|
|
||||||
void setSelectiveSyncBlackList(const QStringList &list)
|
void setSelectiveSyncBlackList(const QStringList &list);
|
||||||
{ _selectiveSyncBlackList = list; }
|
|
||||||
|
|
||||||
/* Return true if we detected that another sync is needed to complete the sync */
|
/* Return true if we detected that another sync is needed to complete the sync */
|
||||||
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
|
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
|
||||||
|
|
||||||
|
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void csyncError( const QString& );
|
void csyncError( const QString& );
|
||||||
void csyncUnavailable();
|
void csyncUnavailable();
|
||||||
|
@ -124,7 +126,11 @@ private:
|
||||||
void finalize();
|
void finalize();
|
||||||
|
|
||||||
static bool _syncRunning; //true when one sync is running somewhere (for debugging)
|
static bool _syncRunning; //true when one sync is running somewhere (for debugging)
|
||||||
|
|
||||||
QMap<QString, SyncFileItem> _syncItemMap;
|
QMap<QString, SyncFileItem> _syncItemMap;
|
||||||
|
|
||||||
|
// should be called _syncItems (present tense). It's the items from the _syncItemMap but
|
||||||
|
// sorted and re-adjusted based on permissions.
|
||||||
SyncFileItemVector _syncedItems;
|
SyncFileItemVector _syncedItems;
|
||||||
|
|
||||||
CSYNC *_csync_ctx;
|
CSYNC *_csync_ctx;
|
||||||
|
|
|
@ -46,13 +46,13 @@ public:
|
||||||
|
|
||||||
Success, ///< The file was properly synced
|
Success, ///< The file was properly synced
|
||||||
Conflict, ///< The file was properly synced, but a conflict was created
|
Conflict, ///< The file was properly synced, but a conflict was created
|
||||||
FileIgnored, ///< The file is in the ignored list
|
FileIgnored, ///< The file is in the ignored list (or blacklisted with no retries left)
|
||||||
Restoration ///< The file was restored because what should have been done was not allowed
|
Restoration ///< The file was restored because what should have been done was not allowed
|
||||||
};
|
};
|
||||||
|
|
||||||
SyncFileItem() : _type(UnknownType), _direction(None), _isDirectory(false),
|
SyncFileItem() : _type(UnknownType), _direction(None), _isDirectory(false),
|
||||||
_instruction(CSYNC_INSTRUCTION_NONE), _modtime(0),
|
_instruction(CSYNC_INSTRUCTION_NONE), _modtime(0),
|
||||||
_size(0), _inode(0), _should_update_etag(false), _blacklistedInDb(false),
|
_size(0), _inode(0), _should_update_etag(false), _hasBlacklistEntry(false),
|
||||||
_status(NoStatus), _httpErrorCode(0), _requestDuration(0), _isRestoration(false) {}
|
_status(NoStatus), _httpErrorCode(0), _requestDuration(0), _isRestoration(false) {}
|
||||||
|
|
||||||
friend bool operator==(const SyncFileItem& item1, const SyncFileItem& item2) {
|
friend bool operator==(const SyncFileItem& item1, const SyncFileItem& item2) {
|
||||||
|
@ -72,6 +72,13 @@ public:
|
||||||
return _file.isEmpty();
|
return _file.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasErrorStatus() const {
|
||||||
|
return _status == SyncFileItem::SoftError
|
||||||
|
|| _status == SyncFileItem::NormalError
|
||||||
|
|| _status == SyncFileItem::FatalError
|
||||||
|
|| !_errorString.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
// Variables usefull for everybody
|
// Variables usefull for everybody
|
||||||
QString _file;
|
QString _file;
|
||||||
QString _renameTarget;
|
QString _renameTarget;
|
||||||
|
@ -91,7 +98,11 @@ public:
|
||||||
QByteArray _remotePerm;
|
QByteArray _remotePerm;
|
||||||
QString _directDownloadUrl;
|
QString _directDownloadUrl;
|
||||||
QString _directDownloadCookies;
|
QString _directDownloadCookies;
|
||||||
bool _blacklistedInDb;
|
|
||||||
|
/// Whether there's an entry in the blacklist table.
|
||||||
|
/// Note: that entry may have retries left, so this can be true
|
||||||
|
/// without the status being FileIgnored.
|
||||||
|
bool _hasBlacklistEntry;
|
||||||
|
|
||||||
// Variables usefull to report to the user
|
// Variables usefull to report to the user
|
||||||
Status _status;
|
Status _status;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,11 +17,10 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <qmutex.h>
|
#include <qmutex.h>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QSqlDatabase>
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QSqlQuery>
|
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
#include "ownsql.h"
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
class SyncJournalFileRecord;
|
class SyncJournalFileRecord;
|
||||||
|
@ -43,6 +42,10 @@ public:
|
||||||
bool deleteFileRecord( const QString& filename, bool recursively = false );
|
bool deleteFileRecord( const QString& filename, bool recursively = false );
|
||||||
int getFileRecordCount();
|
int getFileRecordCount();
|
||||||
bool exists();
|
bool exists();
|
||||||
|
void walCheckpoint();
|
||||||
|
|
||||||
|
QString databaseFilePath();
|
||||||
|
static qint64 getPHash(const QString& );
|
||||||
|
|
||||||
void updateBlacklistEntry( const SyncJournalBlacklistRecord& item );
|
void updateBlacklistEntry( const SyncJournalBlacklistRecord& item );
|
||||||
void wipeBlacklistEntry(const QString& file);
|
void wipeBlacklistEntry(const QString& file);
|
||||||
|
@ -91,6 +94,7 @@ public:
|
||||||
* Commit will actually commit the transaction and create a new one.
|
* Commit will actually commit the transaction and create a new one.
|
||||||
*/
|
*/
|
||||||
void commit(const QString &context, bool startTrans = true);
|
void commit(const QString &context, bool startTrans = true);
|
||||||
|
void commitIfNeededAndStartNewTransaction(const QString &context);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
|
@ -106,31 +110,30 @@ public:
|
||||||
bool isUpdateFrom_1_5();
|
bool isUpdateFrom_1_5();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
qint64 getPHash(const QString& ) const;
|
|
||||||
bool updateDatabaseStructure();
|
bool updateDatabaseStructure();
|
||||||
bool sqlFail(const QString& log, const QSqlQuery &query );
|
bool sqlFail(const QString& log, const SqlQuery &query );
|
||||||
void commitInternal(const QString &context, bool startTrans = true);
|
void commitInternal(const QString &context, bool startTrans = true);
|
||||||
void startTransaction();
|
void startTransaction();
|
||||||
void commitTransaction();
|
void commitTransaction();
|
||||||
QStringList tableColumns( const QString& table );
|
QStringList tableColumns( const QString& table );
|
||||||
bool checkConnect();
|
bool checkConnect();
|
||||||
|
|
||||||
QSqlDatabase _db;
|
SqlDatabase _db;
|
||||||
QString _dbFile;
|
QString _dbFile;
|
||||||
QMutex _mutex; // Public functions are protected with the mutex.
|
QMutex _mutex; // Public functions are protected with the mutex.
|
||||||
int _transaction;
|
int _transaction;
|
||||||
bool _possibleUpgradeFromMirall_1_5;
|
bool _possibleUpgradeFromMirall_1_5;
|
||||||
QScopedPointer<QSqlQuery> _getFileRecordQuery;
|
QScopedPointer<SqlQuery> _getFileRecordQuery;
|
||||||
QScopedPointer<QSqlQuery> _setFileRecordQuery;
|
QScopedPointer<SqlQuery> _setFileRecordQuery;
|
||||||
QScopedPointer<QSqlQuery> _getDownloadInfoQuery;
|
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
|
||||||
QScopedPointer<QSqlQuery> _setDownloadInfoQuery;
|
QScopedPointer<SqlQuery> _setDownloadInfoQuery;
|
||||||
QScopedPointer<QSqlQuery> _deleteDownloadInfoQuery;
|
QScopedPointer<SqlQuery> _deleteDownloadInfoQuery;
|
||||||
QScopedPointer<QSqlQuery> _getUploadInfoQuery;
|
QScopedPointer<SqlQuery> _getUploadInfoQuery;
|
||||||
QScopedPointer<QSqlQuery> _setUploadInfoQuery;
|
QScopedPointer<SqlQuery> _setUploadInfoQuery;
|
||||||
QScopedPointer<QSqlQuery> _deleteUploadInfoQuery;
|
QScopedPointer<SqlQuery> _deleteUploadInfoQuery;
|
||||||
QScopedPointer<QSqlQuery> _deleteFileRecordPhash;
|
QScopedPointer<SqlQuery> _deleteFileRecordPhash;
|
||||||
QScopedPointer<QSqlQuery> _deleteFileRecordRecursively;
|
QScopedPointer<SqlQuery> _deleteFileRecordRecursively;
|
||||||
QScopedPointer<QSqlQuery> _blacklistQuery;
|
QScopedPointer<SqlQuery> _blacklistQuery;
|
||||||
|
|
||||||
/* This is the list of paths we called avoidReadFromDbOnNextSync on.
|
/* 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
|
* It means that they should not be written to the DB in any case since doing
|
||||||
|
|
|
@ -17,11 +17,13 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "owncloudlib.h"
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
class SyncFileItem;
|
class SyncFileItem;
|
||||||
|
|
||||||
class SyncJournalFileRecord
|
class OWNCLOUDSYNC_EXPORT SyncJournalFileRecord
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SyncJournalFileRecord();
|
SyncJournalFileRecord();
|
||||||
|
|
|
@ -224,6 +224,18 @@ QString Utility::toCSyncScheme(const QString &urlStr)
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Utility::doesSetContainPrefix(QSet<QString> &l, QString &p) {
|
||||||
|
|
||||||
|
Q_FOREACH (const QString &setPath, l) {
|
||||||
|
//qDebug() << Q_FUNC_INFO << p << setPath << setPath.startsWith(p);
|
||||||
|
if (setPath.startsWith(p)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//qDebug() << "-> NOOOOO!!!" << p << l.count();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QString Utility::escape(const QString &in)
|
QString Utility::escape(const QString &in)
|
||||||
{
|
{
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
@ -360,17 +372,6 @@ bool Utility::isLinux()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utility::winShellChangeNotify( const QString& path )
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT,
|
|
||||||
reinterpret_cast<const wchar_t *>(QDir::toNativeSeparators(path).utf16()), NULL );
|
|
||||||
#else
|
|
||||||
Q_UNUSED(path);
|
|
||||||
qDebug() << Q_FUNC_INFO << " is not implemented on non Windows systems.";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const char STOPWATCH_END_TAG[] = "_STOPWATCH_END";
|
static const char STOPWATCH_END_TAG[] = "_STOPWATCH_END";
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue