mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-05 22:34:18 +02:00
Compare commits
5 Commits
v1.5.0-bet
...
v1.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14a25f9d3f | ||
|
|
4e777aae33 | ||
|
|
5a84452102 | ||
|
|
8aa75ba4a4 | ||
|
|
2f24172bac |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -6,7 +6,3 @@ CMakeLists.txt.user*
|
||||
*~
|
||||
*.autosave
|
||||
doc/_build/*
|
||||
*~
|
||||
*.kate-swp
|
||||
*.kdev4
|
||||
win/
|
||||
|
||||
@@ -21,20 +21,8 @@ include(${CMAKE_SOURCE_DIR}/VERSION.cmake)
|
||||
configure_file( ${CMAKE_SOURCE_DIR}/src/mirall/version.h.in "${CMAKE_CURRENT_BINARY_DIR}/src/mirall/version.h" )
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_BINARY_DIR}/src/mirall/")
|
||||
|
||||
#####
|
||||
## handle DBUS for Fdo notifications
|
||||
if( UNIX AND NOT APPLE )
|
||||
add_definitions( -DUSE_FDO_NOTIFICATIONS)
|
||||
set(WITH_DBUS ON)
|
||||
endif()
|
||||
####
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(DefineInstallationPaths)
|
||||
include(QtVersionAbstraction)
|
||||
|
||||
setup_qt()
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
|
||||
@@ -70,14 +58,19 @@ else()
|
||||
endif()
|
||||
#####
|
||||
|
||||
#####
|
||||
## handle DBUS for Fdo notifications
|
||||
if( UNIX AND NOT APPLE )
|
||||
add_definitions( -DUSE_FDO_NOTIFICATIONS)
|
||||
endif()
|
||||
####
|
||||
|
||||
#### find libs
|
||||
#find_package(Qt4 4.7.0 COMPONENTS QtCore QtGui QtXml QtNetwork QtTest QtWebkit REQUIRED )
|
||||
#if( UNIX AND NOT APPLE ) # Fdo notifications
|
||||
# find_package(Qt4 4.7.0 COMPONENTS QtDBus REQUIRED )
|
||||
#endif()
|
||||
find_package(Neon REQUIRED)
|
||||
find_package(Qt4 4.7.0 COMPONENTS QtCore QtGui QtXml QtNetwork QtTest QtWebkit REQUIRED )
|
||||
if( UNIX AND NOT APPLE ) # Fdo notifications
|
||||
find_package(Qt4 4.7.0 COMPONENTS QtDBus REQUIRED )
|
||||
endif()
|
||||
find_package(Csync REQUIRED)
|
||||
find_package(QtKeychain REQUIRED)
|
||||
if(UNIX)
|
||||
find_package(INotify REQUIRED)
|
||||
else()
|
||||
@@ -85,6 +78,7 @@ find_package(INotify)
|
||||
endif()
|
||||
find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
find_package(QtKeychain)
|
||||
|
||||
set(WITH_QTKEYCHAIN ${QTKEYCHAIN_FOUND})
|
||||
set(USE_INOTIFY ${INOTIFY_FOUND})
|
||||
|
||||
@@ -21,9 +21,11 @@ set( CSYNC_BINARY_DIR @CSYNC_BINARY_DIR@ )
|
||||
set( MINGW_ROOT @CMAKE_FIND_ROOT_PATH@ )
|
||||
if(CSYNC_BINARY_DIR)
|
||||
set( CSYNC_LIBRARY_DIR "${CSYNC_BINARY_DIR}/src" )
|
||||
set( CSYNC_PLUGIN_DIR "${CSYNC_BINARY_DIR}/modules" )
|
||||
set( CSYNC_CONFIG_DIR "${CSYNC_BINARY_DIR}/config" )
|
||||
else()
|
||||
set( CSYNC_LIBRARY_DIR "${MINGW_ROOT}/bin" )
|
||||
set( CSYNC_PLUGIN_DIR "${MINGW_ROOT}/plugins-0" ) #FIXME: whatever it is
|
||||
set( CSYNC_CONFIG_DIR "${MINGW_ROOT}/etc/ocsync" )
|
||||
endif()
|
||||
set( BUILD_OWNCLOUD_OSX_BUNDLE @BUILD_OWNCLOUD_OSX_BUNDLE@)
|
||||
|
||||
@@ -9,11 +9,10 @@ else ()
|
||||
endif()
|
||||
|
||||
include( VERSION.cmake )
|
||||
set( CPACK_PACKAGE_VERSION_MAJOR ${MIRALL_VERSION_MAJOR} )
|
||||
set( CPACK_PACKAGE_VERSION_MINOR ${MIRALL_VERSION_MINOR} )
|
||||
set( CPACK_PACKAGE_VERSION_PATCH ${MIRALL_VERSION_PATCH} )
|
||||
set( CPACK_PACKAGE_VERSION_BUILD ${MIRALL_VERSION_BUILD} )
|
||||
set( CPACK_PACKAGE_VERSION ${MIRALL_VERSION_FULL}${MIRALL_VERSION_SUFFIX} )
|
||||
set( CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR} )
|
||||
set( CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR} )
|
||||
set( CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH} )
|
||||
set( CPACK_PACKAGE_VERSION ${VERSION} )
|
||||
|
||||
if(APPLE)
|
||||
set( CPACK_GENERATOR "DragNDrop" )
|
||||
|
||||
@@ -1,26 +1,6 @@
|
||||
set( MIRALL_VERSION_MAJOR 1 )
|
||||
set( MIRALL_VERSION_MINOR 5 )
|
||||
set( MIRALL_VERSION_PATCH 0 )
|
||||
set( MIRALL_SOVERSION 0 )
|
||||
set( VERSION_MAJOR 1 )
|
||||
set( VERSION_MINOR 4 )
|
||||
set( VERSION_PATCH 2 )
|
||||
set( VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX}")
|
||||
set( SOVERSION 0 )
|
||||
|
||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
set( MIRALL_VERSION_SUFFIX "beta3" ) #e.g. beta1, beta2, rc1
|
||||
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
|
||||
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||
set( MIRALL_VERSION_BUILD "0" ) # Integer ID. Generated by the build system
|
||||
endif( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||
|
||||
# Composite defines
|
||||
# Used e.g. for libraries Keep at x.y.z.
|
||||
set( MIRALL_VERSION "${MIRALL_VERSION_MAJOR}.${MIRALL_VERSION_MINOR}.${MIRALL_VERSION_PATCH}" )
|
||||
# Version with Build ID. Used in the installer
|
||||
set( MIRALL_VERSION_FULL ${MIRALL_VERSION} )
|
||||
set( MIRALL_VERSION_STRING ${MIRALL_VERSION} )
|
||||
set( MIRALL_VERSION_FULL "${MIRALL_VERSION_FULL}.${MIRALL_VERSION_BUILD}" )
|
||||
|
||||
set( MIRALL_VERSION_STRING "${MIRALL_VERSION}${MIRALL_VERSION_SUFFIX}" )
|
||||
|
||||
if( MIRALL_VERSION_BUILD )
|
||||
set( MIRALL_VERSION_STRING "${MIRALL_VERSION_STRING} (build ${MIRALL_VERSION_BUILD})" )
|
||||
endif( MIRALL_VERSION_BUILD )
|
||||
|
||||
@@ -5,6 +5,7 @@ This sample uses the registry plugin, so you need to download that if you don't
|
||||
|
||||
!define APPNAME "UAC_RealWorldFullyLoadedDualMode"
|
||||
!define ELEVATIONTITLE "${APPNAME}: Elevate" ;displayed during elevation on our custom page
|
||||
!define SMSUBDIR $StartMenuFolder ;"${APPNAME}"
|
||||
!define UNINSTALLER_NAME "Uninstall ${APPNAME}.exe"
|
||||
!define UNINSTALLER_REGSECTION "${APPNAME}"
|
||||
!define RegPath.MSUninstall "Software\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||
@@ -199,7 +200,10 @@ UAC::Exec "" "Notepad.exe" "$Windir\Win.INI" "$InstDir"
|
||||
FunctionEnd
|
||||
|
||||
Function CreateSMShortcuts
|
||||
CreateShortcut "$SMPrograms\${APPNAME}.lnk" "$Windir\Notepad.exe"
|
||||
StrCpy ${SMSUBDIR} $9 ;stupid sync
|
||||
CreateDirectory "$SMPrograms\${SMSUBDIR}"
|
||||
CreateShortcut "$SMPrograms\${SMSUBDIR}\${APPNAME}.lnk" "$Windir\Notepad.exe"
|
||||
CreateShortcut "$SMPrograms\${SMSUBDIR}\Uninstall ${APPNAME}.lnk" "$InstDir\${UNINSTALLER_NAME}"
|
||||
FunctionEnd
|
||||
Function CreateDeskShortcuts
|
||||
CreateShortcut "$Desktop\${APPNAME}.lnk" "$Windir\Notepad.exe"
|
||||
@@ -218,6 +222,7 @@ ${registry::Unload}
|
||||
SectionEnd
|
||||
|
||||
Section "Startmenu Shortcuts"
|
||||
StrCpy $9 ${SMSUBDIR} ;this is stupid as hell, we need correct ${SMSUBDIR} in the outer process, this is the only way (plugins cannot enum "custom" var's AFAIK)
|
||||
${UAC.CallFunctionAsUser} CreateSMShortcuts
|
||||
SectionEnd
|
||||
Section "Desktop Shortcut"
|
||||
@@ -226,7 +231,9 @@ SectionEnd
|
||||
|
||||
Section Uninstall
|
||||
Delete "$InstDir\${UNINSTALLER_NAME}"
|
||||
Delete "$SMPrograms\${APPNAME}.lnk"
|
||||
Delete "$SMPrograms\${SMSUBDIR}\${APPNAME}.lnk"
|
||||
Delete "$SMPrograms\${SMSUBDIR}\Uninstall ${APPNAME}.lnk"
|
||||
RMDir "$SMPrograms\${SMSUBDIR}"
|
||||
Delete "$Desktop\${APPNAME}.lnk"
|
||||
|
||||
RMDir "$InstDir"
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# - Try to find Neon
|
||||
# Once done this will define
|
||||
#
|
||||
# NEON_FOUND - system has Neon
|
||||
# NEON_INCLUDE_DIRS - the Neon include directory
|
||||
# NEON_LIBRARIES - Link these to use Neon
|
||||
# NEON_DEFINITIONS - Compiler switches required for using Neon
|
||||
#
|
||||
# Copyright (c) 2011 Andreas Schneider <asn@cryptomilk.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
|
||||
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_NEON neon)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
find_path(NEON_INCLUDE_DIRS
|
||||
NAMES
|
||||
neon/ne_basic.h
|
||||
HINTS
|
||||
${_NEON_INCLUDEDIR}
|
||||
${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
find_library(NEON_LIBRARIES
|
||||
NAMES
|
||||
neon
|
||||
HINTS
|
||||
${_NEON_LIBDIR}
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Neon DEFAULT_MSG NEON_LIBRARIES NEON_INCLUDE_DIRS)
|
||||
|
||||
# show the NEON_INCLUDE_DIRS and NEON_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(NEON_INCLUDE_DIRS NEON_LIBRARIES)
|
||||
|
||||
# Check if neon was compiled with LFS support, if so, the NE_LFS variable has to
|
||||
# be defined in the owncloud module.
|
||||
# If neon was not compiled with LFS its also ok since the underlying system
|
||||
# than probably supports large files anyway.
|
||||
IF( CMAKE_FIND_ROOT_PATH )
|
||||
FIND_PROGRAM( NEON_CONFIG_EXECUTABLE NAMES neon-config HINTS ${CMAKE_FIND_ROOT_PATH}/bin )
|
||||
ELSE( CMAKE_FIND_ROOT_PATH )
|
||||
FIND_PROGRAM( NEON_CONFIG_EXECUTABLE NAMES neon-config )
|
||||
ENDIF( CMAKE_FIND_ROOT_PATH )
|
||||
|
||||
IF ( NEON_CONFIG_EXECUTABLE )
|
||||
MESSAGE(STATUS "neon-config executable: ${NEON_CONFIG_EXECUTABLE}")
|
||||
# neon-config --support lfs
|
||||
EXECUTE_PROCESS( COMMAND ${NEON_CONFIG_EXECUTABLE} "--support" "lfs"
|
||||
RESULT_VARIABLE LFS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE )
|
||||
|
||||
IF (LFS EQUAL 0)
|
||||
MESSAGE(STATUS "libneon has been compiled with LFS support")
|
||||
SET(NEON_WITH_LFS 1 PARENT_SCOPE)
|
||||
ELSE (LFS EQUAL 0)
|
||||
MESSAGE(STATUS "libneon has not been compiled with LFS support, rely on OS")
|
||||
ENDIF (LFS EQUAL 0)
|
||||
ELSE ( NEON_CONFIG_EXECUTABLE )
|
||||
MESSAGE(STATUS, "neon-config could not be found.")
|
||||
ENDIF ( NEON_CONFIG_EXECUTABLE )
|
||||
@@ -17,15 +17,15 @@
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>@APPLICATION_NAME@ @MIRALL_VERSION_STRING@</string>
|
||||
<string>@APPLICATION_NAME@ @VERSION@</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>@VERSION_FULL@</string>
|
||||
<string>@VERSION@</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>@VERSION_FULL@</string>
|
||||
<string>@VERSION@</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>(C) 2012 @APPLICATION_VENDOR@</string>
|
||||
</dict>
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
# This file defines the Feature Logging macros.
|
||||
#
|
||||
# MACRO_LOG_FEATURE(VAR FEATURE DESCRIPTION URL [REQUIRED [MIN_VERSION [COMMENTS]]])
|
||||
# Logs the information so that it can be displayed at the end
|
||||
# of the configure run
|
||||
# VAR : TRUE or FALSE, indicating whether the feature is supported
|
||||
# FEATURE: name of the feature, e.g. "libjpeg"
|
||||
# DESCRIPTION: description what this feature provides
|
||||
# URL: home page
|
||||
# REQUIRED: TRUE or FALSE, indicating whether the featue is required
|
||||
# MIN_VERSION: minimum version number. empty string if unneeded
|
||||
# COMMENTS: More info you may want to provide. empty string if unnecessary
|
||||
#
|
||||
# MACRO_DISPLAY_FEATURE_LOG()
|
||||
# Call this to display the collected results.
|
||||
# Exits CMake with a FATAL error message if a required feature is missing
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# INCLUDE(MacroLogFeature)
|
||||
#
|
||||
# FIND_PACKAGE(JPEG)
|
||||
# MACRO_LOG_FEATURE(JPEG_FOUND "libjpeg" "Support JPEG images" "http://www.ijg.org" TRUE "3.2a" "")
|
||||
# ...
|
||||
# MACRO_DISPLAY_FEATURE_LOG()
|
||||
|
||||
# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
|
||||
# Copyright (c) 2006, Allen Winter, <winter@kde.org>
|
||||
# Copyright (c) 2009, Sebastian Trueg, <trueg@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
IF (NOT _macroLogFeatureAlreadyIncluded)
|
||||
SET(_file ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
|
||||
IF (EXISTS ${_file})
|
||||
FILE(REMOVE ${_file})
|
||||
ENDIF (EXISTS ${_file})
|
||||
|
||||
SET(_file ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
|
||||
IF (EXISTS ${_file})
|
||||
FILE(REMOVE ${_file})
|
||||
ENDIF (EXISTS ${_file})
|
||||
|
||||
SET(_file ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
|
||||
IF (EXISTS ${_file})
|
||||
FILE(REMOVE ${_file})
|
||||
ENDIF (EXISTS ${_file})
|
||||
|
||||
SET(_macroLogFeatureAlreadyIncluded TRUE)
|
||||
|
||||
INCLUDE(FeatureSummary)
|
||||
|
||||
ENDIF (NOT _macroLogFeatureAlreadyIncluded)
|
||||
|
||||
|
||||
MACRO(MACRO_LOG_FEATURE _var _package _description _url ) # _required _minvers _comments)
|
||||
|
||||
STRING(TOUPPER "${ARGV4}" _required)
|
||||
SET(_minvers "${ARGV5}")
|
||||
SET(_comments "${ARGV6}")
|
||||
|
||||
IF (${_var})
|
||||
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
|
||||
ELSE (${_var})
|
||||
IF ("${_required}" STREQUAL "TRUE")
|
||||
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
|
||||
ELSE ("${_required}" STREQUAL "TRUE")
|
||||
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
|
||||
ENDIF ("${_required}" STREQUAL "TRUE")
|
||||
ENDIF (${_var})
|
||||
|
||||
SET(_logtext " * ${_package}")
|
||||
|
||||
IF (NOT ${_var})
|
||||
IF (${_minvers} MATCHES ".*")
|
||||
SET(_logtext "${_logtext} (${_minvers} or higher)")
|
||||
ENDIF (${_minvers} MATCHES ".*")
|
||||
SET(_logtext "${_logtext} <${_url}>\n ")
|
||||
ELSE (NOT ${_var})
|
||||
SET(_logtext "${_logtext} - ")
|
||||
ENDIF (NOT ${_var})
|
||||
|
||||
SET(_logtext "${_logtext}${_description}")
|
||||
|
||||
IF (NOT ${_var})
|
||||
IF (${_comments} MATCHES ".*")
|
||||
SET(_logtext "${_logtext}\n ${_comments}")
|
||||
ENDIF (${_comments} MATCHES ".*")
|
||||
# SET(_logtext "${_logtext}\n") #double-space missing features?
|
||||
ENDIF (NOT ${_var})
|
||||
|
||||
FILE(APPEND "${_LOGFILENAME}" "${_logtext}\n")
|
||||
|
||||
IF(COMMAND SET_PACKAGE_INFO) # in FeatureSummary.cmake since CMake 2.8.3
|
||||
SET_PACKAGE_INFO("${_package}" "\"${_description}\"" "${_url}" "\"${_comments}\"")
|
||||
ENDIF(COMMAND SET_PACKAGE_INFO)
|
||||
|
||||
ENDMACRO(MACRO_LOG_FEATURE)
|
||||
|
||||
|
||||
MACRO(MACRO_DISPLAY_FEATURE_LOG)
|
||||
IF(COMMAND FEATURE_SUMMARY) # in FeatureSummary.cmake since CMake 2.8.3
|
||||
FEATURE_SUMMARY(FILENAME ${CMAKE_CURRENT_BINARY_DIR}/FindPackageLog.txt
|
||||
WHAT ALL)
|
||||
ENDIF(COMMAND FEATURE_SUMMARY)
|
||||
|
||||
SET(_missingFile ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
|
||||
SET(_enabledFile ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
|
||||
SET(_disabledFile ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
|
||||
|
||||
IF (EXISTS ${_missingFile} OR EXISTS ${_enabledFile} OR EXISTS ${_disabledFile})
|
||||
SET(_printSummary TRUE)
|
||||
ENDIF (EXISTS ${_missingFile} OR EXISTS ${_enabledFile} OR EXISTS ${_disabledFile})
|
||||
|
||||
IF(_printSummary)
|
||||
SET(_missingDeps 0)
|
||||
IF (EXISTS ${_enabledFile})
|
||||
FILE(READ ${_enabledFile} _enabled)
|
||||
FILE(REMOVE ${_enabledFile})
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following external packages were located on your system.\n-- This installation will have the extra features provided by these packages.\n-----------------------------------------------------------------------------\n${_enabled}")
|
||||
ENDIF (EXISTS ${_enabledFile})
|
||||
|
||||
|
||||
IF (EXISTS ${_disabledFile})
|
||||
SET(_missingDeps 1)
|
||||
FILE(READ ${_disabledFile} _disabled)
|
||||
FILE(REMOVE ${_disabledFile})
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following OPTIONAL packages could NOT be located on your system.\n-- Consider installing them to enable more features from this software.\n-----------------------------------------------------------------------------\n${_disabled}")
|
||||
ENDIF (EXISTS ${_disabledFile})
|
||||
|
||||
|
||||
IF (EXISTS ${_missingFile})
|
||||
SET(_missingDeps 1)
|
||||
FILE(READ ${_missingFile} _requirements)
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following REQUIRED packages could NOT be located on your system.\n-- You must install these packages before continuing.\n-----------------------------------------------------------------------------\n${_requirements}")
|
||||
FILE(REMOVE ${_missingFile})
|
||||
SET(_haveMissingReq 1)
|
||||
ENDIF (EXISTS ${_missingFile})
|
||||
|
||||
|
||||
IF (NOT ${_missingDeps})
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- Congratulations! All external packages have been found.")
|
||||
ENDIF (NOT ${_missingDeps})
|
||||
|
||||
|
||||
MESSAGE(${_summary})
|
||||
MESSAGE("-----------------------------------------------------------------------------\n")
|
||||
|
||||
|
||||
IF(_haveMissingReq)
|
||||
MESSAGE(FATAL_ERROR "Exiting: Missing Requirements")
|
||||
ENDIF(_haveMissingReq)
|
||||
|
||||
ENDIF(_printSummary)
|
||||
|
||||
ENDMACRO(MACRO_DISPLAY_FEATURE_LOG)
|
||||
@@ -1,47 +0,0 @@
|
||||
# - MACRO_OPTIONAL_FIND_PACKAGE() combines FIND_PACKAGE() with an OPTION()
|
||||
# MACRO_OPTIONAL_FIND_PACKAGE( <name> [QUIT] )
|
||||
# This macro is a combination of OPTION() and FIND_PACKAGE(), it
|
||||
# works like FIND_PACKAGE(), but additionally it automatically creates
|
||||
# an option name WITH_<name>, which can be disabled via the cmake GUI.
|
||||
# or via -DWITH_<name>=OFF
|
||||
# The standard <name>_FOUND variables can be used in the same way
|
||||
# as when using the normal FIND_PACKAGE()
|
||||
|
||||
# Copyright (c) 2006-2010 Alexander Neundorf, <neundorf@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
# This is just a helper macro to set a bunch of variables empty.
|
||||
# We don't know whether the package uses UPPERCASENAME or CamelCaseName, so we try both:
|
||||
macro(_MOFP_SET_EMPTY_IF_DEFINED _name _var)
|
||||
if(DEFINED ${_name}_${_var})
|
||||
set(${_name}_${_var} "")
|
||||
endif(DEFINED ${_name}_${_var})
|
||||
|
||||
string(TOUPPER ${_name} _nameUpper)
|
||||
if(DEFINED ${_nameUpper}_${_var})
|
||||
set(${_nameUpper}_${_var} "")
|
||||
endif(DEFINED ${_nameUpper}_${_var})
|
||||
endmacro(_MOFP_SET_EMPTY_IF_DEFINED _package _var)
|
||||
|
||||
|
||||
macro (MACRO_OPTIONAL_FIND_PACKAGE _name )
|
||||
option(WITH_${_name} "Search for ${_name} package" ON)
|
||||
if (WITH_${_name})
|
||||
find_package(${_name} ${ARGN})
|
||||
else (WITH_${_name})
|
||||
string(TOUPPER ${_name} _nameUpper)
|
||||
set(${_name}_FOUND FALSE)
|
||||
set(${_nameUpper}_FOUND FALSE)
|
||||
|
||||
_mofp_set_empty_if_defined(${_name} INCLUDE_DIRS)
|
||||
_mofp_set_empty_if_defined(${_name} INCLUDE_DIR)
|
||||
_mofp_set_empty_if_defined(${_name} INCLUDES)
|
||||
_mofp_set_empty_if_defined(${_name} LIBRARY)
|
||||
_mofp_set_empty_if_defined(${_name} LIBRARIES)
|
||||
_mofp_set_empty_if_defined(${_name} LIBS)
|
||||
_mofp_set_empty_if_defined(${_name} FLAGS)
|
||||
_mofp_set_empty_if_defined(${_name} DEFINITIONS)
|
||||
endif (WITH_${_name})
|
||||
endmacro (MACRO_OPTIONAL_FIND_PACKAGE)
|
||||
@@ -39,6 +39,7 @@
|
||||
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt4/plugins/imageformats"
|
||||
|
||||
!define CSYNC_LIBRARY_DIR "@CSYNC_LIBRARY_DIR@"
|
||||
!define CSYNC_PLUGIN_DIR "@CSYNC_PLUGIN_DIR@"
|
||||
!define CSYNC_CONFIG_DIR "@CSYNC_CONFIG_DIR@"
|
||||
|
||||
!define NSI_PATH "${source_path}/admin/win/nsi"
|
||||
@@ -49,8 +50,7 @@
|
||||
|
||||
!define VER_MAJOR "@CPACK_PACKAGE_VERSION_MAJOR@"
|
||||
!define VER_MINOR "@CPACK_PACKAGE_VERSION_MINOR@"
|
||||
!define VER_PATCH "@CPACK_PACKAGE_VERSION_PATCH@"
|
||||
!define VER_BUILD "@CPACK_PACKAGE_VERSION_BUILD@"
|
||||
!define VER_BUILD "@CPACK_PACKAGE_VERSION_PATCH@"
|
||||
!define VERSION "@CPACK_PACKAGE_VERSION@"
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
@@ -206,10 +206,7 @@ Function PageReinstall
|
||||
IntCmp $R0 ${VER_MAJOR} minor_check new_version older_version
|
||||
minor_check:
|
||||
ReadRegDWORD $R0 HKLM "Software\${APPLICATION_NAME}" "VersionMinor"
|
||||
IntCmp $R0 ${VER_MINOR} rev_check new_version older_version
|
||||
rev_check:
|
||||
ReadRegDWORD $R0 HKLM "Software\${APPLICATION_NAME}" "VersionRevision"
|
||||
IntCmp $R0 ${VER_PATCH} build_check new_version older_version
|
||||
IntCmp $R0 ${VER_MINOR} build_check new_version older_version
|
||||
build_check:
|
||||
ReadRegDWORD $R0 HKLM "Software\${APPLICATION_NAME}" "VersionBuild"
|
||||
IntCmp $R0 ${VER_BUILD} same_version new_version older_version
|
||||
@@ -300,9 +297,11 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
File "${IMAGEFORMATS_DLL_PATH}\qjpeg4.dll"
|
||||
File "${IMAGEFORMATS_DLL_PATH}\qico4.dll"
|
||||
|
||||
SetOutPath "$INSTDIR\sqldrivers"
|
||||
File "${SQLITE_DLL_PATH}\qsqlite4.dll"
|
||||
SetOutPath "$INSTDIR\modules"
|
||||
; FIXME: fix installation dir of module, currently needs manual copying to
|
||||
; /usr/i686-w64-mingw32/sys-root/mingw/bin/csync_modules/
|
||||
|
||||
File "${CSYNC_PLUGIN_DIR}/ocsync_owncloud.dll"
|
||||
SetOutPath "$INSTDIR"
|
||||
!endif
|
||||
|
||||
@@ -316,15 +315,16 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
File "${QT_DLL_PATH}\QtCore4.dll"
|
||||
File "${QT_DLL_PATH}\QtGui4.dll"
|
||||
File "${QT_DLL_PATH}\QtNetwork4.dll"
|
||||
File "${QT_DLL_PATH}\QtSql4.dll"
|
||||
File "${QT_DLL_PATH}\QtXml4.dll"
|
||||
File "${QT_DLL_PATH}\QtWebKit4.dll"
|
||||
|
||||
;QtKeyChain stuff
|
||||
File "${MING_BIN}\libqtkeychain.dll"
|
||||
|
||||
File "${CSYNC_LIBRARY_DIR}\libocsync.dll"
|
||||
File "${CSYNC_LIBRARY_DIR}/libocsync.dll"
|
||||
File "${MING_BIN}\libsqlite3-0.dll"
|
||||
File "${MING_BIN}\libiniparser.dll"
|
||||
File "${MING_BIN}\libdl.dll"
|
||||
File "${MING_BIN}\libpng15-15.dll"
|
||||
|
||||
; ownCloud plugin
|
||||
@@ -353,13 +353,18 @@ SectionEnd
|
||||
SectionGroup "Shortcuts"
|
||||
|
||||
!ifdef OPTION_SECTION_SC_START_MENU
|
||||
${MementoSection} "Start Menu Program Shortcut" SEC_START_MENU
|
||||
${MementoSection} "Start Menu Program Group" SEC_START_MENU
|
||||
SectionIn 1 2 3
|
||||
SetDetailsPrint textonly
|
||||
DetailPrint "Adding shortcut for ${APPLICATION_NAME} to the Start Menu."
|
||||
DetailPrint "Adding shortcuts for the ${APPLICATION_NAME} program group to the Start Menu."
|
||||
SetDetailsPrint listonly
|
||||
SetShellVarContext all
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_EXECUTABLE}"
|
||||
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
CreateDirectory "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
;CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\LICENSE.lnk" "$INSTDIR\LICENSE.txt"
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_EXECUTABLE}"
|
||||
;CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Release notes.lnk" "$INSTDIR\NOTES.txt"
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
SetShellVarContext current
|
||||
${MementoSectionEnd}
|
||||
!endif
|
||||
@@ -392,7 +397,7 @@ ${MementoSectionDone}
|
||||
;--------------------------------
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APPLICATION} "${APPLICATION_NAME} essentials."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "${APPLICATION_NAME} shortcut."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "${APPLICATION_NAME} program group."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_DESKTOP} "Desktop shortcut for ${APPLICATION_NAME}."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_QUICK_LAUNCH} "Quick Launch shortcut for ${APPLICATION_NAME}."
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
@@ -414,7 +419,7 @@ Section -post
|
||||
WriteRegStr HKLM "Software\${APPLICATION_NAME}" "" $INSTDIR
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionMajor" "${VER_MAJOR}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionMinor" "${VER_MINOR}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionRevision" "${VER_PATCH}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionRevision" "${REVISION}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionBuild" "${VER_BUILD}"
|
||||
|
||||
;Add or Remove Programs entry.
|
||||
@@ -500,7 +505,7 @@ Section Uninstall
|
||||
;Start menu shortcuts.
|
||||
!ifdef OPTION_SECTION_SC_START_MENU
|
||||
SetShellVarContext all
|
||||
Delete "$SMPROGRAMS\${APPLICATION_NAME}.lnk"
|
||||
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
SetShellVarContext current
|
||||
!endif
|
||||
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
include (MacroOptionalFindPackage)
|
||||
include (MacroLogFeature)
|
||||
|
||||
|
||||
option(BUILD_WITH_QT4 "Build with Qt4 no matter if Qt5 was found" ON)
|
||||
|
||||
if( NOT BUILD_WITH_QT4 )
|
||||
find_package(Qt5Core QUIET)
|
||||
if( Qt5Core_DIR )
|
||||
find_package(Qt5Widgets QUIET)
|
||||
find_package(Qt5Quick QUIET)
|
||||
find_package(Qt5PrintSupport QUIET)
|
||||
find_package(Qt5WebKit QUIET)
|
||||
find_package(Qt5Location QUIET)
|
||||
find_package(Qt5Network QUIET)
|
||||
find_package(Qt5Sensors QUIET)
|
||||
find_package(Qt5Xml QUIET)
|
||||
# find_package(Qt5WebKitWidgets QUIET)
|
||||
|
||||
message(STATUS "Using Qt 5!")
|
||||
|
||||
# We need this to find the paths to qdbusxml2cpp and co
|
||||
if (WITH_DBUS)
|
||||
find_package(Qt5DBus REQUIRED)
|
||||
include_directories(${Qt5DBus_INCLUDES})
|
||||
add_definitions(${Qt5DBus_DEFINITIONS})
|
||||
endif (WITH_DBUS)
|
||||
|
||||
include_directories(${Qt5Widgets_INCLUDES})
|
||||
add_definitions(${Qt5Widgets_DEFINITIONS})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
# set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||
|
||||
|
||||
macro(qt_wrap_ui)
|
||||
qt5_wrap_ui(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_resources)
|
||||
qt5_add_resources(${ARGN})
|
||||
endmacro()
|
||||
|
||||
# find_package(Qt5LinguistTools REQUIRED)
|
||||
macro(qt_add_translation)
|
||||
# qt5_add_translation(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_interface)
|
||||
qt5_add_dbus_interface(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_adaptor)
|
||||
qt5_add_dbus_adaptor(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_wrap_cpp)
|
||||
qt5_wrap_cpp(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(install_qt_executable)
|
||||
install_qt5_executable(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(setup_qt)
|
||||
endmacro()
|
||||
|
||||
set(QT_RCC_EXECUTABLE "${Qt5Core_RCC_EXECUTABLE}")
|
||||
|
||||
#Enable deprecated symbols
|
||||
add_definitions("-DQT_DISABLE_DEPRECATED_BEFORE=0")
|
||||
endif()
|
||||
endif()
|
||||
if( NOT Qt5Core_DIR )
|
||||
message(STATUS "Could not find Qt5, searching for Qt4 instead...")
|
||||
|
||||
set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork" "QtGui" "QtWebkit")
|
||||
if( BUILD_TESTS )
|
||||
list(APPEND NEEDED_QT4_COMPONENTS "QtTest")
|
||||
endif()
|
||||
|
||||
macro_optional_find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS} )
|
||||
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether the \n qtwebkit-devel package and whatever contains QtUiTools is installed too")
|
||||
|
||||
macro(qt5_use_modules)
|
||||
endmacro()
|
||||
|
||||
macro(qt_wrap_ui)
|
||||
qt4_wrap_ui(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_resources)
|
||||
qt4_add_resources(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_translation)
|
||||
qt4_add_translation(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_interface)
|
||||
qt4_add_dbus_interface(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_adaptor)
|
||||
qt4_add_dbus_adaptor(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_wrap_cpp)
|
||||
qt4_wrap_cpp(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(install_qt_executable)
|
||||
install_qt4_executable(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(setup_qt)
|
||||
set(QT_USE_QTGUI TRUE)
|
||||
set(QT_USE_QTSQL TRUE)
|
||||
set(QT_USE_QTNETWORK TRUE)
|
||||
set(QT_USE_QTXML TRUE)
|
||||
set(QT_USE_QTWEBKIT TRUE)
|
||||
set(QT_USE_QTDBUS TRUE)
|
||||
|
||||
include( ${QT_USE_FILE} )
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
if( Qt5Core_DIR )
|
||||
set( HAVE_QT5 TRUE )
|
||||
else( Qt5Core_DIR )
|
||||
set( HAVE_QT5 FALSE )
|
||||
endif( Qt5Core_DIR )
|
||||
@@ -36,7 +36,7 @@ if(SPHINX_FOUND)
|
||||
-D latex_logo=${LATEX_LOGO}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_PDF_DIR} )
|
||||
add_custom_target(doc-pdf $(MAKE) -C ${SPHINX_PDF_DIR} all-pdf
|
||||
add_custom_target(doc-pdf make -C ${SPHINX_PDF_DIR} all-pdf
|
||||
DEPENDS doc-latex )
|
||||
add_dependencies(doc doc-pdf)
|
||||
if (WITH_DOC)
|
||||
|
||||
153
doc/Makefile
Normal file
153
doc/Makefile
Normal file
@@ -0,0 +1,153 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OwncloudDocumentation.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OwncloudDocumentation.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/OwncloudDocumentation"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OwncloudDocumentation"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
@@ -7,11 +7,6 @@ This section explains how to build the ownCloud Client from source
|
||||
for all major platforms. You should read this section if you want
|
||||
to development on the desktop client.
|
||||
|
||||
Note that the building instruction are subject to change as development
|
||||
proceeds. It is important to check the version which is to built.
|
||||
|
||||
This instructions were updated to work with ownCloud Client 1.5.
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
@@ -44,6 +39,10 @@ Next, install the missing dependencies::
|
||||
brew install $(brew deps ocsync)
|
||||
brew install $(brew deps mirall)
|
||||
|
||||
bug:
|
||||
iniparser is not provideed by $(brew deps ocsync)
|
||||
fix with brew install iniparser
|
||||
|
||||
|
||||
To build mirall and csync, follow the `generic build instructions`_.
|
||||
|
||||
@@ -57,12 +56,13 @@ Windows (cross-compile)
|
||||
|
||||
Due to the amount of dependencies that csync entails, building the client
|
||||
for Windows is **currently only supported on openSUSE**, by using the MinGW
|
||||
cross compiler. You can set up openSUSE 12.1, 12.2 or 13.1 in a virtual machine
|
||||
cross compiler. You can set up openSUSE 12.1 or 12.2 in a virtual machine
|
||||
if you do not have it installed already.
|
||||
|
||||
In order to cross-compile, the following repositories need to be added
|
||||
via YaST or ``zypper ar`` (adjust when using openSUSE 12.2 or 13.1)::
|
||||
via YaST or ``zypper ar`` (adjust when using openSUSE 12.2)::
|
||||
|
||||
zypper ar http://download.opensuse.org/repositories/isv:/ownCloud:/devel:/mingw:/win32/openSUSE_12.1/isv:ownCloud:devel:mingw:win32.repo
|
||||
zypper ar http://download.opensuse.org/repositories/windows:/mingw:/win32/openSUSE_12.1/windows:mingw:win32.repo
|
||||
zypper ar http://download.opensuse.org/repositories/windows:/mingw/openSUSE_12.1/windows:mingw.repo
|
||||
|
||||
@@ -70,16 +70,14 @@ Next, install the cross-compiler packages and the cross-compiled dependencies::
|
||||
|
||||
zypper install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \
|
||||
mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \
|
||||
mingw32-headers mingw32-runtime site-config mingw32-libqt4-sql \
|
||||
mingw32-libqt4-sql-sqlite mingw32-libsqlite-devel \
|
||||
mingw32-dlfcn-devel mingw32-libssh2-devel kdewin-png2ico \
|
||||
mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
|
||||
mingw32-libgnutls mingw32-libneon-openssl mingw32-libneon-devel \
|
||||
mingw32-libbeecrypt mingw32-libopenssl mingw32-openssl \
|
||||
mingw32-libpng-devel mingw32-libsqlite mingw32-qtkeychain \
|
||||
mingw32-qtkeychain-devel mingw32-dlfcn mingw32-libintl-devel \
|
||||
mingw32-libneon-devel mingw32-libopenssl-devel mingw32-libproxy-devel \
|
||||
mingw32-libxml2-devel mingw32-zlib-devel
|
||||
mingw32-headers mingw32-runtime site-config mingw32-iniparser-devel \
|
||||
mingw32-libsqlite-devel mingw32-dlfcn-devel mingw32-libssh2-devel \
|
||||
kdewin-png2ico mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
|
||||
mingw32-libgnutls mingw32-libneon mingw32-libneon-devel mingw32-libbeecrypt \
|
||||
mingw32-libopenssl mingw32-openssl mingw32-libpng-devel mingw32-libsqlite \
|
||||
mingw32-qtkeychain mingw32-qtkeychain-devel mingw32-iniparser mingw32-dlfcn \
|
||||
mingw32-libintl-devel mingw32-libneon-devel mingw32-libopenssl-devel \
|
||||
mingw32-libproxy-devel mingw32-libxml2-devel mingw32-zlib-devel
|
||||
|
||||
For the installer, the NSIS installer package is also required::
|
||||
|
||||
@@ -91,10 +89,10 @@ For the installer, the NSIS installer package is also required::
|
||||
mingw32-cross-nsis-plugin-processes mingw32-cross-nsis-plugin-uac
|
||||
|
||||
You will also need to manually download and install the following files with
|
||||
``rpm -ivh <package>`` (They will also work with openSUSE 12.2 and newer)::
|
||||
``rpm -ivh <package>`` (They will also work with OpenSUSE 12.2)::
|
||||
|
||||
rpm -ihv http://download.tomahawk-player.org/packman/mingw:32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-processes-0-1.1.x86_64.rpm
|
||||
rpm -ihv http://download.tomahawk-player.org/packman/mingw:32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-uac-0-3.1.x86_64.rpm
|
||||
rpm -ihv http://pmbs.links2linux.org/download/mingw:/32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-processes-0-1.1.x86_64.rpm
|
||||
rpm -ihv http://pmbs.links2linux.org/download/mingw:/32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-uac-0-3.1.x86_64.rpm
|
||||
|
||||
Now, follow the `generic build instructions`_, but pay attention to
|
||||
the following differences:
|
||||
@@ -124,7 +122,7 @@ CMake and Mirall can be downloaded at ownCloud's `Client Download Page`_.
|
||||
If you want to build the leading edge version of the client, you should
|
||||
use the latest versions of Mirall and CSync via Git_, like so::
|
||||
|
||||
git clone git://git.csync.org/users/owncloud/csync.git ocsync
|
||||
git clone git://git.csync.org/users/freitag/csync.git ocsync
|
||||
git clone git://github.com/owncloud/mirall.git
|
||||
|
||||
Next, create build directories::
|
||||
@@ -133,11 +131,11 @@ Next, create build directories::
|
||||
mkdir mirall-build
|
||||
|
||||
This guide assumes that all directories are residing next to each other.
|
||||
Next, make sure to check out the branch called 'ocsync' in the newly checked out
|
||||
Next, make sure to check out the 'dav' branch in the newly checked out
|
||||
`ocsync` directory::
|
||||
|
||||
cd ocsync
|
||||
git checkout ocsync
|
||||
git checkout dav
|
||||
|
||||
The first package to build is CSync::
|
||||
|
||||
@@ -146,10 +144,9 @@ The first package to build is CSync::
|
||||
make
|
||||
|
||||
You probably have to satisfy some dependencies. Make sure to install all the
|
||||
needed development packages. You will need ``sqlite3`` as well as ``neon`` for
|
||||
the ownCloud module. Take special care about ``neon``. If that is missing, the
|
||||
cmake run will succeed but silently not build the ownCloud module.
|
||||
|
||||
needed development packages. You will need ``iniparser``, ``sqlite3`` as well as
|
||||
``neon`` for the ownCloud module. Take special care about ``neon``. If that is
|
||||
missing, the cmake run will succeed but silently not build the ownCloud module.
|
||||
``libssh`` and ``libsmbclient`` are optional and not required for the client
|
||||
to work. If you want to install the client, run ``make install`` as a final step.
|
||||
|
||||
|
||||
@@ -215,8 +215,6 @@ latex_documents = [
|
||||
man_pages = [
|
||||
('owncloud.1', 'owncloud', u'File synchronisation desktop utility.',
|
||||
[u'The ownCloud developers'], 1),
|
||||
('owncloudcmd.1', 'owncloudcmd', u'Command line ownCloud client tool.',
|
||||
[u'The ownCloud developers'], 1),
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
:orphan:
|
||||
|
||||
owncloud(1)
|
||||
-----------
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
*owncloud* [`OPTIONS`...]
|
||||
@@ -29,10 +28,10 @@ Config File
|
||||
BUGS
|
||||
====
|
||||
|
||||
Please report bugs at https://github.com/owncloud/mirall/issues.
|
||||
Please report bugs at https://github.com/owncloud/core/issues.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
:manpage:`owncloudcmd(1)`
|
||||
`csync(1)`
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
:orphan:
|
||||
|
||||
owncloudcmd(1)
|
||||
--------------
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
*owncloudcmd* [`OPTIONS`...] sourcedir owncloudurl
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
owncloudcmd is the command line tool for the ownCloud file synchronisation
|
||||
desktop utility, based on mirall.
|
||||
|
||||
Contrary to the :manpage:`owncloud(1)` GUI client, `owncloudcmd` will only
|
||||
perform a single sync run and then exit. It thus replaces the `ocsync` binary
|
||||
used for the same purpose in earlier releases.
|
||||
|
||||
A sync run will sync a single local directory with a WebDAV share on a
|
||||
remote ownCloud server.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
``--confdir`` `PATH`
|
||||
The configuration dir where `csync.conf` is located
|
||||
|
||||
BUGS
|
||||
====
|
||||
Please report bugs at https://github.com/owncloud/mirall/issues.
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
:manpage:`owncloud(1)`
|
||||
|
||||
@@ -25,7 +25,7 @@ Here is an explanation of the individual items in the menu:
|
||||
* Operation indicator: Shows the status of the current sync process, or
|
||||
``Up to date`` if server and client are in sync.
|
||||
* **Recent Changes**: shows the last six files modified by sync operations,
|
||||
and provides access to the Sync status, which lists all changes
|
||||
and provides access to the Sync Protocol, which lists all changes
|
||||
since the last restart of ownCloud Client.
|
||||
* ``Settings...``: provides access to the settings menu.
|
||||
* ``Help``: Opens a browser to display this help.
|
||||
@@ -120,12 +120,12 @@ certain DSL lines with very limited upstream capacity.
|
||||
ownCloud Client will pick up changes immediately, but ongoing operations
|
||||
will finish using the old settings.
|
||||
|
||||
The Sync Status Display
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The Sync Protocol
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. index:: sync status
|
||||
.. index:: sync protocol
|
||||
|
||||
The ``Sync Status`` window, which can be invoked from either from the main
|
||||
The ``Sync Protocol`` window, which can be invoked from either from the main
|
||||
menu (``Recent Changes`` -> ``Details...``) or the ``Account Settings``
|
||||
(``Info`` button), will provide you with an in-depth summary of the recent
|
||||
sync activity. It will also show files that have not been synched (ignored
|
||||
@@ -167,17 +167,6 @@ see :ref:`ignored-files-label`.
|
||||
.. image:: images/ignored_files_editor.png
|
||||
:scale: 50%
|
||||
|
||||
Pattern Matching
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
To match file names against the exclude patterns, the unix standard C
|
||||
library function fnmatch is used. It checks the filename against the pattern
|
||||
using standard shell wildcard pattern matching. Check `The opengroup website
|
||||
<http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13_01>`
|
||||
for the gory details.
|
||||
|
||||
The path that is checked is the relative path unter the sync root directory.
|
||||
|
||||
Examples:
|
||||
^^^^^^^^^
|
||||
+-----------+------------------------------+
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<file>resources/view-refresh.png</file>
|
||||
<file>resources/warning-16.png</file>
|
||||
<file>resources/settings.png</file>
|
||||
<file>resources/activity.png</file>
|
||||
<file>resources/network.png</file>
|
||||
<file>resources/owncloud_logo_blue.png</file>
|
||||
</qresource>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
245
src/3rdparty/csync/c_jhash.h
vendored
245
src/3rdparty/csync/c_jhash.h
vendored
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* c_jhash.c Jenkins Hash
|
||||
*
|
||||
* Copyright (c) 1997 Bob Jenkins <bob_jenkins@burtleburtle.net>
|
||||
*
|
||||
* lookup8.c, by Bob Jenkins, January 4 1997, Public Domain.
|
||||
* hash(), hash2(), hash3, and _c_mix() are externally useful functions.
|
||||
* Routines to test the hash are included if SELF_TEST is defined.
|
||||
* You can use this free for any purpose. It has no warranty.
|
||||
*
|
||||
* See http://burtleburtle.net/bob/hash/evahash.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file c_jhash.h
|
||||
*
|
||||
* @brief Interface of the cynapses jhash implementation
|
||||
*
|
||||
* @defgroup cynJHashInternals cynapses libc jhash function
|
||||
* @ingroup cynLibraryAPI
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#ifndef _C_JHASH_H
|
||||
#define _C_JHASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define c_hashsize(n) ((uint8_t) 1 << (n))
|
||||
#define c_hashmask(n) (xhashsize(n) - 1)
|
||||
|
||||
/**
|
||||
* _c_mix -- Mix 3 32-bit values reversibly.
|
||||
*
|
||||
* For every delta with one or two bit set, and the deltas of all three
|
||||
* high bits or all three low bits, whether the original value of a,b,c
|
||||
* is almost all zero or is uniformly distributed,
|
||||
* If _c_mix() is run forward or backward, at least 32 bits in a,b,c
|
||||
* have at least 1/4 probability of changing.
|
||||
* If _c_mix() is run forward, every bit of c will change between 1/3 and
|
||||
* 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
|
||||
* _c_mix() was built out of 36 single-cycle latency instructions in a
|
||||
* structure that could supported 2x parallelism, like so:
|
||||
* a -= b;
|
||||
* a -= c; x = (c>>13);
|
||||
* b -= c; a ^= x;
|
||||
* b -= a; x = (a<<8);
|
||||
* c -= a; b ^= x;
|
||||
* c -= b; x = (b>>13);
|
||||
* ...
|
||||
*
|
||||
* Unfortunately, superscalar Pentiums and Sparcs can't take advantage
|
||||
* of that parallelism. They've also turned some of those single-cycle
|
||||
* latency instructions into multi-cycle latency instructions. Still,
|
||||
* this is the fastest good hash I could find. There were about 2^^68
|
||||
* to choose from. I only looked at a billion or so.
|
||||
*/
|
||||
#define _c_mix(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<<8); \
|
||||
c -= a; c -= b; c ^= (b>>13); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<16); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>3); \
|
||||
b -= c; b -= a; b ^= (a<<10); \
|
||||
c -= a; c -= b; c ^= (b>>15); \
|
||||
}
|
||||
|
||||
/**
|
||||
* _c_mix64 -- Mix 3 64-bit values reversibly.
|
||||
*
|
||||
* _c_mix64() takes 48 machine instructions, but only 24 cycles on a superscalar
|
||||
* machine (like Intel's new MMX architecture). It requires 4 64-bit
|
||||
* registers for 4::2 parallelism.
|
||||
* All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of
|
||||
* (a,b,c), and all deltas of bottom bits were tested. All deltas were
|
||||
* tested both on random keys and on keys that were nearly all zero.
|
||||
* These deltas all cause every bit of c to change between 1/3 and 2/3
|
||||
* of the time (well, only 113/400 to 287/400 of the time for some
|
||||
* 2-bit delta). These deltas all cause at least 80 bits to change
|
||||
* among (a,b,c) when the _c_mix is run either forward or backward (yes it
|
||||
* is reversible).
|
||||
* This implies that a hash using _c_mix64 has no funnels. There may be
|
||||
* characteristics with 3-bit deltas or bigger, I didn't test for
|
||||
* those.
|
||||
*/
|
||||
#define _c_mix64(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>43); \
|
||||
b -= c; b -= a; b ^= (a<<9); \
|
||||
c -= a; c -= b; c ^= (b>>8); \
|
||||
a -= b; a -= c; a ^= (c>>38); \
|
||||
b -= c; b -= a; b ^= (a<<23); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>35); \
|
||||
b -= c; b -= a; b ^= (a<<49); \
|
||||
c -= a; c -= b; c ^= (b>>11); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<18); \
|
||||
c -= a; c -= b; c ^= (b>>22); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief hash a variable-length key into a 32-bit value
|
||||
*
|
||||
* The best hash table sizes are powers of 2. There is no need to do
|
||||
* mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
* use a bitmask. For example, if you need only 10 bits, do
|
||||
* h = (h & hashmask(10));
|
||||
* In which case, the hash table should have hashsize(10) elements.
|
||||
*
|
||||
* Use for hash table lookup, or anything where one collision in 2^32 is
|
||||
* acceptable. Do NOT use for cryptographic purposes.
|
||||
*
|
||||
* @param k The key (the unaligned variable-length array of bytes).
|
||||
*
|
||||
* @param length The length of the key, counting by bytes.
|
||||
*
|
||||
* @param initval Initial value, can be any 4-byte value.
|
||||
*
|
||||
* @return Returns a 32-bit value. Every bit of the key affects every bit
|
||||
* of the return value. Every 1-bit and 2-bit delta achieves
|
||||
* avalanche. About 36+6len instructions.
|
||||
*/
|
||||
static inline uint32_t c_jhash(const uint8_t *k, uint32_t length, uint32_t initval) {
|
||||
uint32_t a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
while (len >= 12) {
|
||||
a += (k[0] +((uint32_t)k[1]<<8) +((uint32_t)k[2]<<16) +((uint32_t)k[3]<<24));
|
||||
b += (k[4] +((uint32_t)k[5]<<8) +((uint32_t)k[6]<<16) +((uint32_t)k[7]<<24));
|
||||
c += (k[8] +((uint32_t)k[9]<<8) +((uint32_t)k[10]<<16)+((uint32_t)k[11]<<24));
|
||||
_c_mix(a,b,c);
|
||||
k += 12; len -= 12;
|
||||
}
|
||||
|
||||
/* handle the last 11 bytes */
|
||||
c += length;
|
||||
/* all the case statements fall through */
|
||||
switch(len) {
|
||||
case 11: c+=((uint32_t)k[10]<<24);
|
||||
case 10: c+=((uint32_t)k[9]<<16);
|
||||
case 9 : c+=((uint32_t)k[8]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 8 : b+=((uint32_t)k[7]<<24);
|
||||
case 7 : b+=((uint32_t)k[6]<<16);
|
||||
case 6 : b+=((uint32_t)k[5]<<8);
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((uint32_t)k[3]<<24);
|
||||
case 3 : a+=((uint32_t)k[2]<<16);
|
||||
case 2 : a+=((uint32_t)k[1]<<8);
|
||||
case 1 : a+=k[0];
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
_c_mix(a,b,c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief hash a variable-length key into a 64-bit value
|
||||
*
|
||||
* The best hash table sizes are powers of 2. There is no need to do
|
||||
* mod a prime (mod is sooo slow!). If you need less than 64 bits,
|
||||
* use a bitmask. For example, if you need only 10 bits, do
|
||||
* h = (h & hashmask(10));
|
||||
* In which case, the hash table should have hashsize(10) elements.
|
||||
*
|
||||
* Use for hash table lookup, or anything where one collision in 2^^64
|
||||
* is acceptable. Do NOT use for cryptographic purposes.
|
||||
*
|
||||
* @param k The key (the unaligned variable-length array of bytes).
|
||||
* @param length The length of the key, counting by bytes.
|
||||
* @param intval Initial value, can be any 8-byte value.
|
||||
*
|
||||
* @return A 64-bit value. Every bit of the key affects every bit of
|
||||
* the return value. No funnels. Every 1-bit and 2-bit delta
|
||||
* achieves avalanche. About 41+5len instructions.
|
||||
*/
|
||||
static inline uint64_t c_jhash64(const uint8_t *k, uint64_t length, uint64_t intval) {
|
||||
uint64_t a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = intval; /* the previous hash value */
|
||||
c = 0x9e3779b97f4a7c13LL; /* the golden ratio; an arbitrary value */
|
||||
|
||||
/* handle most of the key */
|
||||
while (len >= 24)
|
||||
{
|
||||
a += (k[0] +((uint64_t)k[ 1]<< 8)+((uint64_t)k[ 2]<<16)+((uint64_t)k[ 3]<<24)
|
||||
+((uint64_t)k[4 ]<<32)+((uint64_t)k[ 5]<<40)+((uint64_t)k[ 6]<<48)+((uint64_t)k[ 7]<<56));
|
||||
b += (k[8] +((uint64_t)k[ 9]<< 8)+((uint64_t)k[10]<<16)+((uint64_t)k[11]<<24)
|
||||
+((uint64_t)k[12]<<32)+((uint64_t)k[13]<<40)+((uint64_t)k[14]<<48)+((uint64_t)k[15]<<56));
|
||||
c += (k[16] +((uint64_t)k[17]<< 8)+((uint64_t)k[18]<<16)+((uint64_t)k[19]<<24)
|
||||
+((uint64_t)k[20]<<32)+((uint64_t)k[21]<<40)+((uint64_t)k[22]<<48)+((uint64_t)k[23]<<56));
|
||||
_c_mix64(a,b,c);
|
||||
k += 24; len -= 24;
|
||||
}
|
||||
|
||||
/* handle the last 23 bytes */
|
||||
c += length;
|
||||
switch(len) {
|
||||
case 23: c+=((uint64_t)k[22]<<56);
|
||||
case 22: c+=((uint64_t)k[21]<<48);
|
||||
case 21: c+=((uint64_t)k[20]<<40);
|
||||
case 20: c+=((uint64_t)k[19]<<32);
|
||||
case 19: c+=((uint64_t)k[18]<<24);
|
||||
case 18: c+=((uint64_t)k[17]<<16);
|
||||
case 17: c+=((uint64_t)k[16]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 16: b+=((uint64_t)k[15]<<56);
|
||||
case 15: b+=((uint64_t)k[14]<<48);
|
||||
case 14: b+=((uint64_t)k[13]<<40);
|
||||
case 13: b+=((uint64_t)k[12]<<32);
|
||||
case 12: b+=((uint64_t)k[11]<<24);
|
||||
case 11: b+=((uint64_t)k[10]<<16);
|
||||
case 10: b+=((uint64_t)k[ 9]<<8);
|
||||
case 9: b+=((uint64_t)k[ 8]);
|
||||
case 8: a+=((uint64_t)k[ 7]<<56);
|
||||
case 7: a+=((uint64_t)k[ 6]<<48);
|
||||
case 6: a+=((uint64_t)k[ 5]<<40);
|
||||
case 5: a+=((uint64_t)k[ 4]<<32);
|
||||
case 4: a+=((uint64_t)k[ 3]<<24);
|
||||
case 3: a+=((uint64_t)k[ 2]<<16);
|
||||
case 2: a+=((uint64_t)k[ 1]<<8);
|
||||
case 1: a+=((uint64_t)k[ 0]);
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
_c_mix64(a,b,c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
#endif /* _C_JHASH_H */
|
||||
|
||||
3
src/3rdparty/qjson/AUTHORS
vendored
3
src/3rdparty/qjson/AUTHORS
vendored
@@ -1,3 +0,0 @@
|
||||
Eeli Reilin <eeli@emicode.fi>
|
||||
Luis Gustavo S. Barreto <gustavosbarreto@gmail.com>
|
||||
Stephen Kockentiedt <Stephen@Kockentiedt.name>
|
||||
26
src/3rdparty/qjson/LICENSE
vendored
26
src/3rdparty/qjson/LICENSE
vendored
@@ -1,26 +0,0 @@
|
||||
Copyright 2011 Eeli Reilin. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''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 EELI REILIN 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.
|
||||
|
||||
The views and conclusions contained in the software and documentation
|
||||
are those of the authors and should not be interpreted as representing
|
||||
official policies, either expressed or implied, of Eeli Reilin.
|
||||
96
src/3rdparty/qjson/README
vendored
96
src/3rdparty/qjson/README
vendored
@@ -1,96 +0,0 @@
|
||||
########################################################################
|
||||
1. INTRODUCTION
|
||||
|
||||
The Json class is a simple class for parsing JSON data into a QVariant
|
||||
hierarchies. Now, we can also reverse the process and serialize
|
||||
QVariant hierarchies into valid JSON data.
|
||||
|
||||
|
||||
########################################################################
|
||||
2. HOW TO USE
|
||||
|
||||
The parser is really easy to use. Let's say we have the following
|
||||
QString of JSON data:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
{
|
||||
"encoding" : "UTF-8",
|
||||
"plug-ins" : [
|
||||
"python",
|
||||
"c++",
|
||||
"ruby"
|
||||
],
|
||||
"indent" : {
|
||||
"length" : 3,
|
||||
"use_space" : true
|
||||
}
|
||||
}
|
||||
------------------------------------------------------------------------
|
||||
|
||||
We would first call the parse-method:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
//Say that we're using the QtJson namespace
|
||||
using namespace QtJson;
|
||||
bool ok;
|
||||
//json is a QString containing the JSON data
|
||||
QVariantMap result = Json::parse(json, ok).toMap();
|
||||
|
||||
if(!ok) {
|
||||
qFatal("An error occurred during parsing");
|
||||
exit(1);
|
||||
}
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Assuming the parsing process completed without errors, we would then
|
||||
go through the hierarchy:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
qDebug() << "encoding:" << result["encoding"].toString();
|
||||
qDebug() << "plugins:";
|
||||
|
||||
foreach(QVariant plugin, result["plug-ins"].toList()) {
|
||||
qDebug() << "\t-" << plugin.toString();
|
||||
}
|
||||
|
||||
QVariantMap nestedMap = result["indent"].toMap();
|
||||
qDebug() << "length:" << nestedMap["length"].toInt();
|
||||
qDebug() << "use_space:" << nestedMap["use_space"].toBool();
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The previous code would print out the following:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
encoding: "UTF-8"
|
||||
plugins:
|
||||
- "python"
|
||||
- "c++"
|
||||
- "ruby"
|
||||
length: 3
|
||||
use_space: true
|
||||
------------------------------------------------------------------------
|
||||
|
||||
To write JSON data from Qt object is as simple as parsing:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
QVariantMap map;
|
||||
map["name"] = "Name";
|
||||
map["age"] = 22;
|
||||
|
||||
QByteArray data = Json::serialize(map);
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The byte array 'data' contains valid JSON data:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
{
|
||||
name: "Luis Gustavo",
|
||||
age: 22,
|
||||
}
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
########################################################################
|
||||
4. CONTRIBUTING
|
||||
|
||||
The code is available to download at GitHub. Contribute if you dare!
|
||||
653
src/3rdparty/qjson/json.cpp
vendored
653
src/3rdparty/qjson/json.cpp
vendored
@@ -1,653 +0,0 @@
|
||||
/* Copyright 2011 Eeli Reilin. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''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 EELI REILIN 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation
|
||||
* are those of the authors and should not be interpreted as representing
|
||||
* official policies, either expressed or implied, of Eeli Reilin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file json.cpp
|
||||
*/
|
||||
|
||||
#include "json.h"
|
||||
|
||||
namespace QtJson
|
||||
{
|
||||
|
||||
|
||||
static QString sanitizeString(QString str);
|
||||
static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep);
|
||||
static QVariant parseValue(const QString &json, int &index, bool &success);
|
||||
static QVariant parseObject(const QString &json, int &index, bool &success);
|
||||
static QVariant parseArray(const QString &json, int &index, bool &success);
|
||||
static QVariant parseString(const QString &json, int &index, bool &success);
|
||||
static QVariant parseNumber(const QString &json, int &index);
|
||||
static int lastIndexOfNumber(const QString &json, int index);
|
||||
static void eatWhitespace(const QString &json, int &index);
|
||||
static int lookAhead(const QString &json, int index);
|
||||
static int nextToken(const QString &json, int &index);
|
||||
|
||||
|
||||
|
||||
|
||||
/***** public *****/
|
||||
|
||||
|
||||
/**
|
||||
* parse
|
||||
*/
|
||||
QVariant parse(const QString &json)
|
||||
{
|
||||
bool success = true;
|
||||
return parse(json, success);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse
|
||||
*/
|
||||
QVariant parse(const QString &json, bool &success)
|
||||
{
|
||||
success = true;
|
||||
|
||||
//Return an empty QVariant if the JSON data is either null or empty
|
||||
if(!json.isNull() || !json.isEmpty())
|
||||
{
|
||||
QString data = json;
|
||||
//We'll start from index 0
|
||||
int index = 0;
|
||||
|
||||
//Parse the first value
|
||||
QVariant value = parseValue(data, index, success);
|
||||
|
||||
//Return the parsed value
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Return the empty QVariant
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray serialize(const QVariant &data)
|
||||
{
|
||||
bool success = true;
|
||||
return serialize(data, success);
|
||||
}
|
||||
|
||||
QByteArray serialize(const QVariant &data, bool &success)
|
||||
{
|
||||
QByteArray str;
|
||||
success = true;
|
||||
|
||||
if(!data.isValid()) // invalid or null?
|
||||
{
|
||||
str = "null";
|
||||
}
|
||||
else if((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list?
|
||||
{
|
||||
QList<QByteArray> values;
|
||||
const QVariantList list = data.toList();
|
||||
Q_FOREACH(const QVariant& v, list)
|
||||
{
|
||||
QByteArray serializedValue = serialize(v);
|
||||
if(serializedValue.isNull())
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
values << serializedValue;
|
||||
}
|
||||
|
||||
str = "[ " + join( values, ", " ) + " ]";
|
||||
}
|
||||
else if(data.type() == QVariant::Hash) // variant is a hash?
|
||||
{
|
||||
const QVariantHash vhash = data.toHash();
|
||||
QHashIterator<QString, QVariant> it( vhash );
|
||||
str = "{ ";
|
||||
QList<QByteArray> pairs;
|
||||
|
||||
while(it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
QByteArray serializedValue = serialize(it.value());
|
||||
|
||||
if(serializedValue.isNull())
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue;
|
||||
}
|
||||
|
||||
str += join(pairs, ", ");
|
||||
str += " }";
|
||||
}
|
||||
else if(data.type() == QVariant::Map) // variant is a map?
|
||||
{
|
||||
const QVariantMap vmap = data.toMap();
|
||||
QMapIterator<QString, QVariant> it( vmap );
|
||||
str = "{ ";
|
||||
QList<QByteArray> pairs;
|
||||
while(it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
QByteArray serializedValue = serialize(it.value());
|
||||
if(serializedValue.isNull())
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue;
|
||||
}
|
||||
str += join(pairs, ", ");
|
||||
str += " }";
|
||||
}
|
||||
else if((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array?
|
||||
{
|
||||
str = sanitizeString(data.toString()).toUtf8();
|
||||
}
|
||||
else if(data.type() == QVariant::Double) // double?
|
||||
{
|
||||
str = QByteArray::number(data.toDouble(), 'g', 20);
|
||||
if(!str.contains(".") && ! str.contains("e"))
|
||||
{
|
||||
str += ".0";
|
||||
}
|
||||
}
|
||||
else if (data.type() == QVariant::Bool) // boolean value?
|
||||
{
|
||||
str = data.toBool() ? "true" : "false";
|
||||
}
|
||||
else if (data.type() == QVariant::ULongLong) // large unsigned number?
|
||||
{
|
||||
str = QByteArray::number(data.value<qulonglong>());
|
||||
}
|
||||
else if ( data.canConvert<qlonglong>() ) // any signed number?
|
||||
{
|
||||
str = QByteArray::number(data.value<qlonglong>());
|
||||
}
|
||||
else if (data.canConvert<long>())
|
||||
{
|
||||
str = QString::number(data.value<long>()).toUtf8();
|
||||
}
|
||||
else if (data.canConvert<QString>()) // can value be converted to string?
|
||||
{
|
||||
// this will catch QDate, QDateTime, QUrl, ...
|
||||
str = sanitizeString(data.toString()).toUtf8();
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***** private *****/
|
||||
|
||||
|
||||
/**
|
||||
* \enum JsonToken
|
||||
*/
|
||||
enum JsonToken
|
||||
{
|
||||
JsonTokenNone = 0,
|
||||
JsonTokenCurlyOpen = 1,
|
||||
JsonTokenCurlyClose = 2,
|
||||
JsonTokenSquaredOpen = 3,
|
||||
JsonTokenSquaredClose = 4,
|
||||
JsonTokenColon = 5,
|
||||
JsonTokenComma = 6,
|
||||
JsonTokenString = 7,
|
||||
JsonTokenNumber = 8,
|
||||
JsonTokenTrue = 9,
|
||||
JsonTokenFalse = 10,
|
||||
JsonTokenNull = 11
|
||||
};
|
||||
|
||||
static QString sanitizeString(QString str)
|
||||
{
|
||||
str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
|
||||
str.replace(QLatin1String("\""), QLatin1String("\\\""));
|
||||
str.replace(QLatin1String("\b"), QLatin1String("\\b"));
|
||||
str.replace(QLatin1String("\f"), QLatin1String("\\f"));
|
||||
str.replace(QLatin1String("\n"), QLatin1String("\\n"));
|
||||
str.replace(QLatin1String("\r"), QLatin1String("\\r"));
|
||||
str.replace(QLatin1String("\t"), QLatin1String("\\t"));
|
||||
return QString(QLatin1String("\"%1\"")).arg(str);
|
||||
}
|
||||
|
||||
static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep)
|
||||
{
|
||||
QByteArray res;
|
||||
Q_FOREACH(const QByteArray &i, list)
|
||||
{
|
||||
if(!res.isEmpty())
|
||||
{
|
||||
res += sep;
|
||||
}
|
||||
res += i;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* parseValue
|
||||
*/
|
||||
static QVariant parseValue(const QString &json, int &index, bool &success)
|
||||
{
|
||||
//Determine what kind of data we should parse by
|
||||
//checking out the upcoming token
|
||||
switch(lookAhead(json, index))
|
||||
{
|
||||
case JsonTokenString:
|
||||
return parseString(json, index, success);
|
||||
case JsonTokenNumber:
|
||||
return parseNumber(json, index);
|
||||
case JsonTokenCurlyOpen:
|
||||
return parseObject(json, index, success);
|
||||
case JsonTokenSquaredOpen:
|
||||
return parseArray(json, index, success);
|
||||
case JsonTokenTrue:
|
||||
nextToken(json, index);
|
||||
return QVariant(true);
|
||||
case JsonTokenFalse:
|
||||
nextToken(json, index);
|
||||
return QVariant(false);
|
||||
case JsonTokenNull:
|
||||
nextToken(json, index);
|
||||
return QVariant();
|
||||
case JsonTokenNone:
|
||||
break;
|
||||
}
|
||||
|
||||
//If there were no tokens, flag the failure and return an empty QVariant
|
||||
success = false;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
/**
|
||||
* parseObject
|
||||
*/
|
||||
static QVariant parseObject(const QString &json, int &index, bool &success)
|
||||
{
|
||||
QVariantMap map;
|
||||
int token;
|
||||
|
||||
//Get rid of the whitespace and increment index
|
||||
nextToken(json, index);
|
||||
|
||||
//Loop through all of the key/value pairs of the object
|
||||
bool done = false;
|
||||
while(!done)
|
||||
{
|
||||
//Get the upcoming token
|
||||
token = lookAhead(json, index);
|
||||
|
||||
if(token == JsonTokenNone)
|
||||
{
|
||||
success = false;
|
||||
return QVariantMap();
|
||||
}
|
||||
else if(token == JsonTokenComma)
|
||||
{
|
||||
nextToken(json, index);
|
||||
}
|
||||
else if(token == JsonTokenCurlyClose)
|
||||
{
|
||||
nextToken(json, index);
|
||||
return map;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Parse the key/value pair's name
|
||||
QString name = parseString(json, index, success).toString();
|
||||
|
||||
if(!success)
|
||||
{
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
//Get the next token
|
||||
token = nextToken(json, index);
|
||||
|
||||
//If the next token is not a colon, flag the failure
|
||||
//return an empty QVariant
|
||||
if(token != JsonTokenColon)
|
||||
{
|
||||
success = false;
|
||||
return QVariant(QVariantMap());
|
||||
}
|
||||
|
||||
//Parse the key/value pair's value
|
||||
QVariant value = parseValue(json, index, success);
|
||||
|
||||
if(!success)
|
||||
{
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
//Assign the value to the key in the map
|
||||
map[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
//Return the map successfully
|
||||
return QVariant(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseArray
|
||||
*/
|
||||
static QVariant parseArray(const QString &json, int &index, bool &success)
|
||||
{
|
||||
QVariantList list;
|
||||
|
||||
nextToken(json, index);
|
||||
|
||||
bool done = false;
|
||||
while(!done)
|
||||
{
|
||||
int token = lookAhead(json, index);
|
||||
|
||||
if(token == JsonTokenNone)
|
||||
{
|
||||
success = false;
|
||||
return QVariantList();
|
||||
}
|
||||
else if(token == JsonTokenComma)
|
||||
{
|
||||
nextToken(json, index);
|
||||
}
|
||||
else if(token == JsonTokenSquaredClose)
|
||||
{
|
||||
nextToken(json, index);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
QVariant value = parseValue(json, index, success);
|
||||
|
||||
if(!success)
|
||||
{
|
||||
return QVariantList();
|
||||
}
|
||||
|
||||
list.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseString
|
||||
*/
|
||||
static QVariant parseString(const QString &json, int &index, bool &success)
|
||||
{
|
||||
QString s;
|
||||
QChar c;
|
||||
|
||||
eatWhitespace(json, index);
|
||||
|
||||
c = json[index++];
|
||||
|
||||
bool complete = false;
|
||||
while(!complete)
|
||||
{
|
||||
if(index == json.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
c = json[index++];
|
||||
|
||||
if(c == '\"')
|
||||
{
|
||||
complete = true;
|
||||
break;
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
if(index == json.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
c = json[index++];
|
||||
|
||||
if(c == '\"')
|
||||
{
|
||||
s.append('\"');
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
s.append('\\');
|
||||
}
|
||||
else if(c == '/')
|
||||
{
|
||||
s.append('/');
|
||||
}
|
||||
else if(c == 'b')
|
||||
{
|
||||
s.append('\b');
|
||||
}
|
||||
else if(c == 'f')
|
||||
{
|
||||
s.append('\f');
|
||||
}
|
||||
else if(c == 'n')
|
||||
{
|
||||
s.append('\n');
|
||||
}
|
||||
else if(c == 'r')
|
||||
{
|
||||
s.append('\r');
|
||||
}
|
||||
else if(c == 't')
|
||||
{
|
||||
s.append('\t');
|
||||
}
|
||||
else if(c == 'u')
|
||||
{
|
||||
int remainingLength = json.size() - index;
|
||||
|
||||
if(remainingLength >= 4)
|
||||
{
|
||||
QString unicodeStr = json.mid(index, 4);
|
||||
|
||||
int symbol = unicodeStr.toInt(0, 16);
|
||||
|
||||
s.append(QChar(symbol));
|
||||
|
||||
index += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if(!complete)
|
||||
{
|
||||
success = false;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return QVariant(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseNumber
|
||||
*/
|
||||
static QVariant parseNumber(const QString &json, int &index)
|
||||
{
|
||||
eatWhitespace(json, index);
|
||||
|
||||
int lastIndex = lastIndexOfNumber(json, index);
|
||||
int charLength = (lastIndex - index) + 1;
|
||||
QString numberStr;
|
||||
|
||||
numberStr = json.mid(index, charLength);
|
||||
|
||||
index = lastIndex + 1;
|
||||
|
||||
if (numberStr.contains('.')) {
|
||||
return QVariant(numberStr.toDouble(NULL));
|
||||
} else if (numberStr.startsWith('-')) {
|
||||
return QVariant(numberStr.toLongLong(NULL));
|
||||
} else {
|
||||
return QVariant(numberStr.toULongLong(NULL));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lastIndexOfNumber
|
||||
*/
|
||||
static int lastIndexOfNumber(const QString &json, int index)
|
||||
{
|
||||
int lastIndex;
|
||||
|
||||
for(lastIndex = index; lastIndex < json.size(); lastIndex++)
|
||||
{
|
||||
if(QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lastIndex -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* eatWhitespace
|
||||
*/
|
||||
static void eatWhitespace(const QString &json, int &index)
|
||||
{
|
||||
for(; index < json.size(); index++)
|
||||
{
|
||||
if(QString(" \t\n\r").indexOf(json[index]) == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lookAhead
|
||||
*/
|
||||
static int lookAhead(const QString &json, int index)
|
||||
{
|
||||
int saveIndex = index;
|
||||
return nextToken(json, saveIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* nextToken
|
||||
*/
|
||||
static int nextToken(const QString &json, int &index)
|
||||
{
|
||||
eatWhitespace(json, index);
|
||||
|
||||
if(index == json.size())
|
||||
{
|
||||
return JsonTokenNone;
|
||||
}
|
||||
|
||||
QChar c = json[index];
|
||||
index++;
|
||||
switch(c.toLatin1())
|
||||
{
|
||||
case '{': return JsonTokenCurlyOpen;
|
||||
case '}': return JsonTokenCurlyClose;
|
||||
case '[': return JsonTokenSquaredOpen;
|
||||
case ']': return JsonTokenSquaredClose;
|
||||
case ',': return JsonTokenComma;
|
||||
case '"': return JsonTokenString;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '-': return JsonTokenNumber;
|
||||
case ':': return JsonTokenColon;
|
||||
}
|
||||
|
||||
index--;
|
||||
|
||||
int remainingLength = json.size() - index;
|
||||
|
||||
//True
|
||||
if(remainingLength >= 4)
|
||||
{
|
||||
if (json[index] == 't' && json[index + 1] == 'r' &&
|
||||
json[index + 2] == 'u' && json[index + 3] == 'e')
|
||||
{
|
||||
index += 4;
|
||||
return JsonTokenTrue;
|
||||
}
|
||||
}
|
||||
|
||||
//False
|
||||
if (remainingLength >= 5)
|
||||
{
|
||||
if (json[index] == 'f' && json[index + 1] == 'a' &&
|
||||
json[index + 2] == 'l' && json[index + 3] == 's' &&
|
||||
json[index + 4] == 'e')
|
||||
{
|
||||
index += 5;
|
||||
return JsonTokenFalse;
|
||||
}
|
||||
}
|
||||
|
||||
//Null
|
||||
if (remainingLength >= 4)
|
||||
{
|
||||
if (json[index] == 'n' && json[index + 1] == 'u' &&
|
||||
json[index + 2] == 'l' && json[index + 3] == 'l')
|
||||
{
|
||||
index += 4;
|
||||
return JsonTokenNull;
|
||||
}
|
||||
}
|
||||
|
||||
return JsonTokenNone;
|
||||
}
|
||||
|
||||
|
||||
} //end namespace
|
||||
85
src/3rdparty/qjson/json.h
vendored
85
src/3rdparty/qjson/json.h
vendored
@@ -1,85 +0,0 @@
|
||||
/* Copyright 2011 Eeli Reilin. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''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 EELI REILIN 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation
|
||||
* are those of the authors and should not be interpreted as representing
|
||||
* official policies, either expressed or implied, of Eeli Reilin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file json.h
|
||||
*/
|
||||
|
||||
#ifndef JSON_H
|
||||
#define JSON_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
|
||||
|
||||
/**
|
||||
* \namespace QtJson
|
||||
* \brief A JSON data parser
|
||||
*
|
||||
* Json parses a JSON data into a QVariant hierarchy.
|
||||
*/
|
||||
namespace QtJson
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse a JSON string
|
||||
*
|
||||
* \param json The JSON data
|
||||
*/
|
||||
QVariant parse(const QString &json);
|
||||
|
||||
/**
|
||||
* Parse a JSON string
|
||||
*
|
||||
* \param json The JSON data
|
||||
* \param success The success of the parsing
|
||||
*/
|
||||
QVariant parse(const QString &json, bool &success);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
*
|
||||
* \return QByteArray Textual JSON representation
|
||||
*/
|
||||
QByteArray serialize(const QVariant &data);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
* \param success The success of the serialization
|
||||
*
|
||||
* \return QByteArray Textual JSON representation
|
||||
*/
|
||||
QByteArray serialize(const QVariant &data, bool &success);
|
||||
|
||||
} //end namespace
|
||||
|
||||
#endif //JSON_H
|
||||
@@ -61,13 +61,11 @@ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char *
|
||||
}
|
||||
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
|
||||
: QApplication(argc, argv, type)
|
||||
{
|
||||
sysInit();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(Q_WS_X11)
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _SHAREDTOOLS_SINGLEAPPLICATION
|
||||
#define _SHAREDTOOLS_SINGLEAPPLICATION
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
namespace SharedTools {
|
||||
@@ -44,9 +41,7 @@ class QtSingleApplication : public QApplication
|
||||
public:
|
||||
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
|
||||
QtSingleApplication(const QString &id, int &argc, char **argv);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
QtSingleApplication(int &argc, char **argv, Type type);
|
||||
#endif
|
||||
#if defined(Q_WS_X11)
|
||||
explicit QtSingleApplication(Display *dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
|
||||
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0);
|
||||
@@ -88,4 +83,3 @@ private:
|
||||
};
|
||||
|
||||
} // namespace SharedTools
|
||||
#endif // _SHAREDTOOLS_SINGLEAPPLICATION
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
#add_subdirectory(integration)
|
||||
include(${QT_USE_FILE})
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
qt_add_resources(MIRALL_RC_SRC ../mirall.qrc)
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ../mirall.qrc)
|
||||
if ( IS_DIRECTORY ${OEM_THEME_DIR} )
|
||||
qt_add_resources(MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
|
||||
set(theme_dir ${OEM_THEME_DIR}/theme)
|
||||
else()
|
||||
qt_add_resources(MIRALL_RC_SRC ../theme.qrc)
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ../theme.qrc)
|
||||
set(theme_dir ${CMAKE_CURRENT_SOURCE_DIR}/../theme)
|
||||
endif()
|
||||
|
||||
set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
|
||||
|
||||
set(mirall_UI
|
||||
mirall/folderwizardsourcepage.ui
|
||||
mirall/folderwizardtargetpage.ui
|
||||
mirall/sslerrordialog.ui
|
||||
mirall/settingsdialog.ui
|
||||
mirall/generalsettings.ui
|
||||
mirall/networksettings.ui
|
||||
mirall/accountsettings.ui
|
||||
mirall/ignorelisteditor.ui
|
||||
mirall/itemprogressdialog.ui
|
||||
wizard/owncloudsetupnocredspage.ui
|
||||
wizard/owncloudhttpcredspage.ui
|
||||
wizard/owncloudwizardresultpage.ui
|
||||
wizard/owncloudadvancedsetuppage.ui
|
||||
)
|
||||
|
||||
set(3rdparty_SRC
|
||||
3rdparty/qtsingleapplication/qtsingleapplication.cpp
|
||||
3rdparty/qtsingleapplication/qtlocalpeer.cpp
|
||||
@@ -29,10 +45,9 @@ set(3rdparty_HEADER
|
||||
3rdparty/fancylineedit/fancylineedit.h
|
||||
3rdparty/QProgressIndicator/QProgressIndicator.h
|
||||
)
|
||||
qt4_wrap_cpp(3rdparty_MOC ${3rdparty_HEADER})
|
||||
|
||||
qt_wrap_cpp(3rdparty_MOC ${3rdparty_HEADER})
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT WIN32)
|
||||
list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
|
||||
else()
|
||||
list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_win.cpp )
|
||||
@@ -43,34 +58,31 @@ set(3rdparty_INC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtsingleapplication
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QProgressIndicator
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/fancylineedit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/csync
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qjson
|
||||
)
|
||||
|
||||
qt4_wrap_ui(mirall_UI_SRCS ${mirall_UI})
|
||||
|
||||
set(libsync_SRCS
|
||||
mirall/folderman.cpp
|
||||
mirall/folder.cpp
|
||||
mirall/folderwatcher.cpp
|
||||
mirall/syncresult.cpp
|
||||
mirall/networklocation.cpp
|
||||
mirall/mirallconfigfile.cpp
|
||||
mirall/csyncthread.cpp
|
||||
mirall/owncloudpropagator.cpp
|
||||
mirall/syncjournalfilerecord.cpp
|
||||
mirall/syncjournaldb.cpp
|
||||
mirall/fileutils.cpp
|
||||
mirall/theme.cpp
|
||||
mirall/owncloudtheme.cpp
|
||||
mirall/owncloudinfo.cpp
|
||||
mirall/logger.cpp
|
||||
mirall/utility.cpp
|
||||
mirall/connectionvalidator.cpp
|
||||
mirall/progressdispatcher.cpp
|
||||
mirall/mirallaccessmanager.cpp
|
||||
mirall/networkjobs.cpp
|
||||
mirall/account.cpp
|
||||
mirall/quotainfo.cpp
|
||||
creds/dummycredentials.cpp
|
||||
creds/httpcredentials.cpp
|
||||
creds/credentialsfactory.cpp
|
||||
creds/http/credentialstore.cpp
|
||||
creds/http/httpconfigfile.cpp
|
||||
creds/shibbolethcredentials.cpp
|
||||
creds/shibboleth/shibbolethaccessmanager.cpp
|
||||
@@ -79,7 +91,6 @@ set(libsync_SRCS
|
||||
creds/shibboleth/shibbolethrefresher.cpp
|
||||
creds/shibboleth/shibbolethconfigfile.cpp
|
||||
creds/credentialscommon.cpp
|
||||
3rdparty/qjson/json.cpp
|
||||
)
|
||||
|
||||
set(libsync_HEADERS
|
||||
@@ -87,21 +98,18 @@ set(libsync_HEADERS
|
||||
mirall/folder.h
|
||||
mirall/folderwatcher.h
|
||||
mirall/csyncthread.h
|
||||
mirall/owncloudpropagator.h
|
||||
mirall/syncjournaldb.h
|
||||
mirall/theme.h
|
||||
mirall/owncloudtheme.h
|
||||
mirall/owncloudinfo.h
|
||||
mirall/logger.h
|
||||
mirall/connectionvalidator.h
|
||||
mirall/progressdispatcher.h
|
||||
mirall/mirallaccessmanager.h
|
||||
mirall/networkjobs.h
|
||||
mirall/account.h
|
||||
mirall/quotainfo.h
|
||||
creds/abstractcredentials.h
|
||||
creds/dummycredentials.h
|
||||
creds/httpcredentials.h
|
||||
creds/credentialsfactory.h
|
||||
creds/http/credentialstore.h
|
||||
creds/http/httpconfigfile.h
|
||||
creds/shibbolethcredentials.h
|
||||
creds/shibboleth/shibbolethaccessmanager.h
|
||||
@@ -110,7 +118,6 @@ set(libsync_HEADERS
|
||||
creds/shibboleth/shibbolethrefresher.h
|
||||
creds/shibboleth/shibbolethconfigfile.h
|
||||
creds/credentialscommon.h
|
||||
3rdparty/qjson/json.h
|
||||
)
|
||||
|
||||
IF( INOTIFY_FOUND )
|
||||
@@ -119,7 +126,7 @@ IF( INOTIFY_FOUND )
|
||||
set(libsync_HEADERS ${libsync_HEADERS} mirall/inotify.h)
|
||||
set(libsync_HEADERS ${libsync_HEADERS} mirall/folderwatcher_inotify.h)
|
||||
ENDIF()
|
||||
IF( WIN32 )
|
||||
IF( WIN32 )
|
||||
set(libsync_SRCS ${libsync_SRCS} mirall/folderwatcher_win.cpp)
|
||||
set(libsync_HEADERS ${libsync_HEADERS} mirall/folderwatcher_win.h)
|
||||
ENDIF()
|
||||
@@ -128,75 +135,23 @@ IF( APPLE )
|
||||
ENDIF()
|
||||
|
||||
|
||||
qt_wrap_cpp(syncMoc ${libsync_HEADERS})
|
||||
|
||||
# These headers are installed for libowncloudsync to be used by 3rd party apps
|
||||
set(owncloudsync_HEADERS
|
||||
mirall/account.h
|
||||
mirall/csyncthread.h
|
||||
mirall/folder.h
|
||||
mirall/folderman.h
|
||||
mirall/folderwatcher.h
|
||||
mirall/mirallconfigfile.h
|
||||
mirall/networkjobs.h
|
||||
mirall/progressdispatcher.h
|
||||
mirall/syncfileitem.h
|
||||
mirall/syncjournaldb.h
|
||||
mirall/syncresult.h
|
||||
)
|
||||
|
||||
set(creds_HEADERS
|
||||
creds/abstractcredentials.h
|
||||
creds/httpcredentials.h
|
||||
)
|
||||
|
||||
IF (NOT APPLE)
|
||||
INSTALL(
|
||||
FILES ${owncloudsync_HEADERS}
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR}/owncloudsync/mirall
|
||||
)
|
||||
INSTALL(
|
||||
FILES ${creds_HEADERS}
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR}/owncloudsync/creds
|
||||
)
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
IF( DEFINED CSYNC_BUILD_PATH )
|
||||
SET(HTTPBF_LIBRARY ${CSYNC_BUILD_PATH}/src/httpbf/libhttpbf.a)
|
||||
ELSE()
|
||||
FIND_LIBRARY(HTTPBF_LIBRARY NAMES httpbf HINTS $ENV{CSYNC_DIR})
|
||||
ENDIF()
|
||||
|
||||
qt4_wrap_cpp(syncMoc ${libsync_HEADERS})
|
||||
|
||||
list(APPEND libsync_LINK_TARGETS
|
||||
${QT_LIBRARIES}
|
||||
${CSYNC_LIBRARY}
|
||||
${HTTPBF_LIBRARY}
|
||||
dl
|
||||
)
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
list(APPEND libsync_LINK_TARGETS
|
||||
inotify
|
||||
)
|
||||
ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
|
||||
if(QTKEYCHAIN_FOUND)
|
||||
list(APPEND libsync_LINK_TARGETS ${QTKEYCHAIN_LIBRARY})
|
||||
include_directories(${QTKEYCHAIN_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(NEON_FOUND)
|
||||
list(APPEND libsync_LINK_TARGETS ${NEON_LIBRARIES})
|
||||
include_directories(${NEON_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
|
||||
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets Sql)
|
||||
|
||||
set_target_properties( ${synclib_NAME} PROPERTIES
|
||||
VERSION ${MIRALL_VERSION}
|
||||
SOVERSION ${MIRALL_SOVERSION}
|
||||
VERSION ${VERSION}
|
||||
SOVERSION ${SOVERSION}
|
||||
)
|
||||
|
||||
target_link_libraries(${synclib_NAME} ${libsync_LINK_TARGETS} )
|
||||
@@ -220,30 +175,11 @@ else()
|
||||
install(TARGETS ${synclib_NAME} DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
|
||||
endif()
|
||||
|
||||
set(mirall_UI
|
||||
mirall/folderwizardsourcepage.ui
|
||||
mirall/folderwizardtargetpage.ui
|
||||
mirall/sslerrordialog.ui
|
||||
mirall/settingsdialog.ui
|
||||
mirall/generalsettings.ui
|
||||
mirall/networksettings.ui
|
||||
mirall/accountsettings.ui
|
||||
mirall/ignorelisteditor.ui
|
||||
mirall/protocolwidget.ui
|
||||
wizard/owncloudsetupnocredspage.ui
|
||||
wizard/owncloudhttpcredspage.ui
|
||||
wizard/owncloudwizardresultpage.ui
|
||||
wizard/owncloudadvancedsetuppage.ui
|
||||
)
|
||||
|
||||
qt_wrap_ui(mirall_UI_SRCS ${mirall_UI})
|
||||
|
||||
set(mirall_SRCS
|
||||
mirall/application.cpp
|
||||
mirall/systray.cpp
|
||||
mirall/folderwizard.cpp
|
||||
mirall/folderstatusmodel.cpp
|
||||
mirall/protocolwidget.cpp
|
||||
wizard/owncloudwizard.cpp
|
||||
wizard/owncloudsetuppage.cpp
|
||||
wizard/owncloudhttpcredspage.cpp
|
||||
@@ -261,8 +197,7 @@ set(mirall_SRCS
|
||||
mirall/networksettings.cpp
|
||||
mirall/accountsettings.cpp
|
||||
mirall/ignorelisteditor.cpp
|
||||
mirall/owncloudgui.cpp
|
||||
mirall/socketapi.cpp
|
||||
mirall/itemprogressdialog.cpp
|
||||
)
|
||||
|
||||
set(mirall_HEADERS
|
||||
@@ -286,9 +221,7 @@ set(mirall_HEADERS
|
||||
mirall/networksettings.h
|
||||
mirall/accountsettings.h
|
||||
mirall/ignorelisteditor.h
|
||||
mirall/protocolwidget.h
|
||||
mirall/owncloudgui.h
|
||||
mirall/socketapi.h
|
||||
mirall/itemprogressdialog.h
|
||||
)
|
||||
|
||||
if( UNIX AND NOT APPLE)
|
||||
@@ -298,12 +231,12 @@ if( UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
# csync is required.
|
||||
include_directories(${CSYNC_INCLUDE_DIR}/csync ${CSYNC_INCLUDE_DIR} ${CSYNC_INCLUDE_DIR}/httpbf/src ${CSYNC_BUILD_PATH}/src)
|
||||
include_directories(${CSYNC_INCLUDE_DIR}/csync ${CSYNC_INCLUDE_DIR} ${CSYNC_BUILD_PATH}/src)
|
||||
include_directories(${3rdparty_INC})
|
||||
|
||||
qt_wrap_cpp(mirallMoc ${mirall_HEADERS})
|
||||
qt4_wrap_cpp(mirallMoc ${mirall_HEADERS})
|
||||
|
||||
qt_add_translation(mirall_I18N ${TRANSLATIONS})
|
||||
qt4_add_translation(mirall_I18N ${TRANSLATIONS})
|
||||
|
||||
set( final_src
|
||||
${mirall_HEADERS}
|
||||
@@ -333,10 +266,6 @@ kde4_add_app_icon( ownCloud "${theme_dir}/colored/${ICON_APP_NAME}-icon*.png")
|
||||
list(APPEND final_src ${ownCloud})
|
||||
set(ownCloud ${ownCloud_old})
|
||||
|
||||
if (WITH_DBUS)
|
||||
set(ADDITIONAL_APP_MODULES DBus)
|
||||
endif(WITH_DBUS)
|
||||
|
||||
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
|
||||
@@ -355,7 +284,7 @@ endif(NOT WIN32)
|
||||
|
||||
# add_executable( ${APPLICATION_EXECUTABLE} 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})
|
||||
|
||||
else()
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
include(DeployQt4)
|
||||
@@ -365,7 +294,13 @@ else()
|
||||
|
||||
# we must add MACOSX_BUNDLE only if building a bundle
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src})
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
|
||||
|
||||
#FIXME: hardcoded path
|
||||
if ( EXISTS ${CSYNC_BINARY_DIR}/modules/ocsync_owncloud.so )
|
||||
install(FILES ${CSYNC_BINARY_DIR}/modules/ocsync_owncloud.so DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Plugins)
|
||||
else()
|
||||
install(FILES /usr/local/lib/ocsync-0/ocsync_owncloud.so DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Plugins)
|
||||
endif()
|
||||
|
||||
set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
|
||||
install(FILES ${mirall_I18N} DESTINATION ${QM_DIR})
|
||||
@@ -373,6 +308,7 @@ else()
|
||||
install(FILES ${qt_I18N} DESTINATION ${QM_DIR})
|
||||
file(GLOB qtkeychain_I18N ${QT_TRANSLATIONS_DIR}/qtkeychain*.qm)
|
||||
install(FILES ${qtkeychain_I18N} DESTINATION ${QM_DIR})
|
||||
list(APPEND dirs "/usr/local/lib")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -394,7 +330,7 @@ install(TARGETS ${APPLICATION_EXECUTABLE}
|
||||
# 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(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
install_qt4_executable(${OWNCLOUD_OSX_BUNDLE} "qtaccessiblewidgets;qsqlite")
|
||||
install_qt4_executable(${OWNCLOUD_OSX_BUNDLE} "" "${OWNCLOUD_OSX_BUNDLE}/Contents/Plugins/ocsync_owncloud.so" ${dirs})
|
||||
endif()
|
||||
|
||||
find_program(KRAZY2_EXECUTABLE krazy2)
|
||||
@@ -407,17 +343,3 @@ if(KRAZY2_EXECUTABLE)
|
||||
)
|
||||
endif()
|
||||
|
||||
set(owncloudcmd_NAME ${APPLICATION_EXECUTABLE}cmd)
|
||||
set(OWNCLOUDCMD_SRC owncloudcmd/owncloudcmd.cpp)
|
||||
add_executable(${owncloudcmd_NAME} ${OWNCLOUDCMD_SRC})
|
||||
qt5_use_modules(${owncloudcmd_NAME} Network Sql)
|
||||
set_target_properties(${owncloudcmd_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||
target_link_libraries(${owncloudcmd_NAME} ${synclib_NAME})
|
||||
target_link_libraries(${owncloudcmd_NAME} ${CSYNC_LIBRARY})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mirall)
|
||||
install(TARGETS ${owncloudcmd_NAME}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
@@ -19,34 +19,27 @@
|
||||
#include <csync.h>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
class Account;
|
||||
|
||||
class AbstractCredentials : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// No need for virtual destructor - QObject already has one.
|
||||
virtual void syncContextPreInit(CSYNC* ctx) = 0;
|
||||
virtual void syncContextPreStart(CSYNC* ctx) = 0;
|
||||
virtual bool changed(AbstractCredentials* credentials) const = 0;
|
||||
virtual QString authType() const = 0;
|
||||
virtual QString user() const = 0;
|
||||
virtual QNetworkAccessManager* getQNAM() const = 0;
|
||||
virtual bool ready() const = 0;
|
||||
virtual void fetch(Account *account) = 0;
|
||||
virtual bool stillValid(QNetworkReply *reply) = 0;
|
||||
virtual bool fetchFromUser(Account *account) = 0;
|
||||
virtual void persist(Account *account) = 0;
|
||||
/** Invalidates auth token, or password for basic auth */
|
||||
virtual void invalidateToken(Account *account) = 0;
|
||||
|
||||
// No need for virtual destructor - QObject already has one.
|
||||
virtual void syncContextPreInit(CSYNC* ctx) = 0;
|
||||
virtual void syncContextPreStart(CSYNC* ctx) = 0;
|
||||
virtual bool changed(AbstractCredentials* credentials) const = 0;
|
||||
virtual QString authType() const = 0;
|
||||
virtual QNetworkAccessManager* getQNAM() const = 0;
|
||||
virtual bool ready() const = 0;
|
||||
virtual void fetch() = 0;
|
||||
virtual void persistForUrl(const QString& url) = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void fetched();
|
||||
void fetched();
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
@@ -18,12 +19,10 @@
|
||||
#include <QString>
|
||||
#include <QSslCertificate>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "creds/credentialscommon.h"
|
||||
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
@@ -47,7 +46,7 @@ int handleNeonSSLProblems(const char* prompt,
|
||||
int pos = 0;
|
||||
// This is the set of certificates which QNAM accepted, so we should accept
|
||||
// them as well
|
||||
QList<QSslCertificate> certs = AccountManager::instance()->account()->certificateChain();
|
||||
QList<QSslCertificate> certs = ownCloudInfo::instance()->certificateChain();
|
||||
|
||||
while (!certOk && (pos = regexp.indexIn(qPrompt, 1+pos)) != -1) {
|
||||
QString neon_fingerprint = regexp.cap(1);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
|
||||
@@ -35,11 +35,6 @@ QString DummyCredentials::authType() const
|
||||
return QString::fromLatin1("dummy");
|
||||
}
|
||||
|
||||
QString DummyCredentials::user() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QNetworkAccessManager* DummyCredentials::getQNAM() const
|
||||
{
|
||||
return new MirallAccessManager;
|
||||
@@ -50,24 +45,12 @@ bool DummyCredentials::ready() const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyCredentials::stillValid(QNetworkReply *reply)
|
||||
{
|
||||
Q_UNUSED(reply)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyCredentials::fetchFromUser(Account *account)
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
return false;
|
||||
}
|
||||
|
||||
void DummyCredentials::fetch(Account*)
|
||||
void DummyCredentials::fetch()
|
||||
{
|
||||
Q_EMIT(fetched());
|
||||
}
|
||||
|
||||
void DummyCredentials::persist(Account*)
|
||||
void DummyCredentials::persistForUrl(const QString&)
|
||||
{}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -21,21 +21,17 @@ namespace Mirall
|
||||
|
||||
class DummyCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QString user() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
bool stillValid(QNetworkReply *reply);
|
||||
bool fetchFromUser(Account *account);
|
||||
void fetch(Account*);
|
||||
void persist(Account*);
|
||||
void invalidateToken(Account *) {}
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
338
src/creds/http/credentialstore.cpp
Normal file
338
src/creds/http/credentialstore.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* 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 <QtGui>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "creds/http/credentialstore.h"
|
||||
#include "creds/http/httpconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
#include <qtkeychain/keychain.h>
|
||||
using namespace QKeychain;
|
||||
#endif
|
||||
|
||||
#define MAX_LOGIN_ATTEMPTS 3
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
CredentialStore *CredentialStore::_instance=0;
|
||||
CredentialStore::CredState CredentialStore::_state = NotFetched;
|
||||
QString CredentialStore::_passwd = QString::null;
|
||||
QString CredentialStore::_user = QString::null;
|
||||
QString CredentialStore::_url = QString::null;
|
||||
QString CredentialStore::_errorMsg = QString::null;
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
CredentialStore::CredentialType CredentialStore::_type = KeyChain;
|
||||
#else
|
||||
CredentialStore::CredentialType CredentialStore::_type = Settings;
|
||||
#endif
|
||||
|
||||
CredentialStore::CredentialStore(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
CredentialStore *CredentialStore::instance()
|
||||
{
|
||||
if( !CredentialStore::_instance ) CredentialStore::_instance = new CredentialStore;
|
||||
return CredentialStore::_instance;
|
||||
}
|
||||
|
||||
QString CredentialStore::password() const
|
||||
{
|
||||
return _passwd;
|
||||
}
|
||||
QString CredentialStore::user() const
|
||||
{
|
||||
return _user;
|
||||
}
|
||||
|
||||
CredentialStore::CredState CredentialStore::state()
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
void CredentialStore::fetchCredentials()
|
||||
{
|
||||
HttpConfigFile cfgFile;
|
||||
|
||||
bool ok = false;
|
||||
QString pwd;
|
||||
_user = cfgFile.user();
|
||||
_url = cfgFile.ownCloudUrl();
|
||||
|
||||
QString key = keyChainKey(_url);
|
||||
|
||||
if( key.isNull() ) {
|
||||
qDebug() << "Can not fetch credentials, url is zero!";
|
||||
_state = Error;
|
||||
emit( fetchCredentialsFinished(false) );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( _type ) {
|
||||
case CredentialStore::Settings: {
|
||||
/* Read from config file. */
|
||||
_state = Fetching;
|
||||
cfgFile.fixupOldPassword();
|
||||
if( cfgFile.passwordExists() ) {
|
||||
pwd = cfgFile.password();
|
||||
ok = true;
|
||||
} else {
|
||||
ok = false;
|
||||
_state = EntryNotFound;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CredentialStore::KeyChain: {
|
||||
// If the credentials are here already, return.
|
||||
if( _state == Ok || _state == AsyncWriting ) {
|
||||
emit(fetchCredentialsFinished(true));
|
||||
return;
|
||||
}
|
||||
// otherwise fetch asynchronious.
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
_state = AsyncFetching;
|
||||
if( !_user.isEmpty() ) {
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
job->setKey( key );
|
||||
|
||||
connect( job, SIGNAL(finished(QKeychain::Job*)), this,
|
||||
SLOT(slotKeyChainReadFinished(QKeychain::Job*)));
|
||||
job->start();
|
||||
}
|
||||
#else
|
||||
qDebug() << "QtKeyChain: Not yet implemented!";
|
||||
_state = Error;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( _state == Fetching ) { // ...but not AsyncFetching
|
||||
if( ok ) {
|
||||
_passwd = pwd;
|
||||
_state = Ok;
|
||||
}
|
||||
if( !ok && _state == Fetching ) {
|
||||
_state = Error;
|
||||
}
|
||||
|
||||
emit( fetchCredentialsFinished(ok) );
|
||||
} else {
|
||||
// in case of AsyncFetching nothing happens here. The finished-Slot
|
||||
// will emit the finish signal.
|
||||
}
|
||||
}
|
||||
|
||||
void CredentialStore::reset()
|
||||
{
|
||||
_state = NotFetched;
|
||||
_user = QString::null;
|
||||
_passwd = QString::null;
|
||||
}
|
||||
|
||||
QString CredentialStore::keyChainKey( const QString& url ) const
|
||||
{
|
||||
QString u(url);
|
||||
if( u.isEmpty() ) {
|
||||
qDebug() << "Empty url in keyChain, error!";
|
||||
return QString::null;
|
||||
}
|
||||
if( _user.isEmpty() ) {
|
||||
qDebug() << "Error: User is emty!";
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
if( !u.endsWith(QChar('/')) ) {
|
||||
u.append(QChar('/'));
|
||||
}
|
||||
|
||||
QString key = _user+QLatin1Char(':')+u;
|
||||
return key;
|
||||
}
|
||||
|
||||
void CredentialStore::slotKeyChainReadFinished(QKeychain::Job* job)
|
||||
{
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
ReadPasswordJob *pwdJob = static_cast<ReadPasswordJob*>(job);
|
||||
if( pwdJob ) {
|
||||
switch( pwdJob->error() ) {
|
||||
case QKeychain::NoError:
|
||||
_passwd = pwdJob->textData();
|
||||
#ifdef Q_OS_LINUX
|
||||
// Currently there is a bug in the keychain on linux that if no
|
||||
// entry is there, an empty password comes back, but no error.
|
||||
if( _passwd.isEmpty() ) {
|
||||
_state = EntryNotFound;
|
||||
_errorMsg = tr("No password entry found in keychain. Please reconfigure.");
|
||||
} else
|
||||
#endif
|
||||
_state = Ok;
|
||||
break;
|
||||
case QKeychain::EntryNotFound:
|
||||
_state = EntryNotFound;
|
||||
break;
|
||||
case QKeychain::CouldNotDeleteEntry:
|
||||
_state = Error;
|
||||
break;
|
||||
case QKeychain::AccessDenied:
|
||||
_state = AccessDenied;
|
||||
break;
|
||||
case QKeychain::NoBackendAvailable:
|
||||
_state = NoKeychainBackend;
|
||||
break;
|
||||
case QKeychain::NotImplemented:
|
||||
_state = NoKeychainBackend;
|
||||
break;
|
||||
case QKeychain::OtherError:
|
||||
default:
|
||||
_state = Error;
|
||||
|
||||
}
|
||||
/* In case there is no backend, tranparentely switch to Settings file. */
|
||||
if( _state == NoKeychainBackend ) {
|
||||
qDebug() << "No Storage Backend, falling back to Settings mode.";
|
||||
_type = CredentialStore::Settings;
|
||||
fetchCredentials();
|
||||
return;
|
||||
}
|
||||
|
||||
if( _state == EntryNotFound ) {
|
||||
// try to migrate.
|
||||
}
|
||||
|
||||
if( _state != Ok ) {
|
||||
qDebug() << "Error with keychain: " << pwdJob->errorString();
|
||||
if(_errorMsg.isEmpty()) _errorMsg = pwdJob->errorString();
|
||||
} else {
|
||||
_errorMsg = QString::null;
|
||||
}
|
||||
} else {
|
||||
_state = Error;
|
||||
qDebug() << "Error: KeyChain Read Password Job failed!";
|
||||
}
|
||||
emit(fetchCredentialsFinished(_state == Ok));
|
||||
#else
|
||||
(void) job;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString CredentialStore::errorMessage()
|
||||
{
|
||||
return _errorMsg;
|
||||
}
|
||||
|
||||
void CredentialStore::setCredentials( const QString& url, const QString& user,
|
||||
const QString& pwd )
|
||||
{
|
||||
_passwd = pwd;
|
||||
_user = user;
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
_type = KeyChain;
|
||||
#else
|
||||
_type = Settings;
|
||||
#endif
|
||||
_url = url;
|
||||
_state = Ok;
|
||||
}
|
||||
|
||||
void CredentialStore::saveCredentials( )
|
||||
{
|
||||
HttpConfigFile cfgFile;
|
||||
QString key = keyChainKey(_url);
|
||||
if( key.isNull() ) {
|
||||
qDebug() << "Error: Can not save credentials, URL is zero!";
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
#endif
|
||||
|
||||
cfgFile.setUser(_user);
|
||||
switch( _type ) {
|
||||
case CredentialStore::KeyChain: {
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||
// Set password in KeyChain
|
||||
job->setKey( key );
|
||||
job->setTextData(_passwd);
|
||||
|
||||
connect( job, SIGNAL(finished(QKeychain::Job*)), this,
|
||||
SLOT(slotKeyChainWriteFinished(QKeychain::Job*)));
|
||||
_state = AsyncWriting;
|
||||
job->start();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case CredentialStore::Settings:
|
||||
cfgFile.setPassword( _passwd );
|
||||
reset();
|
||||
break;
|
||||
default:
|
||||
// unsupported.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CredentialStore::slotKeyChainWriteFinished( QKeychain::Job *job )
|
||||
{
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
WritePasswordJob *pwdJob = static_cast<WritePasswordJob*>(job);
|
||||
if( pwdJob ) {
|
||||
QKeychain::Error err = pwdJob->error();
|
||||
|
||||
if( err != QKeychain::NoError ) {
|
||||
qDebug() << "Error with keychain: " << pwdJob->errorString();
|
||||
if( err == NoBackendAvailable || err == NotImplemented ||
|
||||
pwdJob->errorString().contains(QLatin1String("Could not open wallet"))) {
|
||||
_state = NoKeychainBackend;
|
||||
_type = Settings;
|
||||
saveCredentials();
|
||||
} else {
|
||||
_state = Error;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Successfully stored password for user " << _user;
|
||||
// Try to remove password formerly stored in the config file.
|
||||
HttpConfigFile cfgFile;
|
||||
cfgFile.removePassword();
|
||||
_state = NotFetched;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Error: KeyChain Write Password Job failed!";
|
||||
_state = Error;
|
||||
}
|
||||
#else
|
||||
(void) job;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Called if a user chooses to not store the password locally.
|
||||
void CredentialStore::deleteKeyChainCredential( const QString& key )
|
||||
{
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
// Start the remove job, do not care so much about the result.
|
||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||
job->setKey( key );
|
||||
job->start();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
132
src/creds/http/credentialstore.h
Normal file
132
src/creds/http/credentialstore.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 CREDENTIALSTORE_H
|
||||
#define CREDENTIALSTORE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QInputDialog>
|
||||
|
||||
namespace QKeychain {
|
||||
class Job;
|
||||
}
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
/*
|
||||
* This object holds the credential information of the ownCloud connection. It
|
||||
* is implemented as a singleton.
|
||||
* At startup of the client, at first the fetchCredentials() method must be called
|
||||
* which tries to get credentials from one of the supported backends. To determine
|
||||
* which backend should be used, MirallConfigFile::credentialType() is called as
|
||||
* the backend is configured in the config file.
|
||||
*
|
||||
* The fetchCredentials() call changes the internal state of the credential store
|
||||
* to one of
|
||||
* Ok: There are credentials. Note that it's unknown if they are correct!!
|
||||
* Fetching: The fetching is not yet finished.
|
||||
* EntryNotFound: No password entry found in the storage.
|
||||
* Error: A general error happened.
|
||||
* After fetching has finished, signal fetchCredentialsFinished(bool) is emitted.
|
||||
* The result can be retrieved with state() and password() and user() methods.
|
||||
*/
|
||||
|
||||
class CredentialStore : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum CredState { NotFetched = 0,
|
||||
Ok,
|
||||
Fetching,
|
||||
AsyncFetching,
|
||||
EntryNotFound,
|
||||
AccessDenied,
|
||||
NoKeychainBackend,
|
||||
Error,
|
||||
AsyncWriting };
|
||||
|
||||
enum CredentialType {
|
||||
Settings = 0,
|
||||
KeyChain
|
||||
};
|
||||
|
||||
QString password( ) const;
|
||||
QString user( ) const;
|
||||
|
||||
/**
|
||||
* @brief state
|
||||
* @return the state of the Credentialstore.
|
||||
*/
|
||||
CredState state();
|
||||
|
||||
/**
|
||||
* @brief fetchCredentials - start to retrieve user credentials.
|
||||
*
|
||||
* This method must be called first to retrieve the credentials.
|
||||
* At the end, this method emits the fetchKeyChainFinished() signal.
|
||||
*/
|
||||
void fetchCredentials();
|
||||
|
||||
/**
|
||||
* @brief instance - singleton pointer.
|
||||
* @return the singleton pointer to access the object.
|
||||
*/
|
||||
static CredentialStore *instance();
|
||||
|
||||
/**
|
||||
* @brief setCredentials - sets the user credentials.
|
||||
*
|
||||
* This function is called from the setup wizard to set the credentials
|
||||
* int this store. Note that it does not store the password.
|
||||
* The function also sets the state to ok.
|
||||
* @param url - the connection url
|
||||
* @param user - the user name
|
||||
*/
|
||||
void setCredentials( const QString& url, const QString& user, const QString& pwd);
|
||||
|
||||
void saveCredentials( );
|
||||
|
||||
QString errorMessage();
|
||||
|
||||
void reset();
|
||||
signals:
|
||||
/**
|
||||
* @brief fetchCredentialsFinished
|
||||
*
|
||||
* emitted as soon as the fetching of the credentials has finished.
|
||||
* If the parameter is true, there is a password and user. This does
|
||||
* however, not say if the credentials are valid log in data.
|
||||
* If false, the user pressed cancel.
|
||||
*/
|
||||
void fetchCredentialsFinished(bool);
|
||||
|
||||
protected slots:
|
||||
void slotKeyChainReadFinished( QKeychain::Job* );
|
||||
void slotKeyChainWriteFinished( QKeychain::Job* );
|
||||
|
||||
private:
|
||||
explicit CredentialStore(QObject *parent = 0);
|
||||
void deleteKeyChainCredential( const QString& );
|
||||
QString keyChainKey( const QString& ) const;
|
||||
|
||||
static CredentialStore *_instance;
|
||||
static CredState _state;
|
||||
static QString _passwd;
|
||||
static QString _user;
|
||||
static QString _url;
|
||||
static QString _errorMsg;
|
||||
static CredentialType _type;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CREDENTIALSTORE_H
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
@@ -14,23 +15,14 @@
|
||||
*/
|
||||
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QSettings>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include <qtkeychain/keychain.h>
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "creds/httpcredentials.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "creds/http/credentialstore.h"
|
||||
#include "creds/credentialscommon.h"
|
||||
#include "creds/httpcredentials.h"
|
||||
|
||||
using namespace QKeychain;
|
||||
|
||||
Q_DECLARE_METATYPE(Mirall::Account*)
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
@@ -47,8 +39,8 @@ int getauth(const char *prompt,
|
||||
{
|
||||
int re = 0;
|
||||
QMutex mutex;
|
||||
// ### safe?
|
||||
HttpCredentials* http_credentials = qobject_cast<HttpCredentials*>(AccountManager::instance()->account()->credentials());
|
||||
MirallConfigFile cfg;
|
||||
HttpCredentials* http_credentials = dynamic_cast< HttpCredentials* > (cfg.getCredentials());
|
||||
|
||||
if (!http_credentials) {
|
||||
qDebug() << "Not a HTTP creds instance!";
|
||||
@@ -73,8 +65,6 @@ int getauth(const char *prompt,
|
||||
return re;
|
||||
}
|
||||
|
||||
const char userC[] = "user";
|
||||
|
||||
} // ns
|
||||
|
||||
class HttpCredentialsAccessManager : public MirallAccessManager {
|
||||
@@ -86,8 +76,7 @@ protected:
|
||||
QByteArray credHash = QByteArray(_cred->user().toUtf8()+":"+_cred->password().toUtf8()).toBase64();
|
||||
QNetworkRequest req(request);
|
||||
req.setRawHeader(QByteArray("Authorization"), QByteArray("Basic ") + credHash);
|
||||
//qDebug() << "Request for " << req.url() << "with authorization" << QByteArray::fromBase64(credHash);
|
||||
return MirallAccessManager::createRequest(op, req, outgoingData);
|
||||
return MirallAccessManager::createRequest(op, req, outgoingData);\
|
||||
}
|
||||
private:
|
||||
const HttpCredentials *_cred;
|
||||
@@ -103,8 +92,7 @@ HttpCredentials::HttpCredentials(const QString& user, const QString& password)
|
||||
: _user(user),
|
||||
_password(password),
|
||||
_ready(true)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
void HttpCredentials::syncContextPreInit (CSYNC* ctx)
|
||||
{
|
||||
@@ -117,7 +105,7 @@ void HttpCredentials::syncContextPreStart (CSYNC* ctx)
|
||||
// any way to get "session_key" module property from csync. Had we
|
||||
// have it, then we could remove this code and keep it in
|
||||
// csyncthread code (or folder code, git remembers).
|
||||
QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
|
||||
QList<QNetworkCookie> cookies(ownCloudInfo::instance()->getLastAuthCookies());
|
||||
QString cookiesAsString;
|
||||
|
||||
// Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
|
||||
@@ -136,7 +124,7 @@ bool HttpCredentials::changed(AbstractCredentials* credentials) const
|
||||
{
|
||||
HttpCredentials* other(dynamic_cast< HttpCredentials* >(credentials));
|
||||
|
||||
if (!other || (other->user() != this->user())) {
|
||||
if (!other || other->user() != this->user()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -173,112 +161,35 @@ bool HttpCredentials::ready() const
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void HttpCredentials::fetch(Account *account)
|
||||
void HttpCredentials::fetch()
|
||||
{
|
||||
_user = account->credentialSetting(QLatin1String(userC)).toString();
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
job->setSettings(account->settingsWithGroup(Theme::instance()->appName()));
|
||||
job->setInsecureFallback(true);
|
||||
job->setKey(keychainKey(account->url().toString(), _user));
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
|
||||
job->setProperty("account", QVariant::fromValue(account));
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
bool HttpCredentials::stillValid(QNetworkReply *reply)
|
||||
{
|
||||
return ((reply->error() != QNetworkReply::AuthenticationRequiredError)
|
||||
// returned if user or password is incorrect
|
||||
&& (reply->error() != QNetworkReply::OperationCanceledError));
|
||||
}
|
||||
|
||||
bool HttpCredentials::fetchFromUser(Account *account)
|
||||
{
|
||||
bool ok = false;
|
||||
QString password = queryPassword(&ok);
|
||||
if (ok) {
|
||||
_password = password;
|
||||
_ready = true;
|
||||
persist(account);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
|
||||
{
|
||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(job);
|
||||
delete readJob->settings();
|
||||
_password = readJob->textData();
|
||||
QKeychain::Error error = job->error();
|
||||
switch (error) {
|
||||
case NoError:
|
||||
_ready = true;
|
||||
Q_EMIT fetched();
|
||||
break;
|
||||
default:
|
||||
if (!_user.isEmpty()) {
|
||||
bool ok;
|
||||
QString pwd = queryPassword(&ok);
|
||||
if (ok) {
|
||||
_password = pwd;
|
||||
_ready = true;
|
||||
persist(qvariant_cast<Account*>(readJob->property("account")));
|
||||
Q_EMIT fetched();
|
||||
break;
|
||||
}
|
||||
}
|
||||
qDebug() << "Error while reading password" << job->errorString();
|
||||
// TODO: merge CredentialStore into HttpCredentials?
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
connect(store, SIGNAL(fetchCredentialsFinished(bool)),
|
||||
this, SLOT(slotCredentialsFetched(bool)));
|
||||
store->fetchCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
QString HttpCredentials::queryPassword(bool *ok)
|
||||
void HttpCredentials::persistForUrl(const QString& url)
|
||||
{
|
||||
qDebug() << AccountManager::instance()->account()->state();
|
||||
if (ok) {
|
||||
QString str = QInputDialog::getText(0, tr("Enter Password"),
|
||||
tr("Please enter %1 password for user '%2':")
|
||||
.arg(Theme::instance()->appNameGUI(), _user),
|
||||
QLineEdit::Password, QString(), ok);
|
||||
qDebug() << AccountManager::instance()->account()->state();
|
||||
return str;
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpCredentials::invalidateToken(Account *account)
|
||||
{
|
||||
_password = QString();
|
||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||
job->setKey(keychainKey(account->url().toString(), _user));
|
||||
job->start();
|
||||
_ready = false;
|
||||
}
|
||||
|
||||
void HttpCredentials::persist(Account *account)
|
||||
{
|
||||
account->setCredentialSetting(QLatin1String(userC), _user);
|
||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||
job->setSettings(account->settingsWithGroup(Theme::instance()->appName()));
|
||||
job->setInsecureFallback(true);
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteJobDone(QKeychain::Job*)));
|
||||
job->setKey(keychainKey(account->url().toString(), _user));
|
||||
job->setTextData(_password);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotWriteJobDone(QKeychain::Job *job)
|
||||
{
|
||||
delete job->settings();
|
||||
switch (job->error()) {
|
||||
case NoError:
|
||||
break;
|
||||
default:
|
||||
qDebug() << "Error while writing password" << job->errorString();
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
store->setCredentials(url, _user, _password);
|
||||
store->saveCredentials();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotCredentialsFetched(bool ok)
|
||||
{
|
||||
_ready = ok;
|
||||
if (_ready) {
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
_user = store->user();
|
||||
_password = store->password();
|
||||
}
|
||||
Q_EMIT fetched();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* authenticator)
|
||||
@@ -291,24 +202,12 @@ void HttpCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* a
|
||||
reply->close();
|
||||
}
|
||||
|
||||
QString HttpCredentials::keychainKey(const QString &url, const QString &user)
|
||||
void HttpCredentials::slotReplyFinished()
|
||||
{
|
||||
QString u(url);
|
||||
if( u.isEmpty() ) {
|
||||
qDebug() << "Empty url in keyChain, error!";
|
||||
return QString::null;
|
||||
}
|
||||
if( user.isEmpty() ) {
|
||||
qDebug() << "Error: User is emty!";
|
||||
return QString::null;
|
||||
}
|
||||
QNetworkReply* reply = qobject_cast< QNetworkReply* >(sender());
|
||||
|
||||
if( !u.endsWith(QChar('/')) ) {
|
||||
u.append(QChar('/'));
|
||||
}
|
||||
|
||||
QString key = user+QLatin1Char(':')+u;
|
||||
return key;
|
||||
disconnect(reply, SIGNAL(finished()),
|
||||
this, SLOT(slotReplyFinished()));
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
@@ -23,46 +24,38 @@
|
||||
class QNetworkReply;
|
||||
class QAuthenticator;
|
||||
|
||||
namespace QKeychain {
|
||||
class Job;
|
||||
}
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class HttpCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HttpCredentials();
|
||||
HttpCredentials(const QString& user, const QString& password);
|
||||
HttpCredentials();
|
||||
HttpCredentials(const QString& user, const QString& password);
|
||||
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch(Account *account);
|
||||
bool stillValid(QNetworkReply *reply);
|
||||
bool fetchFromUser(Account *account);
|
||||
void persist(Account *account);
|
||||
QString user() const;
|
||||
QString password() const;
|
||||
QString queryPassword(bool *ok);
|
||||
void invalidateToken(Account *account);
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
|
||||
QString user() const;
|
||||
QString password() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||
void slotReadJobDone(QKeychain::Job*);
|
||||
void slotWriteJobDone(QKeychain::Job*);
|
||||
void slotCredentialsFetched(bool);
|
||||
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||
void slotReplyFinished();
|
||||
|
||||
private:
|
||||
static QString keychainKey(const QString &url, const QString &user);
|
||||
QString _user;
|
||||
QString _password;
|
||||
bool _ready;
|
||||
QString _user;
|
||||
QString _password;
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkCookieJar>
|
||||
|
||||
#include "creds/shibboleth/shibbolethaccessmanager.h"
|
||||
|
||||
|
||||
@@ -13,16 +13,14 @@
|
||||
|
||||
#include <QEventLoop>
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "creds/shibboleth/shibbolethrefresher.h"
|
||||
#include "creds/shibbolethcredentials.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
ShibbolethRefresher::ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
|
||||
ShibbolethRefresher::ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
|
||||
: QObject(parent),
|
||||
_account(account),
|
||||
_creds(creds),
|
||||
_csync_ctx(csync_ctx)
|
||||
{}
|
||||
@@ -35,8 +33,7 @@ void ShibbolethRefresher::refresh()
|
||||
this, SLOT(onInvalidatedAndFetched(QByteArray)));
|
||||
connect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
&loop, SLOT(quit()));
|
||||
QMetaObject::invokeMethod(_creds, "invalidateAndFetch",Qt::QueuedConnection,
|
||||
Q_ARG(Account*, _account));
|
||||
QMetaObject::invokeMethod(_creds, "invalidateAndFetch", Qt::QueuedConnection);
|
||||
loop.exec();
|
||||
disconnect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
&loop, SLOT(quit()));
|
||||
|
||||
@@ -23,7 +23,6 @@ class QByteArray;
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class Account;
|
||||
class ShibbolethCredentials;
|
||||
|
||||
class ShibbolethRefresher : public QObject
|
||||
@@ -31,7 +30,7 @@ class ShibbolethRefresher : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
|
||||
ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
|
||||
|
||||
void refresh();
|
||||
|
||||
@@ -39,7 +38,6 @@ private Q_SLOTS:
|
||||
void onInvalidatedAndFetched(const QByteArray& cookieData);
|
||||
|
||||
private:
|
||||
Account* _account;
|
||||
ShibbolethCredentials* _creds;
|
||||
CSYNC* _csync_ctx;
|
||||
};
|
||||
|
||||
@@ -16,25 +16,17 @@
|
||||
#include <QNetworkCookie>
|
||||
#include <QWebFrame>
|
||||
#include <QWebPage>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "creds/shibboleth/shibbolethcookiejar.h"
|
||||
#include "creds/shibboleth/shibbolethwebview.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
void ShibbolethWebView::setup(Account *account, ShibbolethCookieJar* jar)
|
||||
void ShibbolethWebView::setup(const QUrl& url, ShibbolethCookieJar* jar)
|
||||
{
|
||||
MirallAccessManager* nm = new MirallAccessManager(this);
|
||||
// we need our own QNAM, but the we offload the SSL error handling to
|
||||
// the account object, which already can do this
|
||||
connect(nm, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
|
||||
account, SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
|
||||
|
||||
QWebPage* page = new QWebPage(this);
|
||||
|
||||
jar->setParent(this);
|
||||
@@ -43,19 +35,18 @@ void ShibbolethWebView::setup(Account *account, ShibbolethCookieJar* jar)
|
||||
connect(page, SIGNAL(loadStarted()),
|
||||
this, SLOT(slotLoadStarted()));
|
||||
connect(page, SIGNAL(loadFinished(bool)),
|
||||
this, SLOT(slotLoadFinished(bool)));
|
||||
this, SLOT(slotLoadFinished()));
|
||||
|
||||
nm->setCookieJar(jar);
|
||||
page->setNetworkAccessManager(nm);
|
||||
page->mainFrame()->load(account->url());
|
||||
this->setPage(page);
|
||||
setWindowTitle(tr("%1 - Authenticate").arg(Theme::instance()->appNameGUI()));
|
||||
page->mainFrame ()->load (url);
|
||||
this->setPage (page);
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(Account* account, QWidget* parent)
|
||||
ShibbolethWebView::ShibbolethWebView(const QUrl& url, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(account, new ShibbolethCookieJar(this));
|
||||
setup(url, new ShibbolethCookieJar(this));
|
||||
}
|
||||
|
||||
ShibbolethWebView::~ShibbolethWebView()
|
||||
@@ -63,10 +54,10 @@ ShibbolethWebView::~ShibbolethWebView()
|
||||
slotLoadFinished();
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(Account* account, ShibbolethCookieJar* jar, QWidget* parent)
|
||||
ShibbolethWebView::ShibbolethWebView(const QUrl& url, ShibbolethCookieJar* jar, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(account, jar);
|
||||
setup(url, jar);
|
||||
}
|
||||
|
||||
void ShibbolethWebView::onNewCookiesForUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url)
|
||||
@@ -105,20 +96,9 @@ void ShibbolethWebView::slotLoadStarted()
|
||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||
}
|
||||
|
||||
void ShibbolethWebView::slotLoadFinished(bool success)
|
||||
void ShibbolethWebView::slotLoadFinished()
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
if (!title().isNull()) {
|
||||
setWindowTitle(tr("%1 - %2").arg(Theme::instance()->appNameGUI(), title()));
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
QMessageBox::critical(this, tr("Error loading IdP login page"),
|
||||
tr("Could not load Shibboleth login page to log you in.\n"
|
||||
"Please ensure that your network connection is working."));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -24,15 +24,14 @@ namespace Mirall
|
||||
{
|
||||
|
||||
class ShibbolethCookieJar;
|
||||
class Account;
|
||||
|
||||
class ShibbolethWebView : public QWebView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethWebView(Account *account, QWidget* parent = 0);
|
||||
ShibbolethWebView(Account *account, ShibbolethCookieJar* jar, QWidget* parent = 0);
|
||||
ShibbolethWebView(const QUrl& url, QWidget* parent = 0);
|
||||
ShibbolethWebView(const QUrl& url, ShibbolethCookieJar* jar, QWidget* parent = 0);
|
||||
~ShibbolethWebView();
|
||||
|
||||
protected:
|
||||
@@ -46,10 +45,10 @@ Q_SIGNALS:
|
||||
private Q_SLOTS:
|
||||
void onNewCookiesForUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url);
|
||||
void slotLoadStarted();
|
||||
void slotLoadFinished(bool success = true);
|
||||
void slotLoadFinished();
|
||||
|
||||
private:
|
||||
void setup(Account *account, ShibbolethCookieJar* jar);
|
||||
void setup(const QUrl& url, ShibbolethCookieJar* jar);
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -12,16 +12,14 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMutex>
|
||||
|
||||
#include "creds/shibbolethcredentials.h"
|
||||
#include "creds/shibboleth/shibbolethaccessmanager.h"
|
||||
#include "creds/shibboleth/shibbolethwebview.h"
|
||||
#include "creds/shibboleth/shibbolethrefresher.h"
|
||||
#include "creds/shibboleth/shibbolethconfigfile.h"
|
||||
#include "creds/credentialscommon.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
@@ -46,16 +44,15 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx,
|
||||
|
||||
QMutex mutex;
|
||||
QMutexLocker locker(&mutex);
|
||||
Account *account = AccountManager::instance()->account();
|
||||
ShibbolethCredentials* creds = qobject_cast<ShibbolethCredentials*>(account->credentials());
|
||||
|
||||
MirallConfigFile cfg;
|
||||
ShibbolethCredentials* creds = dynamic_cast< ShibbolethCredentials* > (cfg.getCredentials());
|
||||
|
||||
if (!creds) {
|
||||
qDebug() << "Not a Shibboleth creds instance!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
ShibbolethRefresher refresher(account, creds, csync_ctx);
|
||||
ShibbolethRefresher refresher(creds, csync_ctx);
|
||||
|
||||
// blocks
|
||||
refresher.refresh();
|
||||
@@ -66,9 +63,7 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx,
|
||||
} // ns
|
||||
|
||||
ShibbolethCredentials::ShibbolethCredentials()
|
||||
: AbstractCredentials(),
|
||||
_url(),
|
||||
_shibCookie(),
|
||||
: _shibCookie(),
|
||||
_ready(false),
|
||||
_browser(0),
|
||||
_otherCookies()
|
||||
@@ -93,7 +88,7 @@ QByteArray ShibbolethCredentials::prepareCookieData() const
|
||||
// have any way to get "session_key" module property from
|
||||
// csync. Had we have it, then we could just append shibboleth
|
||||
// cookies to the "session_key" value and set it in csync module.
|
||||
QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
|
||||
QList<QNetworkCookie> cookies(ownCloudInfo::instance()->getLastAuthCookies());
|
||||
QMap<QString, QString> uniqueCookies;
|
||||
|
||||
cookies << _shibCookie;
|
||||
@@ -147,14 +142,6 @@ QString ShibbolethCredentials::authType() const
|
||||
return QString::fromLatin1("shibboleth");
|
||||
}
|
||||
|
||||
QString ShibbolethCredentials::user() const
|
||||
{
|
||||
// ### TODO: If we had a way to extract the currently authenticated user
|
||||
// somehow, we could return its id token (email) here (stored in REMOTE_USER)
|
||||
// The server doesn't return it by default
|
||||
return QString();
|
||||
}
|
||||
|
||||
QNetworkCookie ShibbolethCredentials::cookie() const
|
||||
{
|
||||
return _shibCookie;
|
||||
@@ -174,16 +161,14 @@ bool ShibbolethCredentials::ready() const
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::fetch(Account *account)
|
||||
void ShibbolethCredentials::fetch()
|
||||
{
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
ShibbolethConfigFile cfg;
|
||||
if (account) {
|
||||
_url = account->url();
|
||||
}
|
||||
_browser = new ShibbolethWebView(account, cfg.createCookieJar());
|
||||
|
||||
_browser = new ShibbolethWebView(QUrl(cfg.ownCloudUrl()), cfg.createCookieJar());
|
||||
connect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie)),
|
||||
this, SLOT(onShibbolethCookieReceived(QNetworkCookie)));
|
||||
connect(_browser, SIGNAL(viewHidden()),
|
||||
@@ -192,33 +177,13 @@ void ShibbolethCredentials::fetch(Account *account)
|
||||
}
|
||||
}
|
||||
|
||||
bool ShibbolethCredentials::stillValid(QNetworkReply *reply)
|
||||
{
|
||||
Q_UNUSED(reply)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShibbolethCredentials::fetchFromUser(Account *account)
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::persist(Account* /*account*/)
|
||||
void ShibbolethCredentials::persistForUrl(const QString& /*url*/)
|
||||
{
|
||||
ShibbolethConfigFile cfg;
|
||||
|
||||
cfg.storeCookies(_otherCookies);
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::invalidateToken(Account *account)
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
_shibCookie.setValue("");
|
||||
// ### access to ctx missing, but might not be required at all
|
||||
//csync_set_module_property(ctx, "session_key", "");
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::disposeBrowser()
|
||||
{
|
||||
disconnect(_browser, SIGNAL(viewHidden()),
|
||||
@@ -247,15 +212,12 @@ void ShibbolethCredentials::slotBrowserHidden()
|
||||
Q_EMIT fetched();
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::invalidateAndFetch(Account* account)
|
||||
void ShibbolethCredentials::invalidateAndFetch()
|
||||
{
|
||||
_ready = false;
|
||||
connect (this, SIGNAL(fetched()),
|
||||
this, SLOT(onFetched()));
|
||||
// small hack to support the ShibbolethRefresher hack
|
||||
// we already rand fetch() with a valid account object,
|
||||
// and hence know the url on refresh
|
||||
fetch(account);
|
||||
fetch();
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::onFetched()
|
||||
|
||||
@@ -38,19 +38,15 @@ public:
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QString user() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch(Account *account);
|
||||
bool stillValid(QNetworkReply *reply);
|
||||
virtual bool fetchFromUser(Account *account);
|
||||
void persist(Account *account);
|
||||
void invalidateToken(Account *account);
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
|
||||
QNetworkCookie cookie() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void invalidateAndFetch(Account *account);
|
||||
void invalidateAndFetch();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onShibbolethCookieReceived(const QNetworkCookie& cookie);
|
||||
@@ -62,7 +58,6 @@ Q_SIGNALS:
|
||||
void invalidatedAndFetched(const QByteArray& cookieData);
|
||||
|
||||
private:
|
||||
QUrl _url;
|
||||
QByteArray prepareCookieData() const;
|
||||
void disposeBrowser();
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
#include <signal.h>
|
||||
|
||||
#include "mirall/application.h"
|
||||
#include "mirall/theme.h"
|
||||
@@ -36,9 +35,7 @@ int main(int argc, char **argv)
|
||||
|
||||
Mirall::Application app(argc, argv);
|
||||
app.initialize();
|
||||
#ifndef Q_OS_WIN
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
if( app.giveHelp() ) {
|
||||
app.showHelp();
|
||||
return 0;
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Daniel Molkentin <danimo@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 "mirall/account.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "creds/credentialsfactory.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QMutex>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QSslSocket>
|
||||
#include <QNetworkCookieJar>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
static const char urlC[] = "url";
|
||||
static const char authTypeC[] = "authType";
|
||||
static const char userC[] = "user";
|
||||
static const char httpUserC[] = "http_user";
|
||||
|
||||
AccountManager *AccountManager::_instance = 0;
|
||||
|
||||
AccountManager *AccountManager::instance()
|
||||
{
|
||||
static QMutex mutex;
|
||||
if (!_instance)
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (!_instance) {
|
||||
_instance = new AccountManager;
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void AccountManager::setAccount(Account *account)
|
||||
{
|
||||
emit accountAboutToChange(account, _account);
|
||||
std::swap(_account, account);
|
||||
emit accountChanged(_account, account);
|
||||
}
|
||||
|
||||
|
||||
Account::Account(AbstractSslErrorHandler *sslErrorHandler, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _sslErrorHandler(sslErrorHandler)
|
||||
, _am(0)
|
||||
, _credentials(0)
|
||||
, _treatSslErrorsAsFailure(false)
|
||||
, _state(Account::Disconnected)
|
||||
{
|
||||
}
|
||||
|
||||
Account::~Account()
|
||||
{
|
||||
}
|
||||
|
||||
void Account::save()
|
||||
{
|
||||
QScopedPointer<QSettings> settings(settingsWithGroup(Theme::instance()->appName()));
|
||||
settings->setValue(QLatin1String(urlC), _url.toString());
|
||||
if (_credentials) {
|
||||
_credentials->persist(this);
|
||||
Q_FOREACH(QString key, _settingsMap.keys()) {
|
||||
settings->setValue(key, _settingsMap.value(key));
|
||||
}
|
||||
settings->setValue(QLatin1String(authTypeC), _credentials->authType());
|
||||
|
||||
// HACK: Save http_user also as user
|
||||
if (_settingsMap.contains(httpUserC))
|
||||
settings->setValue(userC, _settingsMap.value(httpUserC));
|
||||
}
|
||||
settings->sync();
|
||||
|
||||
// ### TODO port away from MirallConfigFile
|
||||
MirallConfigFile cfg;
|
||||
qDebug() << "Saving " << approvedCerts().count() << " unknown certs.";
|
||||
QByteArray certs;
|
||||
Q_FOREACH( const QSslCertificate& cert, approvedCerts() ) {
|
||||
certs += cert.toPem() + '\n';
|
||||
}
|
||||
if (!certs.isEmpty()) {
|
||||
cfg.setCaCerts( certs );
|
||||
}
|
||||
}
|
||||
|
||||
Account* Account::restore()
|
||||
{
|
||||
QScopedPointer<QSettings> settings(settingsWithGroup(Theme::instance()->appName()));
|
||||
if (!settings->childKeys().isEmpty()) {
|
||||
Account *acc = new Account;
|
||||
MirallConfigFile cfg;
|
||||
acc->setApprovedCerts(QSslCertificate::fromData(cfg.caCerts()));
|
||||
acc->setUrl(settings->value(QLatin1String(urlC)).toUrl());
|
||||
acc->setCredentials(CredentialsFactory::create(settings->value(QLatin1String(authTypeC)).toString()));
|
||||
|
||||
// We want to only restore settings for that auth type and the user value
|
||||
acc->_settingsMap.insert(QLatin1String(userC), settings->value(userC));
|
||||
QString authTypePrefix = settings->value(authTypeC).toString() + "_";
|
||||
Q_FOREACH(QString key, settings->childKeys()) {
|
||||
if (!key.startsWith(authTypePrefix))
|
||||
continue;
|
||||
acc->_settingsMap.insert(key, settings->value(key));
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool isEqualExceptProtocol(const QUrl &url1, const QUrl &url2)
|
||||
{
|
||||
return (url1.host() != url2.host() ||
|
||||
url1.port() != url2.port() ||
|
||||
url1.path() != url2.path());
|
||||
}
|
||||
|
||||
bool Account::changed(Account *other, bool ignoreUrlProtocol) const
|
||||
{
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
bool changes = false;
|
||||
if (ignoreUrlProtocol) {
|
||||
changes = isEqualExceptProtocol(_url, other->_url);
|
||||
} else {
|
||||
changes = (_url == other->_url);
|
||||
}
|
||||
|
||||
changes |= _credentials->changed(other->_credentials);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
AbstractCredentials *Account::credentials() const
|
||||
{
|
||||
return _credentials;
|
||||
}
|
||||
|
||||
void Account::setCredentials(AbstractCredentials *cred)
|
||||
{
|
||||
_credentials = cred;
|
||||
// set active credential manager
|
||||
if (_am) {
|
||||
_am->deleteLater();
|
||||
}
|
||||
_am = _credentials->getQNAM();
|
||||
connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
|
||||
SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
|
||||
}
|
||||
|
||||
QUrl Account::davUrl() const
|
||||
{
|
||||
return concatUrlPath(url(), davPath());
|
||||
}
|
||||
|
||||
QList<QNetworkCookie> Account::lastAuthCookies() const
|
||||
{
|
||||
return _am->cookieJar()->cookiesForUrl(_url);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::headRequest(const QString &relPath)
|
||||
{
|
||||
return headRequest(concatUrlPath(url(), relPath));
|
||||
}
|
||||
|
||||
QNetworkReply *Account::headRequest(const QUrl &url)
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
return _am->head(request);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::getRequest(const QString &relPath)
|
||||
{
|
||||
return getRequest(concatUrlPath(url(), relPath));
|
||||
}
|
||||
|
||||
QNetworkReply *Account::getRequest(const QUrl &url)
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
return _am->get(request);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data)
|
||||
{
|
||||
return davRequest(verb, concatUrlPath(davUrl(), relPath), req, data);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
||||
{
|
||||
req.setUrl(url);
|
||||
return _am->sendCustomRequest(req, verb, data);
|
||||
}
|
||||
|
||||
void Account::setCertificateChain(const QList<QSslCertificate> &certs)
|
||||
{
|
||||
_certificateChain = certs;
|
||||
}
|
||||
|
||||
void Account::setApprovedCerts(const QList<QSslCertificate> certs)
|
||||
{
|
||||
_approvedCerts = certs;
|
||||
}
|
||||
|
||||
void Account::addApprovedCerts(const QList<QSslCertificate> certs)
|
||||
{
|
||||
_approvedCerts += certs;
|
||||
}
|
||||
|
||||
void Account::setSslErrorHandler(AbstractSslErrorHandler *handler)
|
||||
{
|
||||
_sslErrorHandler.reset(handler);
|
||||
}
|
||||
|
||||
void Account::setUrl(const QUrl &url)
|
||||
{
|
||||
_url = url;
|
||||
}
|
||||
|
||||
QUrl Account::concatUrlPath(const QUrl &url, const QString &concatPath)
|
||||
{
|
||||
QUrl tmpUrl = url;
|
||||
QString path = tmpUrl.path();
|
||||
// avoid '//'
|
||||
if (path.endsWith('/') && concatPath.startsWith('/')) {
|
||||
path.chop(1);
|
||||
} // avoid missing '/'
|
||||
else if (!path.endsWith('/') && !concatPath.startsWith('/')) {
|
||||
path += QLatin1Char('/');
|
||||
}
|
||||
path += concatPath;
|
||||
tmpUrl.setPath(path);
|
||||
return tmpUrl;
|
||||
}
|
||||
|
||||
QString Account::_configFileName;
|
||||
|
||||
QSettings *Account::settingsWithGroup(const QString& group)
|
||||
{
|
||||
if (_configFileName.isEmpty()) {
|
||||
// cache file name
|
||||
MirallConfigFile cfg;
|
||||
_configFileName = cfg.configFile();
|
||||
}
|
||||
QSettings *settings = new QSettings(_configFileName, QSettings::IniFormat);
|
||||
settings->beginGroup(group);
|
||||
return settings;
|
||||
}
|
||||
|
||||
QVariant Account::credentialSetting(const QString &key) const
|
||||
{
|
||||
if (_credentials) {
|
||||
QString prefix = _credentials->authType();
|
||||
QString value = _settingsMap.value(prefix+"_"+key).toString();
|
||||
if (value.isEmpty()) {
|
||||
value = _settingsMap.value(key).toString();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void Account::setCredentialSetting(const QString &key, const QVariant &value)
|
||||
{
|
||||
if (_credentials) {
|
||||
QString prefix = _credentials->authType();
|
||||
_settingsMap.insert(prefix+"_"+key, value);
|
||||
}
|
||||
}
|
||||
|
||||
int Account::state() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
void Account::setState(int state)
|
||||
{
|
||||
if (_state != state) {
|
||||
_state = state;
|
||||
emit stateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> errors)
|
||||
{
|
||||
qDebug() << "SSL-Warnings happened for url " << reply->url().toString();
|
||||
|
||||
if( _treatSslErrorsAsFailure ) {
|
||||
// User decided once not to trust. Honor this decision.
|
||||
qDebug() << "Certs not trusted by user decision, returning.";
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QSslCertificate> approvedCerts;
|
||||
if (_sslErrorHandler.isNull() ) {
|
||||
qDebug() << Q_FUNC_INFO << "called without valid SSL error handler for account" << url();
|
||||
} else {
|
||||
if (_sslErrorHandler->handleErrors(errors, &approvedCerts, this)) {
|
||||
QSslSocket::addDefaultCaCertificates(approvedCerts);
|
||||
addApprovedCerts(approvedCerts);
|
||||
// all ssl certs are known and accepted. We can ignore the problems right away.
|
||||
qDebug() << "Certs are already known and trusted, Warnings are not valid.";
|
||||
reply->ignoreSslErrors();
|
||||
} else {
|
||||
_treatSslErrorsAsFailure = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Daniel Molkentin <danimo@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 SERVERCONNECTION_H
|
||||
#define SERVERCONNECTION_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QUrl>
|
||||
#include <QNetworkCookie>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslError>
|
||||
|
||||
class QSettings;
|
||||
class QNetworkReply;
|
||||
class QUrl;
|
||||
class QNetworkAccessManager;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class AbstractCredentials;
|
||||
class Account;
|
||||
|
||||
class AccountManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static AccountManager *instance();
|
||||
~AccountManager() {}
|
||||
|
||||
void setAccount(Account *account);
|
||||
Account *account() { return _account; }
|
||||
|
||||
Q_SIGNALS:
|
||||
void accountChanged(Account *newAccount, Account *oldAccount);
|
||||
void accountAboutToChange(Account *newAccount, Account *oldAccount);
|
||||
|
||||
private:
|
||||
AccountManager() : _account(0) {}
|
||||
Account *_account;
|
||||
static AccountManager *_instance;
|
||||
};
|
||||
|
||||
/* Reimplement this to handle SSL errors */
|
||||
class AbstractSslErrorHandler {
|
||||
public:
|
||||
virtual ~AbstractSslErrorHandler() {}
|
||||
virtual bool handleErrors(QList<QSslError>, QList<QSslCertificate>*, Account*) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class represents an account on an ownCloud Server
|
||||
*/
|
||||
class Account : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State { Disconnected = 0, /// no network connection
|
||||
Connected, /// account is online
|
||||
SignedOut /// Disconnected + credential token has been discarded
|
||||
};
|
||||
|
||||
static QString davPath() { return "remote.php/webdav/"; }
|
||||
|
||||
Account(AbstractSslErrorHandler *sslErrorHandler = 0, QObject *parent = 0);
|
||||
~Account();
|
||||
|
||||
/**
|
||||
* Saves the account to a given settings file
|
||||
*/
|
||||
void save();
|
||||
|
||||
/**
|
||||
* Creates an account object from from a given settings file.
|
||||
*/
|
||||
static Account* restore();
|
||||
/**
|
||||
* @brief Creates a minimal account object
|
||||
*
|
||||
* This will set up a ssl error handler
|
||||
*
|
||||
* @return A new Account object
|
||||
*/
|
||||
static Account* create(const QUrl &url);
|
||||
|
||||
/**
|
||||
* @brief Checks the Account instance is different from \param other
|
||||
*
|
||||
* @returns true, if credentials or url have changed, false otherwise
|
||||
*/
|
||||
bool changed(Account *other, bool ignoreUrlProtocol) const;
|
||||
|
||||
/** Holds the accounts credentials */
|
||||
AbstractCredentials* credentials() const;
|
||||
void setCredentials(AbstractCredentials *cred);
|
||||
|
||||
/** Server url of the account */
|
||||
void setUrl(const QUrl &url);
|
||||
QUrl url() const { return _url; }
|
||||
|
||||
/** Returns webdav entry URL, based on url() */
|
||||
QUrl davUrl() const;
|
||||
|
||||
QList<QNetworkCookie> lastAuthCookies() const;
|
||||
|
||||
QNetworkReply* headRequest(const QString &relPath);
|
||||
QNetworkReply* headRequest(const QUrl &url);
|
||||
QNetworkReply* getRequest(const QString &relPath);
|
||||
QNetworkReply* getRequest(const QUrl &url);
|
||||
QNetworkReply* davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data = 0);
|
||||
QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0);
|
||||
|
||||
/** The certificates of the account */
|
||||
QList<QSslCertificate> certificateChain() const { return _certificateChain; }
|
||||
void setCertificateChain(const QList<QSslCertificate> &certs);
|
||||
/** The certificates of the account */
|
||||
QList<QSslCertificate> approvedCerts() const { return _approvedCerts; }
|
||||
void setApprovedCerts(const QList<QSslCertificate> certs);
|
||||
void addApprovedCerts(const QList<QSslCertificate> certs);
|
||||
|
||||
// pluggable handler
|
||||
void setSslErrorHandler(AbstractSslErrorHandler *handler);
|
||||
|
||||
// static helper function
|
||||
static QUrl concatUrlPath(const QUrl &url, const QString &concatPath);
|
||||
static QSettings* settingsWithGroup(const QString &group);
|
||||
|
||||
// to be called by credentials only
|
||||
QVariant credentialSetting(const QString& key) const;
|
||||
void setCredentialSetting(const QString& key, const QVariant &value);
|
||||
|
||||
int state() const;
|
||||
void setState(int state);
|
||||
signals:
|
||||
void stateChanged(int state);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotHandleErrors(QNetworkReply*,QList<QSslError>);
|
||||
|
||||
private:
|
||||
QMap<QString, QVariant> _settingsMap;
|
||||
QUrl _url;
|
||||
QList<QSslCertificate> _approvedCerts;
|
||||
QList<QSslCertificate> _certificateChain;
|
||||
QScopedPointer<AbstractSslErrorHandler> _sslErrorHandler;
|
||||
QNetworkAccessManager *_am;
|
||||
AbstractCredentials* _credentials;
|
||||
bool _treatSslErrorsAsFailure;
|
||||
int _state;
|
||||
static QString _configFileName;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //SERVERCONNECTION_H
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/folderwizard.h"
|
||||
#include "mirall/folderstatusmodel.h"
|
||||
#include "mirall/utility.h"
|
||||
@@ -24,8 +25,7 @@
|
||||
#include "mirall/owncloudsetupwizard.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/ignorelisteditor.h"
|
||||
#include "mirall/account.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
#include <QIcon>
|
||||
#include <QVariant>
|
||||
|
||||
#include "mirall/account.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
static const char progressBarStyleC[] =
|
||||
@@ -55,8 +53,7 @@ static const char progressBarStyleC[] =
|
||||
AccountSettings::AccountSettings(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::AccountSettings),
|
||||
_wasDisabledBefore(false),
|
||||
_account(AccountManager::instance()->account())
|
||||
_wasDisabledBefore(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@@ -70,23 +67,20 @@ AccountSettings::AccountSettings(QWidget *parent) :
|
||||
ui->_folderList->setMinimumWidth( 300 );
|
||||
ui->_folderList->setEditTriggers( QAbstractItemView::NoEditTriggers );
|
||||
|
||||
ui->_buttonRemove->setEnabled(false);
|
||||
ui->_buttonEnable->setEnabled(false);
|
||||
ui->_buttonAdd->setEnabled(true);
|
||||
ui->_ButtonRemove->setEnabled(false);
|
||||
ui->_ButtonEnable->setEnabled(false);
|
||||
ui->_ButtonInfo->setEnabled(false);
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
|
||||
QAction *resetFolderAction = new QAction(this);
|
||||
resetFolderAction->setShortcut(QKeySequence(Qt::Key_F5));
|
||||
connect(resetFolderAction, SIGNAL(triggered()), SLOT(slotResetCurrentFolder()));
|
||||
addAction(resetFolderAction);
|
||||
|
||||
QAction *syncNowAction = new QAction(this);
|
||||
syncNowAction->setShortcut(QKeySequence(Qt::Key_F6));
|
||||
connect(syncNowAction, SIGNAL(triggered()), SLOT(slotSyncCurrentFolderNow()));
|
||||
addAction(syncNowAction);
|
||||
|
||||
connect(ui->_buttonRemove, SIGNAL(clicked()), this, SLOT(slotRemoveCurrentFolder()));
|
||||
connect(ui->_buttonEnable, SIGNAL(clicked()), this, SLOT(slotEnableCurrentFolder()));
|
||||
connect(ui->_buttonAdd, SIGNAL(clicked()), this, SLOT(slotAddFolder()));
|
||||
connect(ui->_ButtonRemove, SIGNAL(clicked()), this, SLOT(slotRemoveCurrentFolder()));
|
||||
connect(ui->_ButtonEnable, SIGNAL(clicked()), this, SLOT(slotEnableCurrentFolder()));
|
||||
connect(ui->_ButtonInfo, SIGNAL(clicked()), this, SLOT(slotInfoAboutCurrentFolder()));
|
||||
connect(ui->_ButtonAdd, SIGNAL(clicked()), this, SLOT(slotAddFolder()));
|
||||
connect(ui->modifyAccountButton, SIGNAL(clicked()), SLOT(slotOpenAccountWizard()));
|
||||
connect(ui->ignoredFilesButton, SIGNAL(clicked()), SLOT(slotIgnoreFilesEditor()));;
|
||||
|
||||
@@ -95,43 +89,32 @@ AccountSettings::AccountSettings(QWidget *parent) :
|
||||
|
||||
QColor color = palette().highlight().color();
|
||||
ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
|
||||
ui->connectLabel->setWordWrap(true);
|
||||
ui->connectLabel->setOpenExternalLinks(true);
|
||||
ui->quotaLabel->setWordWrap(true);
|
||||
ownCloudInfo *ocInfo = ownCloudInfo::instance();
|
||||
slotUpdateQuota(ocInfo->lastQuotaTotalBytes(), ocInfo->lastQuotaUsedBytes());
|
||||
connect(ocInfo, SIGNAL(quotaUpdated(qint64,qint64)), SLOT(slotUpdateQuota(qint64,qint64)));
|
||||
|
||||
ui->connectLabel->setText(tr("No account configured."));
|
||||
ui->_buttonAdd->setEnabled(false);
|
||||
if (_account) {
|
||||
connect(_account, SIGNAL(stateChanged(int)), SLOT(slotAccountStateChanged(int)));
|
||||
slotAccountStateChanged(_account->state());
|
||||
}
|
||||
ui->connectLabel->setWordWrap( true );
|
||||
|
||||
setFolderList(FolderMan::instance()->map());
|
||||
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderActivated( const QModelIndex& indx )
|
||||
{
|
||||
bool state = indx.isValid();
|
||||
|
||||
bool haveFolders = ui->_folderList->model()->rowCount() > 0;
|
||||
|
||||
ui->_buttonRemove->setEnabled(state);
|
||||
if( Theme::instance()->singleSyncFolder() ) {
|
||||
// only one folder synced folder allowed.
|
||||
ui->_buttonAdd->setVisible(!haveFolders);
|
||||
} else {
|
||||
ui->_buttonAdd->setVisible(true);
|
||||
ui->_buttonAdd->setEnabled( state );
|
||||
}
|
||||
ui->_buttonEnable->setEnabled( state );
|
||||
ui->_ButtonRemove->setEnabled( state );
|
||||
ui->_ButtonEnable->setEnabled( state );
|
||||
ui->_ButtonInfo->setEnabled( state );
|
||||
|
||||
if ( state ) {
|
||||
bool folderEnabled = _model->data( indx, FolderStatusDelegate::FolderSyncEnabled).toBool();
|
||||
qDebug() << "folder is sync enabled: " << folderEnabled;
|
||||
if ( folderEnabled ) {
|
||||
ui->_buttonEnable->setText( tr( "Pause" ) );
|
||||
ui->_ButtonEnable->setText( tr( "Pause" ) );
|
||||
} else {
|
||||
ui->_buttonEnable->setText( tr( "Resume" ) );
|
||||
ui->_ButtonEnable->setText( tr( "Resume" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,7 +157,7 @@ void AccountSettings::slotFolderWizardAccepted()
|
||||
folderMan->slotScheduleAllFolders();
|
||||
emit folderChanged();
|
||||
}
|
||||
slotButtonsSetEnabled();
|
||||
buttonsSetEnabled();
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderWizardRejected()
|
||||
@@ -198,19 +181,30 @@ void AccountSettings::slotAddFolder( Folder *folder )
|
||||
QStandardItem *item = new QStandardItem();
|
||||
folderToModelItem( item, folder );
|
||||
_model->appendRow( item );
|
||||
// in order to update the enabled state of the "Sync now" button
|
||||
connect(folder, SIGNAL(syncStateChange()), this, SLOT(slotButtonsSetEnabled()), Qt::UniqueConnection);
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AccountSettings::slotButtonsSetEnabled()
|
||||
void AccountSettings::buttonsSetEnabled()
|
||||
{
|
||||
bool haveFolders = ui->_folderList->model()->rowCount() > 0;
|
||||
|
||||
ui->_ButtonRemove->setEnabled(false);
|
||||
if( Theme::instance()->singleSyncFolder() ) {
|
||||
// only one folder synced folder allowed.
|
||||
ui->_ButtonAdd->setVisible(!haveFolders);
|
||||
} else {
|
||||
ui->_ButtonAdd->setVisible(true);
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
}
|
||||
|
||||
QModelIndex selected = ui->_folderList->currentIndex();
|
||||
bool isSelected = selected.isValid();
|
||||
if (isSelected) {
|
||||
slotFolderActivated(selected);
|
||||
}
|
||||
|
||||
ui->_ButtonEnable->setEnabled(isSelected);
|
||||
ui->_ButtonRemove->setEnabled(isSelected);
|
||||
ui->_ButtonInfo->setEnabled(isSelected);
|
||||
}
|
||||
|
||||
void AccountSettings::setGeneralErrors( const QStringList& errors )
|
||||
@@ -223,9 +217,9 @@ void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
if( ! item || !f ) return;
|
||||
|
||||
item->setData( f->nativePath(), FolderStatusDelegate::FolderPathRole );
|
||||
item->setData( f->remotePath(), FolderStatusDelegate::FolderSecondPathRole );
|
||||
item->setData( f->secondPath(), FolderStatusDelegate::FolderSecondPathRole );
|
||||
item->setData( f->alias(), FolderStatusDelegate::FolderAliasRole );
|
||||
item->setData( f->syncEnabled(), FolderStatusDelegate::FolderSyncEnabled );
|
||||
item->setData( f->userSyncEnabled() && f->syncEnabled(), FolderStatusDelegate::FolderSyncEnabled );
|
||||
|
||||
SyncResult res = f->syncResult();
|
||||
SyncResult::Status status = res.status();
|
||||
@@ -234,7 +228,7 @@ void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
|
||||
Theme *theme = Theme::instance();
|
||||
item->setData( theme->statusHeaderText( status ), Qt::ToolTipRole );
|
||||
if( f->syncEnabled() ) {
|
||||
if( f->syncEnabled() && f->userSyncEnabled() ) {
|
||||
if( status == SyncResult::SyncPrepare ) {
|
||||
if( _wasDisabledBefore ) {
|
||||
// if the folder was disabled before, set the sync icon
|
||||
@@ -242,11 +236,7 @@ void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
} // we keep the previous icon for the SyncPrepare state.
|
||||
} else {
|
||||
// kepp the previous icon for the prepare phase.
|
||||
if( status == SyncResult::Problem) {
|
||||
item->setData( theme->syncStateIcon( SyncResult::Success), FolderStatusDelegate::FolderStatusIconRole );
|
||||
} else {
|
||||
item->setData( theme->syncStateIcon( status ), FolderStatusDelegate::FolderStatusIconRole );
|
||||
}
|
||||
item->setData( theme->syncStateIcon( status ), FolderStatusDelegate::FolderStatusIconRole );
|
||||
}
|
||||
} else {
|
||||
item->setData( theme->folderDisabledIcon( ), FolderStatusDelegate::FolderStatusIconRole ); // size 48 before
|
||||
@@ -257,8 +247,7 @@ void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
if( errorList.isEmpty() ) {
|
||||
if( (status == SyncResult::Error ||
|
||||
status == SyncResult::SetupError ||
|
||||
status == SyncResult::SyncAbortRequested ||
|
||||
status == SyncResult::Unavailable)) {
|
||||
status == SyncResult::Unavailable )) {
|
||||
errorList << theme->statusHeaderText(status);
|
||||
}
|
||||
}
|
||||
@@ -310,6 +299,7 @@ void AccountSettings::slotRemoveCurrentFolder()
|
||||
_model->removeRow(row);
|
||||
|
||||
emit folderChanged();
|
||||
slotCheckConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,7 +318,7 @@ void AccountSettings::slotResetCurrentFolder()
|
||||
if( ret == QMessageBox::Yes ) {
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder *f = folderMan->folder(alias);
|
||||
f->slotTerminateSync(true);
|
||||
f->slotTerminateSync();
|
||||
f->wipe();
|
||||
folderMan->slotScheduleAllFolders();
|
||||
}
|
||||
@@ -359,6 +349,24 @@ void AccountSettings::showConnectionLabel( const QString& message, const QString
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotCheckConnection()
|
||||
{
|
||||
if( ownCloudInfo::instance()->isConfigured() ) {
|
||||
connect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
connect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
|
||||
showConnectionLabel( tr("Checking %1 connection...").arg(Theme::instance()->appNameGUI()));
|
||||
qDebug() << "Check status.php from statusdialog.";
|
||||
ownCloudInfo::instance()->checkInstallation();
|
||||
} else {
|
||||
// ownCloud is not yet configured.
|
||||
showConnectionLabel( tr("No %1 connection configured.").arg(Theme::instance()->appNameGUI()) );
|
||||
ui->_ButtonAdd->setEnabled( false);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::setFolderList( const Folder::Map &folders )
|
||||
{
|
||||
_model->clear();
|
||||
@@ -377,7 +385,7 @@ void AccountSettings::setFolderList( const Folder::Map &folders )
|
||||
if (idx.isValid()) {
|
||||
ui->_folderList->setCurrentIndex(idx);
|
||||
}
|
||||
slotButtonsSetEnabled();
|
||||
buttonsSetEnabled();
|
||||
|
||||
}
|
||||
|
||||
@@ -390,7 +398,7 @@ void AccountSettings::slotFolderOpenAction( const QString& alias )
|
||||
QUrl url(f->path(), QUrl::TolerantMode);
|
||||
url.setScheme( QLatin1String("file") );
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef Q_OS_WIN32
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = f->path();
|
||||
|
||||
@@ -434,11 +442,10 @@ void AccountSettings::slotEnableCurrentFolder()
|
||||
|
||||
// message box can return at any time while the thread keeps running,
|
||||
// so better check again after the user has responded.
|
||||
if ( f->isBusy() && terminate ) {
|
||||
f->slotTerminateSync(false);
|
||||
}
|
||||
if ( f->isBusy() && terminate )
|
||||
folderMan->terminateSyncProcess( alias );
|
||||
|
||||
folderMan->slotEnableFolder( alias, !folderEnabled );
|
||||
folderMan->slotGuiPauseFolder( alias, !folderEnabled );
|
||||
|
||||
// keep state for the icon setting.
|
||||
if( !folderEnabled ) _wasDisabledBefore = true;
|
||||
@@ -450,20 +457,6 @@ void AccountSettings::slotEnableCurrentFolder()
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotSyncCurrentFolderNow()
|
||||
{
|
||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
||||
if( !selected.isValid() )
|
||||
return;
|
||||
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder *f = folderMan->folder( alias );
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
f->evaluateSync(QStringList());
|
||||
}
|
||||
|
||||
void AccountSettings::slotUpdateFolderState( Folder *folder )
|
||||
{
|
||||
QStandardItem *item = 0;
|
||||
@@ -486,45 +479,52 @@ void AccountSettings::slotUpdateFolderState( Folder *folder )
|
||||
} else {
|
||||
// the dialog is not visible.
|
||||
}
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
//void AccountSettings::slotOCInfo( const QString& url, const QString& versionStr, const QString& version, const QString& )
|
||||
//{
|
||||
//#ifdef Q_OS_WIN
|
||||
// // work around a bug in QDesktopServices on Win32, see i-net
|
||||
// QString filePath = url;
|
||||
void AccountSettings::slotOCInfo( const QString& url, const QString& versionStr, const QString& version, const QString& )
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = url;
|
||||
|
||||
// if (filePath.startsWith("\\\\") || filePath.startsWith("//"))
|
||||
// _OCUrl.setUrl(QDir::toNativeSeparators(filePath));
|
||||
// else
|
||||
// _OCUrl = QUrl::fromLocalFile(filePath);
|
||||
//#else
|
||||
// _OCUrl = QUrl::fromLocalFile(url);
|
||||
//#endif
|
||||
if (filePath.startsWith("\\\\") || filePath.startsWith("//"))
|
||||
_OCUrl.setUrl(QDir::toNativeSeparators(filePath));
|
||||
else
|
||||
_OCUrl = QUrl::fromLocalFile(filePath);
|
||||
#else
|
||||
_OCUrl = QUrl::fromLocalFile(url);
|
||||
#endif
|
||||
|
||||
// qDebug() << "#-------# oC found on " << url;
|
||||
// /* enable the open button */
|
||||
// ui->connectLabel->setOpenExternalLinks(true);
|
||||
// QUrl safeUrl(url);
|
||||
// safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||
// showConnectionLabel( tr("Connected to <a href=\"%1\">%2</a>.").arg(url, safeUrl.toString()),
|
||||
// tr("Version: %1 (%2)").arg(versionStr).arg(version) );
|
||||
// ui->_buttonAdd->setEnabled(true);
|
||||
qDebug() << "#-------# oC found on " << url;
|
||||
/* enable the open button */
|
||||
ui->connectLabel->setOpenExternalLinks(true);
|
||||
QUrl safeUrl(url);
|
||||
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||
showConnectionLabel( tr("Connected to <a href=\"%1\">%2</a>.").arg(url, safeUrl.toString()),
|
||||
tr("Version: %1 (%2)").arg(versionStr).arg(version) );
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
|
||||
// disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
// this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
// disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
// this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
//}
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
}
|
||||
|
||||
//void AccountSettings::slotOCInfoFail( QNetworkReply *reply)
|
||||
//{
|
||||
// QString errStr = tr("unknown problem.");
|
||||
// if( reply ) errStr = reply->errorString();
|
||||
void AccountSettings::slotOCInfoFail( QNetworkReply *reply)
|
||||
{
|
||||
QString errStr = tr("unknown problem.");
|
||||
if( reply ) errStr = reply->errorString();
|
||||
|
||||
// showConnectionLabel( tr("<p>Failed to connect to %1: <tt>%2</tt></p>").arg(Theme::instance()->appNameGUI()).arg(errStr) );
|
||||
// ui->_buttonAdd->setEnabled( false);
|
||||
//}
|
||||
showConnectionLabel( tr("<p>Failed to connect to %1: <tt>%2</tt></p>").arg(Theme::instance()->appNameGUI()).arg(errStr) );
|
||||
ui->_ButtonAdd->setEnabled( false);
|
||||
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
|
||||
}
|
||||
|
||||
void AccountSettings::slotOpenOC()
|
||||
{
|
||||
@@ -567,8 +567,9 @@ QString AccountSettings::shortenFilename( const QString& folder, const QString&
|
||||
// rip off the whole ownCloud URL.
|
||||
Folder *f = FolderMan::instance()->folder(folder);
|
||||
if( f ) {
|
||||
QString remotePathUrl = f->remoteUrl().toString();
|
||||
QString remotePathUrl = ownCloudInfo::instance()->webdavUrl() + QLatin1Char('/') + f->secondPath();
|
||||
shortFile.remove(Utility::toCSyncScheme(remotePathUrl));
|
||||
|
||||
}
|
||||
}
|
||||
return shortFile;
|
||||
@@ -602,7 +603,7 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
qDebug() << "================================> INVALID Progress for folder " << folder;
|
||||
return;
|
||||
}
|
||||
if( (progress.kind == Progress::StartSync)
|
||||
if( (progress.kind == Progress::StartSync || progress.kind == Progress::EndSync)
|
||||
&& progress.overall_file_count == 0 ) {
|
||||
// do not show progress if nothing is transmitted.
|
||||
return;
|
||||
@@ -633,7 +634,6 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
case Progress::StartDownload:
|
||||
case Progress::StartUpload:
|
||||
case Progress::StartDelete:
|
||||
case Progress::StartRename:
|
||||
syncFileProgressString = tr("Start");
|
||||
if( _hideProgressTimers.contains(item) ) {
|
||||
// The timer is still running.
|
||||
@@ -648,7 +648,6 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
case Progress::EndDownload:
|
||||
case Progress::EndUpload:
|
||||
case Progress::EndDelete:
|
||||
case Progress::EndRename:
|
||||
break;
|
||||
case Progress::EndSync:
|
||||
syncFileProgressString = tr("Completely");
|
||||
@@ -670,9 +669,7 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
case Progress::Download:
|
||||
case Progress::Upload:
|
||||
case Progress::Inactive:
|
||||
case Progress::SoftError:
|
||||
case Progress::NormalError:
|
||||
case Progress::FatalError:
|
||||
case Progress::Error:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -750,9 +747,7 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
||||
ui->quotaProgressBar->setValue(qVal);
|
||||
QString usedStr = Utility::octetsToString(used);
|
||||
QString totalStr = Utility::octetsToString(total);
|
||||
double percent = used/(double)total*100;
|
||||
QString percentStr = Utility::compactFormatDouble(percent, 1);
|
||||
ui->quotaLabel->setText(tr("%1 of %2 (%3%) in use.").arg(usedStr, totalStr, percentStr));
|
||||
ui->quotaLabel->setText(tr("%1 of %2 in use.").arg(usedStr, totalStr));
|
||||
} else {
|
||||
ui->quotaProgressBar->setVisible(false);
|
||||
ui->quotaInfoLabel->setVisible(false);
|
||||
@@ -771,35 +766,9 @@ void AccountSettings::slotIgnoreFilesEditor()
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotAccountStateChanged(int state)
|
||||
void AccountSettings::slotInfoAboutCurrentFolder()
|
||||
{
|
||||
if (_account) {
|
||||
QUrl safeUrl(_account->url());
|
||||
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||
ui->_buttonAdd->setEnabled(state == Account::Connected);
|
||||
if (state == Account::Connected) {
|
||||
QString user;
|
||||
if (AbstractCredentials *cred = _account->credentials()) {
|
||||
user = cred->user();
|
||||
}
|
||||
if (user.isEmpty()) {
|
||||
showConnectionLabel( tr("Connected to <a href=\"%1\">%2</a>.").arg(_account->url().toString(), safeUrl.toString())
|
||||
/*, tr("Version: %1 (%2)").arg(versionStr).arg(version) */ );
|
||||
} else {
|
||||
showConnectionLabel( tr("Connected to <a href=\"%1\">%2</a> as <i>%3</i>.").arg(_account->url().toString(), safeUrl.toString(), user)
|
||||
/*, tr("Version: %1 (%2)").arg(versionStr).arg(version) */ );
|
||||
}
|
||||
} else {
|
||||
showConnectionLabel( tr("No connection to %1 at <a href=\"%1\">%2</a>.")
|
||||
.arg(Theme::instance()->appNameGUI(),
|
||||
_account->url().toString(),
|
||||
safeUrl.toString()) );
|
||||
}
|
||||
} else {
|
||||
// ownCloud is not yet configured.
|
||||
showConnectionLabel( tr("No %1 connection configured.").arg(Theme::instance()->appNameGUI()) );
|
||||
ui->_buttonAdd->setEnabled( false);
|
||||
}
|
||||
emit(openProgressDialog());
|
||||
}
|
||||
|
||||
AccountSettings::~AccountSettings()
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
|
||||
class QStandardItemModel;
|
||||
class QModelIndex;
|
||||
@@ -37,8 +38,8 @@ class AccountSettings;
|
||||
}
|
||||
|
||||
class FolderMan;
|
||||
class ItemProgressDialog;
|
||||
class IgnoreListEditor;
|
||||
class Account;
|
||||
|
||||
class AccountSettings : public QWidget
|
||||
{
|
||||
@@ -49,10 +50,11 @@ public:
|
||||
~AccountSettings();
|
||||
|
||||
void setFolderList( const Folder::Map& );
|
||||
void buttonsSetEnabled();
|
||||
|
||||
signals:
|
||||
void folderChanged();
|
||||
void openProtocol();
|
||||
void openProgressDialog();
|
||||
void openFolderAlias( const QString& );
|
||||
void infoFolderAlias( const QString& );
|
||||
|
||||
@@ -60,15 +62,16 @@ public slots:
|
||||
void slotFolderActivated( const QModelIndex& );
|
||||
void slotOpenOC();
|
||||
void slotUpdateFolderState( Folder* );
|
||||
void slotCheckConnection();
|
||||
void slotOCInfo( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotOCInfoFail( QNetworkReply* );
|
||||
void slotDoubleClicked( const QModelIndex& );
|
||||
void slotFolderOpenAction( const QString& );
|
||||
void slotSetProgress(const QString&, const Progress::Info& progress);
|
||||
void slotProgressProblem(const QString& folder, const Progress::SyncProblem& problem);
|
||||
void slotButtonsSetEnabled();
|
||||
|
||||
void slotUpdateQuota( qint64,qint64 );
|
||||
void slotIgnoreFilesEditor();
|
||||
void slotAccountStateChanged(int state);
|
||||
|
||||
void setGeneralErrors( const QStringList& errors );
|
||||
|
||||
@@ -76,8 +79,8 @@ protected slots:
|
||||
void slotAddFolder();
|
||||
void slotAddFolder( Folder* );
|
||||
void slotEnableCurrentFolder();
|
||||
void slotSyncCurrentFolderNow();
|
||||
void slotRemoveCurrentFolder();
|
||||
void slotInfoAboutCurrentFolder();
|
||||
void slotResetCurrentFolder();
|
||||
void slotFolderWizardAccepted();
|
||||
void slotFolderWizardRejected();
|
||||
@@ -91,6 +94,7 @@ private:
|
||||
void showConnectionLabel( const QString& message, const QString& tooltip = QString() );
|
||||
|
||||
Ui::AccountSettings *ui;
|
||||
QPointer<ItemProgressDialog> _fileItemDialog;
|
||||
QPointer<IgnoreListEditor> _ignoreEditor;
|
||||
QStandardItemModel *_model;
|
||||
QUrl _OCUrl;
|
||||
@@ -98,7 +102,6 @@ private:
|
||||
QString _kindContext;
|
||||
QStringList _generalErrors;
|
||||
bool _wasDisabledBefore;
|
||||
Account *_account;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="syncStatusGroupBox">
|
||||
<property name="title">
|
||||
<string>Account to Synchronize</string>
|
||||
<string>Sync Status</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
@@ -59,21 +59,21 @@
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttonAdd">
|
||||
<widget class="QPushButton" name="_ButtonAdd">
|
||||
<property name="text">
|
||||
<string>Add Folder...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttonEnable">
|
||||
<widget class="QPushButton" name="_ButtonEnable">
|
||||
<property name="text">
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttonRemove">
|
||||
<widget class="QPushButton" name="_ButtonRemove">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
@@ -92,6 +92,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_ButtonInfo">
|
||||
<property name="text">
|
||||
<string>Info...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -114,9 +121,6 @@
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -19,18 +19,24 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "mirall/application.h"
|
||||
#include "mirall/systray.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/folderwatcher.h"
|
||||
#include "mirall/networklocation.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/owncloudsetupwizard.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/sslerrordialog.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/updatedetector.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/settingsdialog.h"
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/inotify.h"
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/socketapi.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
@@ -38,14 +44,26 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <QHash>
|
||||
#include <QHashIterator>
|
||||
#include <QUrl>
|
||||
#include <QDesktopServices>
|
||||
#include <QTranslator>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
// application logging handler.
|
||||
void mirallLogCatcher(QtMsgType type, const char *msg)
|
||||
{
|
||||
Q_UNUSED(type)
|
||||
// qDebug() exports to local8Bit, which is not always UTF-8
|
||||
Logger::instance()->mirallLog( QString::fromLocal8Bit(msg) );
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static const char optionsC[] =
|
||||
@@ -69,7 +87,7 @@ QString applicationTrPath()
|
||||
#ifdef Q_OS_MAC
|
||||
return QApplication::applicationDirPath()+QLatin1String("/../Resources/Translations"); // path defaults to app dir.
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef Q_OS_WIN32
|
||||
return QApplication::applicationDirPath();
|
||||
#endif
|
||||
}
|
||||
@@ -79,14 +97,18 @@ QString applicationTrPath()
|
||||
|
||||
Application::Application(int &argc, char **argv) :
|
||||
SharedTools::QtSingleApplication(argc, argv),
|
||||
_gui(0),
|
||||
_tray(0),
|
||||
_networkMgr(new QNetworkConfigurationManager(this)),
|
||||
_sslErrorDialog(0),
|
||||
_contextMenu(0),
|
||||
_recentActionsMenu(0),
|
||||
_theme(Theme::instance()),
|
||||
_helpOnly(false),
|
||||
_logBrowser(0),
|
||||
_startupNetworkError(false),
|
||||
_showLogWindow(false),
|
||||
_logExpire(0),
|
||||
_showLogWindow(false),
|
||||
_logFlush(false),
|
||||
_userTriggeredConnect(false)
|
||||
_helpOnly(false)
|
||||
{
|
||||
setApplicationName( _theme->appNameGUI() );
|
||||
setWindowIcon( _theme->applicationIcon() );
|
||||
@@ -95,42 +117,56 @@ Application::Application(int &argc, char **argv) :
|
||||
//no need to waste time;
|
||||
if ( _helpOnly ) return;
|
||||
|
||||
setupLogging();
|
||||
setupLogBrowser();
|
||||
setupTranslations();
|
||||
|
||||
connect( this, SIGNAL(messageReceived(QString)), SLOT(slotParseOptions(QString)));
|
||||
connect( Logger::instance(), SIGNAL(guiLog(QString,QString)),
|
||||
this, SLOT(slotShowTrayMessage(QString,QString)));
|
||||
connect( Logger::instance(), SIGNAL(optionalGuiLog(QString,QString)),
|
||||
this, SLOT(slotShowOptionalTrayMessage(QString,QString)));
|
||||
connect( Logger::instance(), SIGNAL(guiMessage(QString,QString)),
|
||||
this, SLOT(slotShowGuiMessage(QString,QString)));
|
||||
|
||||
Account *account = Account::restore();
|
||||
if (account) {
|
||||
account->setSslErrorHandler(new SslDialogErrorHandler);
|
||||
AccountManager::instance()->setAccount(account);
|
||||
}
|
||||
ProgressDispatcher *pd = ProgressDispatcher::instance();
|
||||
connect( pd, SIGNAL(progressInfo(QString,Progress::Info)), this,
|
||||
SLOT(slotUpdateProgress(QString,Progress::Info)) );
|
||||
connect( pd, SIGNAL(progressSyncProblem(QString,Progress::SyncProblem)),
|
||||
SLOT(slotProgressSyncProblem(QString,Progress::SyncProblem)));
|
||||
|
||||
FolderMan::instance()->setSyncEnabled(false);
|
||||
// create folder manager for sync folder management
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
connect( folderMan, SIGNAL(folderSyncStateChange(QString)),
|
||||
this,SLOT(slotSyncStateChange(QString)));
|
||||
folderMan->setSyncEnabled(false);
|
||||
|
||||
/* use a signal mapper to map the open requests to the alias names */
|
||||
_folderOpenActionMapper = new QSignalMapper(this);
|
||||
connect(_folderOpenActionMapper, SIGNAL(mapped(const QString &)),
|
||||
this, SLOT(slotFolderOpenAction(const QString &)));
|
||||
|
||||
setQuitOnLastWindowClosed(false);
|
||||
|
||||
qRegisterMetaType<Progress::Kind>("Progress::Kind");
|
||||
qRegisterMetaType<Progress::Info>("Progress::Info");
|
||||
qRegisterMetaType<Progress::SyncProblem>("Progress::SyncProblem");
|
||||
#if 0
|
||||
qDebug() << "* Network is" << (_networkMgr->isOnline() ? "online" : "offline");
|
||||
foreach (const QNetworkConfiguration& netCfg, _networkMgr->allConfigurations(QNetworkConfiguration::Active)) {
|
||||
//qDebug() << "Network:" << netCfg.identifier();
|
||||
}
|
||||
#endif
|
||||
|
||||
// connect(_networkMgr, SIGNAL(onlineStateChanged(bool)), SLOT(slotCheckConnection()));
|
||||
|
||||
MirallConfigFile cfg;
|
||||
_theme->setSystrayUseMonoIcons(cfg.monoIcons());
|
||||
connect (_theme, SIGNAL(systrayUseMonoIconsChanged(bool)), SLOT(slotUseMonoIconsChanged(bool)));
|
||||
|
||||
FolderMan::instance()->setupFolders();
|
||||
slotSetupProxy(); // folders have to be defined first.
|
||||
setupActions();
|
||||
setupSystemTray();
|
||||
|
||||
_gui = new ownCloudGui(this);
|
||||
if( _showLogWindow ) {
|
||||
_gui->slotToggleLogBrowser(); // _showLogWindow is set in parseOptions.
|
||||
}
|
||||
connect( _gui, SIGNAL(setupProxy()), SLOT(slotSetupProxy()));
|
||||
if (account) {
|
||||
connect(account, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
|
||||
}
|
||||
connect(AccountManager::instance(), SIGNAL(accountChanged(Account*,Account*)),
|
||||
this, SLOT(slotAccountChanged(Account*,Account*)));
|
||||
folderMan->setupFolders();
|
||||
slotSetupProxy(); // folders have to be defined first.
|
||||
|
||||
// startup procedure.
|
||||
QTimer::singleShot( 0, this, SLOT( slotCheckConnection() ));
|
||||
@@ -139,61 +175,20 @@ Application::Application(int &argc, char **argv) :
|
||||
QTimer::singleShot( 3000, this, SLOT( slotStartUpdateDetector() ));
|
||||
}
|
||||
|
||||
connect( ownCloudInfo::instance(), SIGNAL(sslFailed(QNetworkReply*, QList<QSslError>)),
|
||||
this,SLOT(slotSSLFailed(QNetworkReply*, QList<QSslError>)));
|
||||
|
||||
connect( ownCloudInfo::instance(), SIGNAL(quotaUpdated(qint64,qint64)),
|
||||
SLOT(slotRefreshQuotaDisplay(qint64, qint64)));
|
||||
|
||||
connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup()));
|
||||
|
||||
_socketApi = new SocketApi(this, cfg.configPathWithAppName().append(QLatin1String("socket")));
|
||||
|
||||
qDebug() << "Network Location: " << NetworkLocation::currentLocation().encoded();
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
// qDebug() << "* Mirall shutdown";
|
||||
}
|
||||
|
||||
void Application::slotLogin()
|
||||
{
|
||||
Account *a = AccountManager::instance()->account();
|
||||
if (a) {
|
||||
FolderMan::instance()->setupFolders();
|
||||
_userTriggeredConnect = true;
|
||||
slotCheckConnection();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotLogout()
|
||||
{
|
||||
Account *a = AccountManager::instance()->account();
|
||||
if (a) {
|
||||
// invalidate & forget token/password
|
||||
a->credentials()->invalidateToken(a);
|
||||
// terminate all syncs and unload folders
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
folderMan->setSyncEnabled(false);
|
||||
folderMan->terminateSyncProcess();
|
||||
folderMan->unloadAllFolders();
|
||||
a->setState(Account::SignedOut);
|
||||
// show result
|
||||
_gui->slotComputeOverallSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotAccountChanged(Account *newAccount, Account *oldAccount)
|
||||
{
|
||||
disconnect(oldAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
|
||||
connect(newAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
|
||||
}
|
||||
|
||||
|
||||
void Application::slotCleanup()
|
||||
{
|
||||
// explicitly close windows. This is somewhat of a hack to ensure
|
||||
// that saving the geometries happens ASAP during a OS shutdown
|
||||
Account *account = AccountManager::instance()->account();
|
||||
if (account) {
|
||||
account->save();
|
||||
}
|
||||
_gui->slotShutdown();
|
||||
_gui->deleteLater();
|
||||
qDebug() << "* Mirall shutdown";
|
||||
}
|
||||
|
||||
void Application::slotStartUpdateDetector()
|
||||
@@ -204,35 +199,52 @@ void Application::slotStartUpdateDetector()
|
||||
|
||||
void Application::slotCheckConnection()
|
||||
{
|
||||
Account *account = AccountManager::instance()->account();
|
||||
|
||||
if( account ) {
|
||||
AbstractCredentials* credentials(account->credentials());
|
||||
if( checkConfigExists(false) ) {
|
||||
MirallConfigFile cfg;
|
||||
AbstractCredentials* credentials(cfg.getCredentials());
|
||||
|
||||
if (! credentials->ready()) {
|
||||
connect( credentials, SIGNAL(fetched()),
|
||||
this, SLOT(slotCredentialsFetched()));
|
||||
credentials->fetch(account);
|
||||
credentials->fetch();
|
||||
} else {
|
||||
runValidator();
|
||||
}
|
||||
} else {
|
||||
// let gui open the setup wizard
|
||||
_gui->slotOpenSettingsDialog( true );
|
||||
// the call to checkConfigExists opens the setup wizard
|
||||
// if the config does not exist. Nothing to do here.
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotCredentialsFetched()
|
||||
{
|
||||
Account *account = AccountManager::instance()->account();
|
||||
disconnect(account->credentials(), SIGNAL(fetched()),
|
||||
MirallConfigFile cfg;
|
||||
AbstractCredentials* credentials(cfg.getCredentials());
|
||||
|
||||
disconnect(credentials, SIGNAL(fetched()),
|
||||
this, SLOT(slotCredentialsFetched()));
|
||||
runValidator();
|
||||
}
|
||||
|
||||
void Application::slotCleanup()
|
||||
{
|
||||
// explicitly close windows. This is somewhat of a hack to ensure
|
||||
// that saving the geometries happens ASAP during a OS shutdown
|
||||
|
||||
// those do delete on close
|
||||
if (!_settingsDialog.isNull()) _settingsDialog->close();
|
||||
if (!_progressDialog.isNull()) _progressDialog->close();
|
||||
|
||||
// those need an extra invitation
|
||||
if (!_tray.isNull()) _tray->deleteLater();
|
||||
if (!_logBrowser.isNull()) _logBrowser->deleteLater();
|
||||
}
|
||||
|
||||
void Application::runValidator()
|
||||
{
|
||||
_conValidator = new ConnectionValidator(AccountManager::instance()->account());
|
||||
_startupFail.clear();
|
||||
|
||||
_conValidator = new ConnectionValidator();
|
||||
connect( _conValidator, SIGNAL(connectionResult(ConnectionValidator::Status)),
|
||||
this, SLOT(slotConnectionValidatorResult(ConnectionValidator::Status)) );
|
||||
_conValidator->checkConnection();
|
||||
@@ -241,40 +253,69 @@ void Application::runValidator()
|
||||
void Application::slotConnectionValidatorResult(ConnectionValidator::Status status)
|
||||
{
|
||||
qDebug() << "Connection Validator Result: " << _conValidator->statusString(status);
|
||||
QStringList startupFails;
|
||||
|
||||
if( status == ConnectionValidator::Connected ) {
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
qDebug() << "######## Connection and Credentials are ok!";
|
||||
folderMan->setSyncEnabled(true);
|
||||
|
||||
int cnt = folderMan->map().size();
|
||||
slotShowOptionalTrayMessage(tr("%1 Sync Started").arg(_theme->appNameGUI()),
|
||||
tr("Sync started for %n configured sync folder(s).","", cnt));
|
||||
|
||||
// queue up the sync for all folders.
|
||||
folderMan->slotScheduleAllFolders();
|
||||
if(!_connectionMsgBox.isNull()) {
|
||||
_connectionMsgBox->close();
|
||||
}
|
||||
|
||||
} else {
|
||||
// if we have problems here, it's unlikely that syncing will work.
|
||||
FolderMan::instance()->setSyncEnabled(false);
|
||||
|
||||
startupFails = _conValidator->errors();
|
||||
_startupFail = _conValidator->errors();
|
||||
_startupNetworkError = _conValidator->networkError();
|
||||
if (_userTriggeredConnect) {
|
||||
if(_connectionMsgBox.isNull()) {
|
||||
_connectionMsgBox = new QMessageBox(QMessageBox::Warning, tr("Connection failed"),
|
||||
_conValidator->errors().join(". ").append('.'), QMessageBox::Ok, 0);
|
||||
_connectionMsgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
_connectionMsgBox->open();
|
||||
_userTriggeredConnect = false;
|
||||
}
|
||||
}
|
||||
QTimer::singleShot(30*1000, this, SLOT(slotCheckConnection()));
|
||||
}
|
||||
_gui->startupConnected( (status == ConnectionValidator::Connected), startupFails);
|
||||
computeOverallSyncStatus();
|
||||
setupContextMenu();
|
||||
|
||||
_conValidator->deleteLater();
|
||||
}
|
||||
|
||||
void Application::slotSSLFailed( QNetworkReply *reply, QList<QSslError> errors )
|
||||
{
|
||||
qDebug() << "SSL-Warnings happened for url " << reply->url().toString();
|
||||
|
||||
if( ownCloudInfo::instance()->certsUntrusted() ) {
|
||||
// User decided once to untrust. Honor this decision.
|
||||
qDebug() << "Untrusted by user decision, returning.";
|
||||
return;
|
||||
}
|
||||
|
||||
QString configHandle = ownCloudInfo::instance()->configHandle(reply);
|
||||
|
||||
// make the ssl dialog aware of the custom config. It loads known certs.
|
||||
if( ! _sslErrorDialog ) {
|
||||
_sslErrorDialog = new SslErrorDialog;
|
||||
}
|
||||
_sslErrorDialog->setCustomConfigHandle( configHandle );
|
||||
|
||||
if( _sslErrorDialog->setErrorList( errors ) ) {
|
||||
// all ssl certs are known and accepted. We can ignore the problems right away.
|
||||
qDebug() << "Certs are already known and trusted, Warnings are not valid.";
|
||||
reply->ignoreSslErrors();
|
||||
} else {
|
||||
if( _sslErrorDialog->exec() == QDialog::Accepted ) {
|
||||
if( _sslErrorDialog->trustConnection() ) {
|
||||
reply->ignoreSslErrors();
|
||||
} else {
|
||||
// User does not want to trust.
|
||||
ownCloudInfo::instance()->setCertsUntrusted(true);
|
||||
}
|
||||
} else {
|
||||
ownCloudInfo::instance()->setCertsUntrusted(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotownCloudWizardDone( int res )
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
@@ -293,15 +334,126 @@ void Application::slotownCloudWizardDone( int res )
|
||||
|
||||
}
|
||||
|
||||
void Application::setupLogging()
|
||||
void Application::setupActions()
|
||||
{
|
||||
_actionOpenoC = new QAction(tr("Open %1 in browser").arg(_theme->appNameGUI()), this);
|
||||
QObject::connect(_actionOpenoC, SIGNAL(triggered(bool)), SLOT(slotOpenOwnCloud()));
|
||||
_actionQuota = new QAction(tr("Calculating quota..."), this);
|
||||
_actionQuota->setEnabled( false );
|
||||
_actionStatus = new QAction(tr("Unknown status"), this);
|
||||
_actionStatus->setEnabled( false );
|
||||
_actionSettings = new QAction(tr("Settings..."), this);
|
||||
_actionRecent = new QAction(tr("Details..."), this);
|
||||
_actionRecent->setEnabled( true );
|
||||
|
||||
QObject::connect(_actionRecent, SIGNAL(triggered(bool)), SLOT(slotItemProgressDialog()));
|
||||
QObject::connect(_actionSettings, SIGNAL(triggered(bool)), SLOT(slotSettings()));
|
||||
_actionHelp = new QAction(tr("Help"), this);
|
||||
QObject::connect(_actionHelp, SIGNAL(triggered(bool)), SLOT(slotHelp()));
|
||||
_actionQuit = new QAction(tr("Quit %1").arg(_theme->appNameGUI()), this);
|
||||
QObject::connect(_actionQuit, SIGNAL(triggered(bool)), SLOT(quit()));
|
||||
}
|
||||
|
||||
void Application::setupSystemTray()
|
||||
{
|
||||
// Setting a parent heres will crash on X11 since by the time qapp runs
|
||||
// its childrens dtors, the X11->screen variable queried for is gone -> crash
|
||||
_tray = new Systray();
|
||||
_tray->setIcon( _theme->syncStateIcon( SyncResult::NotYetStarted, true ) );
|
||||
|
||||
connect(_tray.data(), SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
SLOT(slotTrayClicked(QSystemTrayIcon::ActivationReason)));
|
||||
|
||||
setupContextMenu();
|
||||
|
||||
_tray->show();
|
||||
}
|
||||
|
||||
void Application::setupContextMenu()
|
||||
{
|
||||
bool isConfigured = ownCloudInfo::instance()->isConfigured();
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
_actionOpenoC->setEnabled(isConfigured);
|
||||
|
||||
if( _contextMenu ) {
|
||||
_contextMenu->clear();
|
||||
_recentActionsMenu->clear();
|
||||
_recentActionsMenu->addAction(tr("None."));
|
||||
_recentActionsMenu->addAction(_actionRecent);
|
||||
} else {
|
||||
_contextMenu = new QMenu();
|
||||
_recentActionsMenu = _contextMenu->addMenu(tr("Recent Changes"));
|
||||
// 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).
|
||||
_tray->setContextMenu(_contextMenu);
|
||||
}
|
||||
_contextMenu->setTitle(_theme->appNameGUI() );
|
||||
_contextMenu->addAction(_actionOpenoC);
|
||||
|
||||
int folderCnt = folderMan->map().size();
|
||||
// add open actions for all sync folders to the tray menu
|
||||
if( _theme->singleSyncFolder() ) {
|
||||
// there should be exactly one folder. No sync-folder add action will be shown.
|
||||
QStringList li = folderMan->map().keys();
|
||||
if( li.size() == 1 ) {
|
||||
Folder *folder = folderMan->map().value(li.first());
|
||||
if( folder ) {
|
||||
// if there is singleFolder mode, a generic open action is displayed.
|
||||
QAction *action = new QAction( tr("Open %1 folder").arg(_theme->appNameGUI()), this);
|
||||
connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
|
||||
_folderOpenActionMapper->setMapping( action, folder->alias() );
|
||||
|
||||
_contextMenu->addAction(action);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// show a grouping with more than one folder.
|
||||
if ( folderCnt > 1) {
|
||||
_contextMenu->addAction(tr("Managed Folders:"))->setDisabled(true);
|
||||
}
|
||||
foreach (Folder *folder, folderMan->map() ) {
|
||||
QAction *action = new QAction( tr("Open folder '%1'").arg(folder->alias()), this );
|
||||
connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
|
||||
_folderOpenActionMapper->setMapping( action, folder->alias() );
|
||||
|
||||
_contextMenu->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionQuota);
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionStatus);
|
||||
_contextMenu->addMenu(_recentActionsMenu);
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionSettings);
|
||||
if (!_theme->helpUrl().isEmpty()) {
|
||||
_contextMenu->addAction(_actionHelp);
|
||||
}
|
||||
_contextMenu->addSeparator();
|
||||
|
||||
_contextMenu->addAction(_actionQuit);
|
||||
}
|
||||
|
||||
void Application::setupLogBrowser()
|
||||
{
|
||||
// might be called from second instance
|
||||
Logger::instance()->setLogFile(_logFile);
|
||||
Logger::instance()->setLogDir(_logDir);
|
||||
Logger::instance()->setLogExpire(_logExpire);
|
||||
Logger::instance()->setLogFlush(_logFlush);
|
||||
if (_logBrowser.isNull()) {
|
||||
// init the log browser.
|
||||
qInstallMsgHandler( mirallLogCatcher );
|
||||
_logBrowser = new LogBrowser;
|
||||
// ## TODO: allow new log name maybe?
|
||||
if (!_logDirectory.isEmpty()) {
|
||||
enterNextLogFile();
|
||||
} else if (!_logFile.isEmpty()) {
|
||||
qDebug() << "Logging into logfile: " << _logFile << " with flush " << _logFlush;
|
||||
_logBrowser->setLogFile( _logFile, _logFlush );
|
||||
}
|
||||
}
|
||||
|
||||
Logger::instance()->enterNextLogFile();
|
||||
if (_showLogWindow)
|
||||
slotOpenLogBrowser();
|
||||
|
||||
qDebug() << QString::fromLatin1( "################## %1 %2 (%3) %4").arg(_theme->appName())
|
||||
.arg( QLocale::system().name() )
|
||||
@@ -310,6 +462,37 @@ void Application::setupLogging()
|
||||
|
||||
}
|
||||
|
||||
void Application::enterNextLogFile()
|
||||
{
|
||||
if (_logBrowser && !_logDirectory.isEmpty()) {
|
||||
QDir dir(_logDirectory);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
|
||||
// Find out what is the file with the highest nymber if any
|
||||
QStringList files = dir.entryList(QStringList("owncloud.log.*"),
|
||||
QDir::Files);
|
||||
QRegExp rx("owncloud.log.(\\d+)");
|
||||
uint maxNumber = 0;
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
foreach(const QString &s, files) {
|
||||
if (rx.exactMatch(s)) {
|
||||
maxNumber = qMax(maxNumber, rx.cap(1).toUInt());
|
||||
if (_logExpire > 0) {
|
||||
QFileInfo fileInfo = dir.absoluteFilePath(s);
|
||||
if (fileInfo.lastModified().addSecs(60*60 * _logExpire) < now) {
|
||||
dir.remove(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString filename = _logDirectory + "/owncloud.log." + QString::number(maxNumber+1);
|
||||
_logBrowser->setLogFile(filename , _logFlush);
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkProxy proxyFromConfig(const MirallConfigFile& cfg)
|
||||
{
|
||||
QNetworkProxy proxy;
|
||||
@@ -358,16 +541,240 @@ void Application::slotSetupProxy()
|
||||
FolderMan::instance()->slotScheduleAllFolders();
|
||||
}
|
||||
|
||||
void Application::slotRefreshQuotaDisplay( qint64 total, qint64 used )
|
||||
{
|
||||
if (total == 0) {
|
||||
_actionQuota->setText(tr("Quota n/a"));
|
||||
return;
|
||||
}
|
||||
|
||||
double percent = used/(double)total*100;
|
||||
QString percentFormatted = Utility::compactFormatDouble(percent, 1);
|
||||
QString totalFormatted = Utility::octetsToString(total);
|
||||
_actionQuota->setText(tr("%1% of %2 in use").arg(percentFormatted).arg(totalFormatted));
|
||||
}
|
||||
|
||||
void Application::slotUseMonoIconsChanged(bool)
|
||||
{
|
||||
_gui->slotComputeOverallSyncStatus();
|
||||
computeOverallSyncStatus();
|
||||
}
|
||||
|
||||
void Application::slotProgressSyncProblem(const QString& folder, const Progress::SyncProblem& problem)
|
||||
{
|
||||
Q_UNUSED(folder);
|
||||
Q_UNUSED(problem);
|
||||
|
||||
// display a warn icon if warnings happend.
|
||||
QIcon warnIcon(":/mirall/resources/warning-16");
|
||||
_actionRecent->setIcon(warnIcon);
|
||||
|
||||
rebuildRecentMenus();
|
||||
}
|
||||
|
||||
void Application::rebuildRecentMenus()
|
||||
{
|
||||
_recentActionsMenu->clear();
|
||||
const QList<Progress::Info>& progressInfoList = ProgressDispatcher::instance()->recentChangedItems(5);
|
||||
|
||||
if( progressInfoList.size() == 0 ) {
|
||||
_recentActionsMenu->addAction(tr("No items synced recently"));
|
||||
} else {
|
||||
QListIterator<Progress::Info> i(progressInfoList);
|
||||
|
||||
while(i.hasNext()) {
|
||||
Progress::Info info = i.next();
|
||||
QString kindStr = Progress::asResultString(info.kind);
|
||||
QString timeStr = info.timestamp.toString("hh:mm");
|
||||
|
||||
QString actionText = tr("%1 (%2, %3)").arg(info.current_file).arg(kindStr).arg(timeStr);
|
||||
_recentActionsMenu->addAction( actionText );
|
||||
}
|
||||
}
|
||||
// add a more... entry.
|
||||
_recentActionsMenu->addAction(_actionRecent);
|
||||
}
|
||||
|
||||
void Application::slotUpdateProgress(const QString &folder, const Progress::Info& progress)
|
||||
{
|
||||
Q_UNUSED(folder);
|
||||
|
||||
// shows an entry in the context menu.
|
||||
QString curAmount = Utility::octetsToString(progress.overall_current_bytes);
|
||||
QString totalAmount = Utility::octetsToString(progress.overall_transmission_size);
|
||||
_actionStatus->setText(tr("Syncing %1 of %2 (%3 of %4) ").arg(progress.current_file_no)
|
||||
.arg(progress.overall_file_count).arg(curAmount, totalAmount));
|
||||
|
||||
// wipe the problem list at start of sync.
|
||||
if( progress.kind == Progress::StartSync ) {
|
||||
_actionRecent->setIcon( QIcon() ); // Fixme: Set a "in-progress"-item eventually.
|
||||
}
|
||||
|
||||
// If there was a change in the file list, redo the progress menu.
|
||||
if( progress.kind == Progress::EndDownload || progress.kind == Progress::EndUpload ||
|
||||
progress.kind == Progress::EndDelete ) {
|
||||
rebuildRecentMenus();
|
||||
}
|
||||
|
||||
if (progress.kind == Progress::EndSync) {
|
||||
rebuildRecentMenus(); // show errors.
|
||||
QTimer::singleShot(2000, this, SLOT(slotDisplayIdle()));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotDisplayIdle()
|
||||
{
|
||||
_actionStatus->setText(tr("Up to date"));
|
||||
}
|
||||
|
||||
void Application::slotHelp()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(_theme->helpUrl()));
|
||||
}
|
||||
|
||||
/*
|
||||
* open the folder with the given Alais
|
||||
*/
|
||||
void Application::slotFolderOpenAction( const QString& alias )
|
||||
{
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
qDebug() << "opening local url " << f->path();
|
||||
if( f ) {
|
||||
QUrl url(f->path(), QUrl::TolerantMode);
|
||||
url.setScheme( QLatin1String("file") );
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = f->path();
|
||||
|
||||
if (filePath.startsWith(QLatin1String("\\\\")) || filePath.startsWith(QLatin1String("//")))
|
||||
url.setUrl(QDir::toNativeSeparators(filePath));
|
||||
else
|
||||
url = QUrl::fromLocalFile(filePath);
|
||||
#endif
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotOpenOwnCloud()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
|
||||
QString url = cfgFile.ownCloudUrl();
|
||||
QDesktopServices::openUrl( url );
|
||||
}
|
||||
|
||||
void Application::slotTrayClicked( QSystemTrayIcon::ActivationReason reason )
|
||||
{
|
||||
// A click on the tray icon should only open the status window on Win and
|
||||
// Linux, not on Mac. They want a menu entry.
|
||||
#if defined Q_WS_WIN || defined Q_WS_X11
|
||||
if( reason == QSystemTrayIcon::Trigger ) {
|
||||
checkConfigExists(true); // start settings if config is existing.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Application::checkConfigExists(bool openSettings)
|
||||
{
|
||||
// if no config file is there, start the configuration wizard.
|
||||
MirallConfigFile cfgFile;
|
||||
|
||||
if( cfgFile.exists() && !cfgFile.ownCloudUrl().isEmpty() ) {
|
||||
if( openSettings ) {
|
||||
slotSettings();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "No configured folders yet, starting setup wizard";
|
||||
OwncloudSetupWizard::runWizard(this, SLOT(slotownCloudWizardDone(int)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotOpenLogBrowser()
|
||||
{
|
||||
_logBrowser->show();
|
||||
_logBrowser->raise();
|
||||
}
|
||||
|
||||
// slot hit when a folder gets changed in the settings dialog.
|
||||
void Application::slotFoldersChanged()
|
||||
{
|
||||
computeOverallSyncStatus();
|
||||
setupContextMenu();
|
||||
}
|
||||
|
||||
void Application::slotSettings()
|
||||
{
|
||||
if (_settingsDialog.isNull()) {
|
||||
_settingsDialog = new SettingsDialog(this);
|
||||
_settingsDialog->setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
_settingsDialog->show();
|
||||
}
|
||||
|
||||
_settingsDialog->setGeneralErrors( _startupFail );
|
||||
Utility::raiseDialog(_settingsDialog.data());
|
||||
}
|
||||
|
||||
void Application::slotItemProgressDialog()
|
||||
{
|
||||
if (_progressDialog.isNull()) {
|
||||
_progressDialog = new ItemProgressDialog(this);
|
||||
_progressDialog->setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
_progressDialog->setupList();
|
||||
_progressDialog->show();
|
||||
}
|
||||
Utility::raiseDialog(_progressDialog.data());
|
||||
}
|
||||
|
||||
void Application::slotParseOptions(const QString &opts)
|
||||
{
|
||||
QStringList options = opts.split(QLatin1Char('|'));
|
||||
parseOptions(options);
|
||||
setupLogging();
|
||||
setupLogBrowser();
|
||||
}
|
||||
|
||||
void Application::slotShowTrayMessage(const QString &title, const QString &msg)
|
||||
{
|
||||
if( _tray )
|
||||
_tray->showMessage(title, msg);
|
||||
else
|
||||
qDebug() << "Tray not ready: " << msg;
|
||||
}
|
||||
|
||||
void Application::slotShowOptionalTrayMessage(const QString &title, const QString &msg)
|
||||
{
|
||||
MirallConfigFile cfg;
|
||||
if (cfg.optionalDesktopNotifications())
|
||||
slotShowTrayMessage(title, msg);
|
||||
}
|
||||
|
||||
void Application::slotShowGuiMessage(const QString &title, const QString &message)
|
||||
{
|
||||
QMessageBox *msgBox = new QMessageBox;
|
||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
msgBox->setText(message);
|
||||
msgBox->setWindowTitle(title);
|
||||
msgBox->setIcon(QMessageBox::Information);
|
||||
msgBox->open();
|
||||
}
|
||||
|
||||
void Application::slotSyncStateChange( const QString& alias )
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
const SyncResult& result = folderMan->syncResult( alias );
|
||||
emit folderStateChanged( folderMan->folder(alias) );
|
||||
|
||||
computeOverallSyncStatus();
|
||||
|
||||
qDebug() << "Sync state changed for folder " << alias << ": " << result.statusString();
|
||||
|
||||
if (result.status() == SyncResult::Success || result.status() == SyncResult::Error) {
|
||||
enterNextLogFile();
|
||||
}
|
||||
if( _progressDialog ) {
|
||||
_progressDialog->setSyncResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::parseOptions(const QStringList &options)
|
||||
@@ -383,17 +790,17 @@ void Application::parseOptions(const QStringList &options)
|
||||
setHelp();
|
||||
break;
|
||||
} else if (option == QLatin1String("--logwindow") ||
|
||||
option == QLatin1String("-l")) {
|
||||
option == QLatin1String("-l")) {
|
||||
_showLogWindow = true;
|
||||
} else if (option == QLatin1String("--logfile")) {
|
||||
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
|
||||
_logFile = it.next();
|
||||
_logFile = it.next();
|
||||
} else {
|
||||
setHelp();
|
||||
}
|
||||
} else if (option == QLatin1String("--logdir")) {
|
||||
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
|
||||
_logDir = it.next();
|
||||
_logDirectory = it.next();
|
||||
} else {
|
||||
setHelp();
|
||||
}
|
||||
@@ -416,6 +823,48 @@ void Application::parseOptions(const QStringList &options)
|
||||
setHelp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::computeOverallSyncStatus()
|
||||
{
|
||||
|
||||
// display the info of the least successful sync (eg. not just display the result of the latest sync
|
||||
QString trayMessage;
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder::Map map = folderMan->map();
|
||||
SyncResult overallResult = FolderMan::accountStatus(map.values());
|
||||
|
||||
// if there have been startup problems, show an error message.
|
||||
if( !_startupFail.isEmpty() ) {
|
||||
trayMessage = _startupFail.join(QLatin1String("\n"));
|
||||
QIcon statusIcon;
|
||||
if (_startupNetworkError) {
|
||||
statusIcon = _theme->syncStateIcon( SyncResult::NotYetStarted, true );
|
||||
} else {
|
||||
statusIcon = _theme->syncStateIcon( SyncResult::Error, true );
|
||||
}
|
||||
_tray->setIcon( statusIcon );
|
||||
_tray->setToolTip(trayMessage);
|
||||
} else {
|
||||
// create the tray blob message, check if we have an defined state
|
||||
if( overallResult.status() != SyncResult::Undefined ) {
|
||||
QStringList allStatusStrings;
|
||||
foreach(Folder* folder, map.values()) {
|
||||
qDebug() << "Folder in overallStatus Message: " << folder << " with name " << folder->alias();
|
||||
QString folderMessage = folderMan->statusToString(folder->syncResult().status(), folder->syncEnabled());
|
||||
allStatusStrings += tr("Folder %1: %2").arg(folder->alias(), folderMessage);
|
||||
}
|
||||
|
||||
if( ! allStatusStrings.isEmpty() )
|
||||
trayMessage = allStatusStrings.join(QLatin1String("\n"));
|
||||
else
|
||||
trayMessage = tr("No sync folders configured.");
|
||||
|
||||
QIcon statusIcon = _theme->syncStateIcon( overallResult.status(), true);
|
||||
_tray->setIcon( statusIcon );
|
||||
_tray->setToolTip(trayMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,7 +907,7 @@ void Application::showHelp()
|
||||
<< QLatin1String(optionsC);
|
||||
|
||||
if (_theme->appName() == QLatin1String("ownCloud"))
|
||||
stream << endl << "For more information, see http://www.owncloud.org" << endl << endl;
|
||||
stream << endl << "For more information, see http://www.owncloud.org" << endl;
|
||||
|
||||
displayHelpText(helpText);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#define APPLICATION_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslError>
|
||||
#include <QPointer>
|
||||
#include <QQueue>
|
||||
|
||||
@@ -23,18 +25,25 @@
|
||||
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logbrowser.h"
|
||||
#include "mirall/owncloudgui.h"
|
||||
#include "mirall/systray.h"
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
|
||||
class QMessageBox;
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class QSystemTrayIcon;
|
||||
class QNetworkConfigurationManager;
|
||||
class QSignalMapper;
|
||||
class QNetworkReply;
|
||||
|
||||
namespace Mirall {
|
||||
class Theme;
|
||||
class Folder;
|
||||
class FolderWatcher;
|
||||
class ownCloudInfo;
|
||||
class SslErrorDialog;
|
||||
class SocketApi;
|
||||
class SettingsDialog;
|
||||
class ItemProgressDialog;
|
||||
|
||||
class Application : public SharedTools::QtSingleApplication
|
||||
{
|
||||
@@ -53,10 +62,16 @@ public slots:
|
||||
protected:
|
||||
void parseOptions(const QStringList& );
|
||||
void setupTranslations();
|
||||
void setupLogging();
|
||||
void setupActions();
|
||||
void setupSystemTray();
|
||||
void setupContextMenu();
|
||||
void setupLogBrowser();
|
||||
void enterNextLogFile();
|
||||
bool checkConfigExists(bool openSettings);
|
||||
|
||||
//folders have to be disabled while making config changes
|
||||
void computeOverallSyncStatus();
|
||||
|
||||
// reimplemented
|
||||
#if defined(Q_WS_WIN)
|
||||
bool winEventFilter( MSG * message, long * result );
|
||||
@@ -67,42 +82,70 @@ signals:
|
||||
void folderStateChanged(Folder*);
|
||||
|
||||
protected slots:
|
||||
void slotFoldersChanged();
|
||||
void slotSettings();
|
||||
void slotItemProgressDialog();
|
||||
void slotParseOptions( const QString& );
|
||||
void slotShowTrayMessage(const QString&, const QString&);
|
||||
void slotShowOptionalTrayMessage(const QString&, const QString&);
|
||||
void slotShowGuiMessage(const QString& title, const QString& message);
|
||||
void slotCheckConnection();
|
||||
void slotConnectionValidatorResult(ConnectionValidator::Status);
|
||||
void slotSyncStateChange( const QString& );
|
||||
void slotTrayClicked( QSystemTrayIcon::ActivationReason );
|
||||
void slotFolderOpenAction(const QString & );
|
||||
void slotOpenOwnCloud();
|
||||
void slotOpenLogBrowser();
|
||||
void slotSSLFailed( QNetworkReply *reply, QList<QSslError> errors );
|
||||
void slotStartUpdateDetector();
|
||||
void slotSetupProxy();
|
||||
void slotRefreshQuotaDisplay( qint64 total, qint64 used );
|
||||
void slotUseMonoIconsChanged( bool );
|
||||
void slotUpdateProgress(const QString&, const Progress::Info&);
|
||||
void slotProgressSyncProblem(const QString& folder, const Progress::SyncProblem &problem);
|
||||
void slotDisplayIdle();
|
||||
void slotHelp();
|
||||
void slotCredentialsFetched();
|
||||
void slotLogin();
|
||||
void slotLogout();
|
||||
void slotCleanup();
|
||||
void slotAccountChanged(Account *newAccount, Account *oldAccount);
|
||||
|
||||
private:
|
||||
void setHelp();
|
||||
void raiseDialog( QWidget* );
|
||||
void rebuildRecentMenus();
|
||||
void runValidator();
|
||||
|
||||
QPointer<ownCloudGui> _gui;
|
||||
QPointer<SocketApi> _socketApi;
|
||||
QPointer<Systray> _tray;
|
||||
QAction *_actionOpenoC;
|
||||
QAction *_actionSettings;
|
||||
QAction *_actionQuota;
|
||||
QAction *_actionStatus;
|
||||
QAction *_actionRecent;
|
||||
QAction *_actionHelp;
|
||||
QAction *_actionQuit;
|
||||
|
||||
QNetworkConfigurationManager *_networkMgr;
|
||||
|
||||
SslErrorDialog *_sslErrorDialog;
|
||||
ConnectionValidator *_conValidator;
|
||||
|
||||
Theme *_theme;
|
||||
// tray's menu
|
||||
QMenu *_contextMenu;
|
||||
QMenu *_recentActionsMenu;
|
||||
|
||||
bool _helpOnly;
|
||||
Theme *_theme;
|
||||
QSignalMapper *_folderOpenActionMapper;
|
||||
QPointer<LogBrowser>_logBrowser;
|
||||
QPointer<SettingsDialog> _settingsDialog;
|
||||
QPointer<ItemProgressDialog> _progressDialog;
|
||||
|
||||
QString _logFile;
|
||||
QString _logDirectory;
|
||||
QStringList _startupFail;
|
||||
bool _startupNetworkError;
|
||||
|
||||
// options from command line:
|
||||
int _logExpire;
|
||||
bool _showLogWindow;
|
||||
QString _logFile;
|
||||
QString _logDir;
|
||||
int _logExpire;
|
||||
bool _logFlush;
|
||||
bool _userTriggeredConnect;
|
||||
QPointer<QMessageBox> _connectionMsgBox;
|
||||
|
||||
friend class ownCloudGui; // for _startupNetworkError
|
||||
bool _logFlush;
|
||||
bool _helpOnly;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -12,21 +12,26 @@
|
||||
*/
|
||||
|
||||
#include <QtCore>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
ConnectionValidator::ConnectionValidator(Account *account, QObject *parent)
|
||||
ConnectionValidator::ConnectionValidator(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ConnectionValidator::ConnectionValidator(const QString& connection, QObject *parent)
|
||||
: QObject(parent),
|
||||
_account(account),
|
||||
_connection(connection),
|
||||
_networkError(QNetworkReply::NoError)
|
||||
{
|
||||
ownCloudInfo::instance()->setCustomConfigHandle(_connection);
|
||||
}
|
||||
|
||||
QStringList ConnectionValidator::errors() const
|
||||
@@ -80,28 +85,37 @@ QString ConnectionValidator::statusString( Status stat ) const
|
||||
|
||||
void ConnectionValidator::checkConnection()
|
||||
{
|
||||
if( _account ) {
|
||||
CheckServerJob *checkJob = new CheckServerJob(_account, false, this);
|
||||
checkJob->setIgnoreCredentialFailure(true);
|
||||
connect(checkJob, SIGNAL(instanceFound(QUrl,QVariantMap)), SLOT(slotStatusFound(QUrl,QVariantMap)));
|
||||
connect(checkJob, SIGNAL(networkError(QNetworkReply*)), SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
checkJob->start();
|
||||
if( ownCloudInfo::instance()->isConfigured() ) {
|
||||
connect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
SLOT(slotStatusFound(QString,QString,QString,QString)));
|
||||
|
||||
connect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
|
||||
// checks for status.php
|
||||
ownCloudInfo::instance()->checkInstallation();
|
||||
} else {
|
||||
_errors << tr("No ownCloud account configured");
|
||||
_errors << tr("No ownCloud connection configured");
|
||||
emit connectionResult( NotConfigured );
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &info)
|
||||
void ConnectionValidator::slotStatusFound( const QString& url, const QString& versionStr, const QString& version, const QString& /*edition*/)
|
||||
{
|
||||
// status.php was found.
|
||||
qDebug() << "** Application: ownCloud found: "
|
||||
<< url << " with version "
|
||||
<< CheckServerJob::versionString(info)
|
||||
<< "(" << CheckServerJob::version(info) << ")";
|
||||
qDebug() << "** Application: ownCloud found: " << url << " with version " << versionStr << "(" << version << ")";
|
||||
// now check the authentication
|
||||
MirallConfigFile cfgFile(_connection);
|
||||
|
||||
if( CheckServerJob::version(info).startsWith("4.0") ) {
|
||||
cfgFile.setOwnCloudVersion( version );
|
||||
// disconnect from ownCloudInfo
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
this, SLOT(slotStatusFound(QString,QString,QString,QString)));
|
||||
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
|
||||
if( version.startsWith("4.0") ) {
|
||||
_errors.append( tr("The configured server for this client is too old") );
|
||||
_errors.append( tr("Please update to the latest server and restart the client.") );
|
||||
emit connectionResult( ServerVersionMismatch );
|
||||
@@ -114,10 +128,13 @@ void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &inf
|
||||
// status.php could not be loaded.
|
||||
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
|
||||
{
|
||||
_account->setState(Account::Disconnected);
|
||||
// disconnect from ownCloudInfo
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
this, SLOT(slotStatusFound(QString,QString,QString,QString)));
|
||||
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
|
||||
// ### TODO
|
||||
_errors.append(tr("Unable to connect to %1").arg(_account->url().toString()));
|
||||
_errors.append( reply->errorString() );
|
||||
_networkError = (reply->error() != QNetworkReply::NoError);
|
||||
emit connectionResult( StatusNotFound );
|
||||
@@ -126,46 +143,34 @@ void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
|
||||
|
||||
void ConnectionValidator::slotCheckAuthentication()
|
||||
{
|
||||
connect( ownCloudInfo::instance(), SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this, SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
|
||||
qDebug() << "# checking for authentication settings.";
|
||||
ownCloudInfo::instance()->getWebDAVPath(QLatin1String("/") ); // this call needs to be authenticated.
|
||||
// simply GET the webdav root, will fail if credentials are wrong.
|
||||
// continue in slotAuthCheck here :-)
|
||||
PropfindJob *job = new PropfindJob(_account, "/", this);
|
||||
job->setIgnoreCredentialFailure(true);
|
||||
job->setProperties(QList<QByteArray>() << "getlastmodified");
|
||||
connect(job, SIGNAL(result(QVariantMap)), SLOT(slotAuthSuccess()));
|
||||
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotAuthFailed(QNetworkReply*)));
|
||||
job->start();
|
||||
qDebug() << "# checking for authentication settings.";
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotAuthFailed(QNetworkReply *reply)
|
||||
void ConnectionValidator::slotAuthCheck( const QString&, QNetworkReply *reply )
|
||||
{
|
||||
Status stat = StatusNotFound;
|
||||
Status stat = Connected;
|
||||
|
||||
if( reply->error() == QNetworkReply::AuthenticationRequiredError ||
|
||||
reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user/pwd is wrong.
|
||||
qDebug() << reply->error() << reply->errorString();
|
||||
reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user is wrong.
|
||||
qDebug() << "******** Password is wrong!";
|
||||
_errors << tr("The provided credentials are not correct");
|
||||
stat = CredentialsWrong;
|
||||
switch (_account->state()) {
|
||||
case Account::SignedOut:
|
||||
_account->setState(Account::SignedOut);
|
||||
break;
|
||||
default:
|
||||
_account->setState(Account::Disconnected);
|
||||
}
|
||||
|
||||
} else if( reply->error() != QNetworkReply::NoError ) {
|
||||
_errors << reply->errorString();
|
||||
}
|
||||
|
||||
// disconnect from ownCloud Info signals
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
|
||||
emit connectionResult( stat );
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotAuthSuccess()
|
||||
{
|
||||
_account->setState(Account::Connected);
|
||||
emit connectionResult(Connected);
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
}
|
||||
|
||||
@@ -16,18 +16,17 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QVariantMap>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class Account;
|
||||
|
||||
class ConnectionValidator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ConnectionValidator(Account *account, QObject *parent = 0);
|
||||
explicit ConnectionValidator(QObject *parent = 0);
|
||||
explicit ConnectionValidator(const QString& connection, QObject *parent = 0);
|
||||
|
||||
enum Status {
|
||||
Undefined,
|
||||
@@ -57,16 +56,15 @@ signals:
|
||||
public slots:
|
||||
|
||||
protected slots:
|
||||
void slotStatusFound(const QUrl&url, const QVariantMap &info);
|
||||
void slotNoStatusFound(QNetworkReply *reply);
|
||||
void slotStatusFound( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotNoStatusFound(QNetworkReply *);
|
||||
|
||||
void slotCheckAuthentication();
|
||||
void slotAuthFailed(QNetworkReply *reply);
|
||||
void slotAuthSuccess();
|
||||
void slotAuthCheck( const QString& ,QNetworkReply * );
|
||||
|
||||
private:
|
||||
QStringList _errors;
|
||||
Account *_account;
|
||||
QString _connection;
|
||||
bool _networkError;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,13 +14,10 @@
|
||||
*/
|
||||
|
||||
#include "mirall/csyncthread.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "owncloudpropagator.h"
|
||||
#include "syncjournaldb.h"
|
||||
#include "syncjournalfilerecord.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -45,28 +42,16 @@
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
void csyncLogCatcher(int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
/* static variables to hold the credentials */
|
||||
|
||||
QMutex CSyncThread::_mutex;
|
||||
QMutex CSyncThread::_syncMutex;
|
||||
|
||||
CSyncThread::CSyncThread(CSYNC *csync, const QString &localPath, const QString &remotePath, SyncJournalDb *journal)
|
||||
CSyncThread::CSyncThread(CSYNC *csync)
|
||||
{
|
||||
_mutex.lock();
|
||||
_localPath = localPath;
|
||||
_remotePath = remotePath;
|
||||
_csync_ctx = csync;
|
||||
_journal = journal;
|
||||
_mutex.unlock();
|
||||
qRegisterMetaType<SyncFileItem>("SyncFileItem");
|
||||
qRegisterMetaType<SyncFileItem::Status>("SyncFileItem::Status");
|
||||
}
|
||||
|
||||
CSyncThread::~CSyncThread()
|
||||
@@ -76,173 +61,110 @@ CSyncThread::~CSyncThread()
|
||||
|
||||
//Convert an error code from csync to a user readable string.
|
||||
// Keep that function thread safe as it can be called from the sync thread or the main thread
|
||||
QString CSyncThread::csyncErrorToString(CSYNC_STATUS err)
|
||||
QString CSyncThread::csyncErrorToString( CSYNC_ERROR_CODE err, const char *errString )
|
||||
{
|
||||
QString errStr;
|
||||
|
||||
switch( err ) {
|
||||
case CSYNC_STATUS_OK:
|
||||
case CSYNC_ERR_NONE:
|
||||
errStr = tr("Success.");
|
||||
break;
|
||||
case CSYNC_STATUS_NO_LOCK:
|
||||
case CSYNC_ERR_LOG:
|
||||
errStr = tr("CSync Logging setup failed.");
|
||||
break;
|
||||
case CSYNC_ERR_LOCK:
|
||||
errStr = tr("CSync failed to create a lock file.");
|
||||
break;
|
||||
case CSYNC_STATUS_STATEDB_LOAD_ERROR:
|
||||
case CSYNC_ERR_STATEDB_LOAD:
|
||||
errStr = tr("CSync failed to load the state db.");
|
||||
break;
|
||||
case CSYNC_STATUS_STATEDB_WRITE_ERROR:
|
||||
errStr = tr("CSync failed to write the state db.");
|
||||
break;
|
||||
case CSYNC_STATUS_NO_MODULE:
|
||||
case CSYNC_ERR_MODULE:
|
||||
errStr = tr("<p>The %1 plugin for csync could not be loaded.<br/>Please verify the installation!</p>").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_STATUS_TIMESKEW:
|
||||
case CSYNC_ERR_TIMESKEW:
|
||||
errStr = tr("The system time on this client is different than the system time on the server. "
|
||||
"Please use a time synchronization service (NTP) on the server and client machines "
|
||||
"so that the times remain the same.");
|
||||
break;
|
||||
case CSYNC_STATUS_FILESYSTEM_UNKNOWN:
|
||||
case CSYNC_ERR_FILESYSTEM:
|
||||
errStr = tr("CSync could not detect the filesystem type.");
|
||||
break;
|
||||
case CSYNC_STATUS_TREE_ERROR:
|
||||
case CSYNC_ERR_TREE:
|
||||
errStr = tr("CSync got an error while processing internal trees.");
|
||||
break;
|
||||
case CSYNC_STATUS_MEMORY_ERROR:
|
||||
case CSYNC_ERR_MEM:
|
||||
errStr = tr("CSync failed to reserve memory.");
|
||||
break;
|
||||
case CSYNC_STATUS_PARAM_ERROR:
|
||||
case CSYNC_ERR_PARAM:
|
||||
errStr = tr("CSync fatal parameter error.");
|
||||
break;
|
||||
case CSYNC_STATUS_UPDATE_ERROR:
|
||||
case CSYNC_ERR_UPDATE:
|
||||
errStr = tr("CSync processing step update failed.");
|
||||
break;
|
||||
case CSYNC_STATUS_RECONCILE_ERROR:
|
||||
case CSYNC_ERR_RECONCILE:
|
||||
errStr = tr("CSync processing step reconcile failed.");
|
||||
break;
|
||||
case CSYNC_STATUS_PROPAGATE_ERROR:
|
||||
case CSYNC_ERR_PROPAGATE:
|
||||
errStr = tr("CSync processing step propagate failed.");
|
||||
break;
|
||||
case CSYNC_STATUS_REMOTE_ACCESS_ERROR:
|
||||
case CSYNC_ERR_ACCESS_FAILED:
|
||||
errStr = tr("<p>The target directory does not exist.</p><p>Please check the sync setup.</p>");
|
||||
break;
|
||||
case CSYNC_STATUS_REMOTE_CREATE_ERROR:
|
||||
case CSYNC_STATUS_REMOTE_STAT_ERROR:
|
||||
case CSYNC_ERR_REMOTE_CREATE:
|
||||
case CSYNC_ERR_REMOTE_STAT:
|
||||
errStr = tr("A remote file can not be written. Please check the remote access.");
|
||||
break;
|
||||
case CSYNC_STATUS_LOCAL_CREATE_ERROR:
|
||||
case CSYNC_STATUS_LOCAL_STAT_ERROR:
|
||||
case CSYNC_ERR_LOCAL_CREATE:
|
||||
case CSYNC_ERR_LOCAL_STAT:
|
||||
errStr = tr("The local filesystem can not be written. Please check permissions.");
|
||||
break;
|
||||
case CSYNC_STATUS_PROXY_ERROR:
|
||||
case CSYNC_ERR_PROXY:
|
||||
errStr = tr("CSync failed to connect through a proxy.");
|
||||
break;
|
||||
case CSYNC_STATUS_PROXY_AUTH_ERROR:
|
||||
errStr = tr("CSync could not authenticate at the proxy.");
|
||||
break;
|
||||
case CSYNC_STATUS_LOOKUP_ERROR:
|
||||
case CSYNC_ERR_LOOKUP:
|
||||
errStr = tr("CSync failed to lookup proxy or server.");
|
||||
break;
|
||||
case CSYNC_STATUS_SERVER_AUTH_ERROR:
|
||||
case CSYNC_ERR_AUTH_SERVER:
|
||||
errStr = tr("CSync failed to authenticate at the %1 server.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_STATUS_CONNECT_ERROR:
|
||||
case CSYNC_ERR_AUTH_PROXY:
|
||||
errStr = tr("CSync failed to authenticate at the proxy.");
|
||||
break;
|
||||
case CSYNC_ERR_CONNECT:
|
||||
errStr = tr("CSync failed to connect to the network.");
|
||||
break;
|
||||
case CSYNC_STATUS_TIMEOUT:
|
||||
case CSYNC_ERR_TIMEOUT:
|
||||
errStr = tr("A network connection timeout happend.");
|
||||
break;
|
||||
case CSYNC_STATUS_HTTP_ERROR:
|
||||
case CSYNC_ERR_HTTP:
|
||||
errStr = tr("A HTTP transmission error happened.");
|
||||
break;
|
||||
case CSYNC_STATUS_PERMISSION_DENIED:
|
||||
errStr = tr("CSync failed due to not handled permission deniend.");
|
||||
case CSYNC_ERR_PERM:
|
||||
errStr = tr("CSync: Permission deniend.");
|
||||
break;
|
||||
case CSYNC_STATUS_NOT_FOUND:
|
||||
errStr = tr("CSync failed to access "); // filename gets added.
|
||||
case CSYNC_ERR_NOT_FOUND:
|
||||
errStr = tr("CSync: File not found.");
|
||||
break;
|
||||
case CSYNC_STATUS_FILE_EXISTS:
|
||||
errStr = tr("CSync tried to create a directory that already exists.");
|
||||
case CSYNC_ERR_EXISTS:
|
||||
errStr = tr("CSync: Directory already exists.");
|
||||
break;
|
||||
case CSYNC_STATUS_OUT_OF_SPACE:
|
||||
errStr = tr("CSync: No space on %1 server available.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_STATUS_QUOTA_EXCEEDED:
|
||||
errStr = tr("CSync: No space on %1 server available.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_STATUS_UNSUCCESSFUL:
|
||||
errStr = tr("CSync unspecified error.");
|
||||
break;
|
||||
case CSYNC_STATUS_ABORTED:
|
||||
errStr = tr("Aborted by the user");
|
||||
case CSYNC_ERR_NOSPC:
|
||||
errStr = tr("CSync: No space left on %1 server.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_ERR_UNSPEC:
|
||||
errStr = tr("CSync: unspecified error.");
|
||||
|
||||
default:
|
||||
errStr = tr("An internal error number %1 happend.").arg( (int) err );
|
||||
}
|
||||
|
||||
if( errString ) {
|
||||
errStr += tr(" Backend Message: ")+QString::fromUtf8(errString);
|
||||
}
|
||||
return errStr;
|
||||
|
||||
}
|
||||
|
||||
bool CSyncThread::checkBlacklisting( SyncFileItem *item )
|
||||
{
|
||||
bool re = false;
|
||||
|
||||
if( !_journal ) {
|
||||
qWarning() << "Journal is undefined!";
|
||||
return false;
|
||||
}
|
||||
|
||||
SyncJournalBlacklistRecord entry = _journal->blacklistEntry(item->_file);
|
||||
item->_blacklistedInDb = false;
|
||||
|
||||
// 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.
|
||||
if( entry.isValid() ) {
|
||||
item->_blacklistedInDb = true;
|
||||
|
||||
if( entry._retryCount <= 0 ) {
|
||||
re = true;
|
||||
}
|
||||
|
||||
// if the retryCount is 0, but the etag for downloads or the mtime for uploads
|
||||
// has changed, it is tried again
|
||||
// note that if the retryCount is -1 we never try again.
|
||||
if( entry._retryCount == 0 ) {
|
||||
if( item->_dir == SyncFileItem::Up ) { // check the modtime
|
||||
if(item->_modtime == 0 || entry._lastTryModtime == 0) {
|
||||
re = false;
|
||||
} else {
|
||||
if( item->_modtime != entry._lastTryModtime ) {
|
||||
re = false;
|
||||
qDebug() << item->_file << " is blacklisted, but has changed mtime!";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// download, check the etag.
|
||||
if( item->_etag.isEmpty() || entry._lastTryEtag.isEmpty() ) {
|
||||
qDebug() << item->_file << "one ETag is empty, no blacklisting";
|
||||
return false;
|
||||
} else {
|
||||
if( item->_etag != entry._lastTryEtag ) {
|
||||
re = false;
|
||||
qDebug() << item->_file << " is blacklisted, but has changed etag!";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( re ) {
|
||||
qDebug() << "Item is on blacklist: " << entry._file << "retries:" << entry._retryCount;
|
||||
item->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
item->_errorString = tr("The item is not synced because of previous errors.");
|
||||
slotProgress( Progress::SoftError, *item );
|
||||
}
|
||||
}
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkLocal( TREE_WALK_FILE* file, void *data )
|
||||
{
|
||||
return static_cast<CSyncThread*>(data)->treewalkFile( file, false );
|
||||
@@ -253,41 +175,66 @@ int CSyncThread::treewalkRemote( TREE_WALK_FILE* file, void *data )
|
||||
return static_cast<CSyncThread*>(data)->treewalkFile( file, true );
|
||||
}
|
||||
|
||||
int CSyncThread::walkFinalize(TREE_WALK_FILE* file, void *data )
|
||||
{
|
||||
return static_cast<CSyncThread*>(data)->treewalkError( file);
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
{
|
||||
if( ! file ) return -1;
|
||||
SyncFileItem item;
|
||||
item._file = QString::fromUtf8( file->path );
|
||||
item._originalFile = item._file;
|
||||
item._instruction = file->instruction;
|
||||
item._dir = SyncFileItem::None;
|
||||
item._fileId = QString::fromUtf8(file->file_id);
|
||||
if(file->error_string) {
|
||||
item._errorString = QString::fromUtf8(file->error_string);
|
||||
}
|
||||
SyncFileItem::Direction dir;
|
||||
|
||||
// record the seen files to be able to clean the journal later
|
||||
_seenFiles[item._file] = QString();
|
||||
int re = 0;
|
||||
|
||||
switch(file->error_status) {
|
||||
case CSYNC_STATUS_OK:
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK:
|
||||
item._errorString = tr("Symbolic links are not supported in syncing.");
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST:
|
||||
item._errorString = tr("File is listed on the ignore list.");
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS:
|
||||
item._errorString = tr("File contains invalid characters that can not be synced cross platform.");
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT("Non handled error-status");
|
||||
/* No error string */
|
||||
if (file->instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& file->instruction != CSYNC_INSTRUCTION_REMOVE) {
|
||||
_hasFiles = true;
|
||||
}
|
||||
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
break;
|
||||
default:
|
||||
if (!_needsUpdate)
|
||||
_needsUpdate = true;
|
||||
}
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
// No need to do anything.
|
||||
return re;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
item._renameTarget = QString::fromUtf8( file->rename_path );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
dir = SyncFileItem::None;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_STAT_ERROR:
|
||||
case CSYNC_INSTRUCTION_DELETED:
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
default:
|
||||
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
}
|
||||
|
||||
item._isDirectory = file->type == CSYNC_FTW_TYPE_DIR;
|
||||
item._modtime = file->modtime;
|
||||
item._etag = file->etag;
|
||||
item._size = file->size;
|
||||
item._should_update_etag = file->should_update_etag;
|
||||
switch( file->type ) {
|
||||
case CSYNC_FTW_TYPE_DIR:
|
||||
item._type = SyncFileItem::Directory;
|
||||
@@ -302,110 +249,66 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
item._type = SyncFileItem::UnknownType;
|
||||
}
|
||||
|
||||
SyncFileItem::Direction dir;
|
||||
|
||||
int re = 0;
|
||||
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
_progressInfo.overall_file_count++;
|
||||
_progressInfo.overall_transmission_size += file->size;
|
||||
//fall trough
|
||||
default:
|
||||
_needsUpdate = true;
|
||||
}
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
// We need to update the database.
|
||||
_journal->setFileRecord(SyncJournalFileRecord(item, _localPath + item._file));
|
||||
item._instruction = CSYNC_INSTRUCTION_NONE;
|
||||
// fall trough
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
if (item._isDirectory && remote) {
|
||||
// Because we want still to update etags of directories
|
||||
dir = SyncFileItem::None;
|
||||
} else {
|
||||
// No need to do anything.
|
||||
_hasFiles = true;
|
||||
|
||||
return re;
|
||||
}
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
item._renameTarget = QString::fromUtf8( file->rename_path );
|
||||
if (item._isDirectory)
|
||||
_renamedFolders.insert(item._file, item._renameTarget);
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
//
|
||||
slotProgress(Progress::SoftError, item, 0, 0);
|
||||
dir = SyncFileItem::None;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_STAT_ERROR:
|
||||
case CSYNC_INSTRUCTION_DELETED:
|
||||
default:
|
||||
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
}
|
||||
|
||||
item._dir = dir;
|
||||
// check for blacklisting of this item.
|
||||
// if the item is on blacklist, the instruction was set to IGNORE
|
||||
checkBlacklisting( &item );
|
||||
|
||||
if (file->instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& file->instruction != CSYNC_INSTRUCTION_REMOVE) {
|
||||
_hasFiles = true;
|
||||
}
|
||||
_mutex.lock();
|
||||
_syncedItems.append(item);
|
||||
_mutex.unlock();
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
|
||||
CSYNC_STATUS err = csync_get_status( ctx );
|
||||
const char *errMsg = csync_get_status_string( ctx );
|
||||
QString errStr = csyncErrorToString(err);
|
||||
if( errMsg ) {
|
||||
if( !errStr.endsWith(" ")) {
|
||||
errStr.append(" ");
|
||||
}
|
||||
errStr += QString::fromUtf8(errMsg);
|
||||
int CSyncThread::treewalkError(TREE_WALK_FILE* file)
|
||||
{
|
||||
SyncFileItem item; // only used for search.
|
||||
item._file= QString::fromUtf8(file->path);
|
||||
int indx = _syncedItems.indexOf(item);
|
||||
|
||||
if ( indx == -1 )
|
||||
return 0;
|
||||
|
||||
if( file &&
|
||||
(file->instruction == CSYNC_INSTRUCTION_STAT_ERROR ||
|
||||
file->instruction == CSYNC_INSTRUCTION_ERROR) ) {
|
||||
_mutex.lock();
|
||||
_syncedItems[indx]._instruction = file->instruction;
|
||||
_syncedItems[indx]._errorString = QString::fromUtf8(file->error_string);
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
// if there is csyncs url modifier in the error message, replace it.
|
||||
if( errStr.contains("ownclouds://") ) errStr.replace("ownclouds://", "https://");
|
||||
if( errStr.contains("owncloud://") ) errStr.replace("owncloud://", "http://");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct CSyncRunScopeHelper {
|
||||
CSyncRunScopeHelper(CSYNC *ctx, CSyncThread *parent)
|
||||
: _ctx(ctx), _parent(parent)
|
||||
{
|
||||
_t.start();
|
||||
}
|
||||
~CSyncRunScopeHelper() {
|
||||
csync_commit(_ctx);
|
||||
|
||||
qDebug() << "CSync run took " << _t.elapsed() << " Milliseconds";
|
||||
emit(_parent->finished());
|
||||
_parent->_syncMutex.unlock();
|
||||
}
|
||||
CSYNC *_ctx;
|
||||
QTime _t;
|
||||
CSyncThread *_parent;
|
||||
};
|
||||
|
||||
void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
|
||||
CSYNC_ERROR_CODE err = csync_get_error( ctx );
|
||||
const char *errMsg = csync_get_error_string( ctx );
|
||||
QString errStr = csyncErrorToString(err, errMsg);
|
||||
qDebug() << " #### ERROR during "<< state << ": " << errStr;
|
||||
|
||||
if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_ABORTED) ) {
|
||||
qDebug() << "Update phase was aborted by user!";
|
||||
} else if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_SERVICE_UNAVAILABLE ) ||
|
||||
CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_CONNECT_ERROR )) {
|
||||
switch (err) {
|
||||
case CSYNC_ERR_SERVICE_UNAVAILABLE:
|
||||
case CSYNC_ERR_CONNECT:
|
||||
emit csyncUnavailable();
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
emit csyncError(errStr);
|
||||
}
|
||||
csync_commit(_csync_ctx);
|
||||
emit finished();
|
||||
_syncMutex.unlock();
|
||||
thread()->quit();
|
||||
}
|
||||
|
||||
void CSyncThread::startSync()
|
||||
@@ -420,63 +323,44 @@ void CSyncThread::startSync()
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << "Sync started";
|
||||
|
||||
|
||||
qDebug() << "starting to sync " << qApp->thread() << QThread::currentThread();
|
||||
_syncedItems.clear();
|
||||
|
||||
_mutex.lock();
|
||||
_syncedItems.clear();
|
||||
_needsUpdate = false;
|
||||
if (!_abortRequested.fetchAndAddRelease(0)) {
|
||||
csync_resume(_csync_ctx);
|
||||
}
|
||||
_mutex.unlock();
|
||||
|
||||
// cleans up behind us and emits finished() to ease error handling
|
||||
CSyncRunScopeHelper helper(_csync_ctx, this);
|
||||
|
||||
// maybe move this somewhere else where it can influence a running sync?
|
||||
MirallConfigFile cfg;
|
||||
|
||||
if (!_journal->exists()) {
|
||||
qDebug() << "=====sync looks new (no DB exists), activating recursive PROPFIND if csync supports it";
|
||||
bool no_recursive_propfind = false;
|
||||
csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind);
|
||||
} else {
|
||||
// retrieve the file count from the db and close it afterwards because
|
||||
// csync_update also opens the database.
|
||||
int fileRecordCount = 0;
|
||||
fileRecordCount = _journal->getFileRecordCount();
|
||||
_journal->close();
|
||||
|
||||
if( fileRecordCount == -1 ) {
|
||||
qDebug() << "No way to create a sync journal!";
|
||||
emit csyncError(tr("Unable to initialize a sync journal."));
|
||||
|
||||
csync_commit(_csync_ctx);
|
||||
emit finished();
|
||||
_syncMutex.unlock();
|
||||
thread()->quit();
|
||||
|
||||
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";
|
||||
}
|
||||
int downloadLimit = 0;
|
||||
if (cfg.useDownloadLimit()) {
|
||||
downloadLimit = cfg.downloadLimit() * 1000;
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "bandwidth_limit_download", &downloadLimit);
|
||||
|
||||
int uploadLimit = -75; // 75%
|
||||
int useUpLimit = cfg.useUploadLimit();
|
||||
if ( useUpLimit >= 1) {
|
||||
uploadLimit = cfg.uploadLimit() * 1000;
|
||||
} else if (useUpLimit == 0) {
|
||||
uploadLimit = 0;
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "bandwidth_limit_upload", &uploadLimit);
|
||||
|
||||
csync_set_progress_callback( _csync_ctx, cb_progress );
|
||||
|
||||
csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx);
|
||||
csync_set_userdata(_csync_ctx, this);
|
||||
|
||||
// TODO: This should be a part of this method, but we don't have
|
||||
// any way to get "session_key" module property from csync. Had we
|
||||
// have it, then we could keep this code and remove it from
|
||||
// AbstractCredentials implementations.
|
||||
if (Account *account = AccountManager::instance()->account()) {
|
||||
account->credentials()->syncContextPreStart(_csync_ctx);
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << "No default Account object, huh?";
|
||||
}
|
||||
cfg.getCredentials()->syncContextPreStart(_csync_ctx);
|
||||
// if (_lastAuthCookies.length() > 0) {
|
||||
// // Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
|
||||
// // when https://github.com/owncloud/core/pull/4042 is merged.
|
||||
@@ -491,34 +375,23 @@ void CSyncThread::startSync()
|
||||
// }
|
||||
|
||||
// csync_set_auth_callback( _csync_ctx, getauth );
|
||||
csync_set_log_callback( csyncLogCatcher );
|
||||
csync_set_log_level( 11 );
|
||||
|
||||
_syncTime.start();
|
||||
|
||||
QElapsedTimer updateTime;
|
||||
updateTime.start();
|
||||
|
||||
qDebug() << "#### Update start #################################################### >>";
|
||||
|
||||
if( csync_update(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "csync_update");
|
||||
return;
|
||||
}
|
||||
qDebug() << "<<#### Update end #################################################### " << updateTime.elapsed();
|
||||
qDebug() << "<<#### Update end ###########################################################";
|
||||
|
||||
if( csync_reconcile(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "csync_reconcile");
|
||||
return;
|
||||
}
|
||||
|
||||
slotProgress(Progress::StartSync, SyncFileItem(), 0, 0);
|
||||
|
||||
_progressInfo = Progress::Info();
|
||||
|
||||
_hasFiles = false;
|
||||
bool walkOk = true;
|
||||
_seenFiles.clear();
|
||||
|
||||
if( csync_walk_local_tree(_csync_ctx, &treewalkLocal, 0) < 0 ) {
|
||||
qDebug() << "Error in local treewalk.";
|
||||
walkOk = false;
|
||||
@@ -527,15 +400,9 @@ void CSyncThread::startSync()
|
||||
qDebug() << "Error in remote treewalk.";
|
||||
}
|
||||
|
||||
// Adjust the paths for the renames.
|
||||
for (SyncFileItemVector::iterator it = _syncedItems.begin();
|
||||
it != _syncedItems.end(); ++it) {
|
||||
it->_file = adjustRenamedPath(it->_file);
|
||||
}
|
||||
|
||||
if (!_hasFiles && !_syncedItems.isEmpty()) {
|
||||
qDebug() << Q_FUNC_INFO << "All the files are going to be removed, asking the user";
|
||||
bool cancel = false;
|
||||
bool cancel = true;
|
||||
emit aboutToRemoveAllFiles(_syncedItems.first()._dir, &cancel);
|
||||
if (cancel) {
|
||||
qDebug() << Q_FUNC_INFO << "Abort sync";
|
||||
@@ -546,153 +413,95 @@ void CSyncThread::startSync()
|
||||
if (_needsUpdate)
|
||||
emit(started());
|
||||
|
||||
ne_session_s *session = 0;
|
||||
// that call to set property actually is a get which will return the session
|
||||
// FIXME add a csync_get_module_property to csync
|
||||
|
||||
csync_set_module_property(_csync_ctx, "get_dav_session", &session);
|
||||
Q_ASSERT(session);
|
||||
|
||||
_propagator.reset(new OwncloudPropagator (session, _localPath, _remotePath,
|
||||
_journal, &_abortRequested));
|
||||
connect(_propagator.data(), SIGNAL(completed(SyncFileItem)),
|
||||
this, SLOT(transferCompleted(SyncFileItem)), Qt::QueuedConnection);
|
||||
connect(_propagator.data(), SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)),
|
||||
this, SLOT(slotProgress(Progress::Kind,SyncFileItem,quint64,quint64)));
|
||||
connect(_propagator.data(), SIGNAL(finished()), this, SLOT(slotFinished()));
|
||||
|
||||
int downloadLimit = 0;
|
||||
if (cfg.useDownloadLimit()) {
|
||||
downloadLimit = cfg.downloadLimit() * 1000;
|
||||
}
|
||||
_propagator->_downloadLimit = downloadLimit;
|
||||
|
||||
int uploadLimit = -75; // 75%
|
||||
int useUpLimit = cfg.useUploadLimit();
|
||||
if ( useUpLimit >= 1) {
|
||||
uploadLimit = cfg.uploadLimit() * 1000;
|
||||
} else if (useUpLimit == 0) {
|
||||
uploadLimit = 0;
|
||||
}
|
||||
_propagator->_uploadLimit = uploadLimit;
|
||||
|
||||
_propagator->start(_syncedItems);
|
||||
}
|
||||
|
||||
void CSyncThread::transferCompleted(const SyncFileItem &item)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << item._file << item._status << item._errorString;
|
||||
|
||||
/* Update the _syncedItems vector */
|
||||
int idx = _syncedItems.indexOf(item);
|
||||
if (idx >= 0) {
|
||||
_syncedItems[idx]._instruction = item._instruction;
|
||||
_syncedItems[idx]._errorString = item._errorString;
|
||||
_syncedItems[idx]._status = item._status;
|
||||
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "Could not find index in synced items!";
|
||||
}
|
||||
|
||||
if (item._status == SyncFileItem::FatalError) {
|
||||
emit csyncError(item._errorString);
|
||||
}
|
||||
}
|
||||
|
||||
void CSyncThread::slotFinished()
|
||||
{
|
||||
// emit the treewalk results.
|
||||
if( ! _journal->postSyncCleanup( _seenFiles ) ) {
|
||||
qDebug() << "Cleaning of synced ";
|
||||
}
|
||||
_journal->commit("All Finished.", false);
|
||||
emit treeWalkResult(_syncedItems);
|
||||
|
||||
csync_commit(_csync_ctx);
|
||||
|
||||
qDebug() << "CSync run took " << _syncTime.elapsed() << " Milliseconds";
|
||||
slotProgress(Progress::EndSync,SyncFileItem(), 0 , 0);
|
||||
emit finished();
|
||||
_propagator.reset(0);
|
||||
_syncMutex.unlock();
|
||||
thread()->quit();
|
||||
}
|
||||
|
||||
void CSyncThread::progressProblem(Progress::Kind kind, const SyncFileItem& item)
|
||||
{
|
||||
Progress::SyncProblem problem;
|
||||
|
||||
problem.kind = kind;
|
||||
problem.current_file = item._file;
|
||||
problem.error_message = item._errorString;
|
||||
problem.error_code = item._httpErrorCode;
|
||||
problem.timestamp = QDateTime::currentDateTime();
|
||||
|
||||
// connected to something in folder.
|
||||
emit transmissionProblem( problem );
|
||||
}
|
||||
|
||||
void CSyncThread::slotProgress(Progress::Kind kind, const SyncFileItem& item, quint64 curr, quint64 total)
|
||||
{
|
||||
if( Progress::isErrorKind(kind) ) {
|
||||
progressProblem(kind, item);
|
||||
if( csync_propagate(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "cysnc_reconcile");
|
||||
return;
|
||||
}
|
||||
|
||||
if( kind == Progress::StartSync ) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_currentFileNo = 0;
|
||||
_lastOverallBytes = 0;
|
||||
}
|
||||
if( kind == Progress::StartDelete ||
|
||||
kind == Progress::StartDownload ||
|
||||
kind == Progress::StartRename ||
|
||||
kind == Progress::StartUpload ) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_currentFileNo += 1;
|
||||
}
|
||||
|
||||
if( kind == Progress::EndUpload ||
|
||||
kind == Progress::EndDownload ||
|
||||
kind == Progress::EndRename ||
|
||||
kind == Progress::EndDelete ) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_lastOverallBytes += total;
|
||||
curr = 0;
|
||||
}
|
||||
|
||||
Progress::Info pInfo(_progressInfo);
|
||||
pInfo.kind = kind;
|
||||
pInfo.current_file = item._file;
|
||||
pInfo.rename_target = item._renameTarget;
|
||||
pInfo.file_size = total;
|
||||
pInfo.current_file_bytes = curr;
|
||||
pInfo.current_file_no = _currentFileNo;
|
||||
pInfo.timestamp = QDateTime::currentDateTime();
|
||||
pInfo.overall_current_bytes = _lastOverallBytes + curr;
|
||||
// Connect to something in folder!
|
||||
emit transmissionProgress( pInfo );
|
||||
}
|
||||
|
||||
/* Given a path on the remote, give the path as it is when the rename is done */
|
||||
QString CSyncThread::adjustRenamedPath(const QString& original)
|
||||
{
|
||||
int slashPos = original.size();
|
||||
while ((slashPos = original.lastIndexOf('/' , slashPos - 1)) > 0) {
|
||||
QHash< QString, QString >::const_iterator it = _renamedFolders.constFind(original.left(slashPos));
|
||||
if (it != _renamedFolders.constEnd()) {
|
||||
return *it + original.mid(slashPos);
|
||||
if( walkOk ) {
|
||||
if( csync_walk_local_tree(_csync_ctx, &walkFinalize, 0) < 0 ||
|
||||
csync_walk_remote_tree(_csync_ctx, &walkFinalize, 0 ) < 0 ) {
|
||||
qDebug() << "Error in finalize treewalk.";
|
||||
} else {
|
||||
// emit the treewalk results.
|
||||
emit treeWalkResult(_syncedItems);
|
||||
}
|
||||
}
|
||||
return original;
|
||||
qDebug() << Q_FUNC_INFO << "Sync finished";
|
||||
}
|
||||
|
||||
void CSyncThread::abort()
|
||||
Progress::Kind CSyncThread::csyncToProgressKind( enum csync_notify_type_e kind )
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
csync_request_abort(_csync_ctx);
|
||||
_abortRequested = true;
|
||||
Progress::Kind pKind = Progress::Invalid;
|
||||
|
||||
switch(kind) {
|
||||
case CSYNC_NOTIFY_INVALID:
|
||||
pKind = Progress::Invalid;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_SYNC_SEQUENCE:
|
||||
pKind = Progress::StartSync;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_DOWNLOAD:
|
||||
pKind = Progress::StartDownload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_UPLOAD:
|
||||
pKind = Progress::StartUpload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_PROGRESS:
|
||||
pKind = Progress::Context;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_DOWNLOAD:
|
||||
pKind = Progress::EndDownload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_UPLOAD:
|
||||
pKind = Progress::EndUpload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_SYNC_SEQUENCE:
|
||||
pKind = Progress::EndSync;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_DELETE:
|
||||
pKind = Progress::StartDelete;
|
||||
break;
|
||||
case CSYNC_NOTIFY_END_DELETE:
|
||||
pKind = Progress::EndDelete;
|
||||
break;
|
||||
case CSYNC_NOTIFY_ERROR:
|
||||
pKind = Progress::Error;
|
||||
break;
|
||||
default:
|
||||
pKind = Progress::Invalid;
|
||||
break;
|
||||
}
|
||||
return pKind;
|
||||
}
|
||||
|
||||
void CSyncThread::cb_progress( CSYNC_PROGRESS *progress, void *userdata )
|
||||
{
|
||||
if( !progress ) {
|
||||
qDebug() << "No progress block in progress callback found!";
|
||||
return;
|
||||
}
|
||||
if( !userdata ) {
|
||||
qDebug() << "No thread given in progress callback!";
|
||||
return;
|
||||
}
|
||||
Progress::Info pInfo;
|
||||
CSyncThread *thread = static_cast<CSyncThread*>(userdata);
|
||||
|
||||
pInfo.kind = thread->csyncToProgressKind( progress->kind );
|
||||
pInfo.current_file = QUrl::fromEncoded( progress->path ).toString();
|
||||
pInfo.file_size = progress->file_size;
|
||||
pInfo.current_file_bytes = progress->curr_bytes;
|
||||
|
||||
pInfo.overall_file_count = progress->overall_file_count;
|
||||
pInfo.current_file_no = progress->current_file_no;
|
||||
pInfo.overall_transmission_size = progress->overall_transmission_size;
|
||||
pInfo.overall_current_bytes = progress->current_overall_bytes;
|
||||
pInfo.timestamp = QDateTime::currentDateTime();
|
||||
|
||||
// Connect to something in folder!
|
||||
thread->transmissionProgress( pInfo );
|
||||
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
#include <qelapsedtimer.h>
|
||||
#include <qstack.h>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkCookie>
|
||||
|
||||
@@ -35,41 +33,26 @@ class QProcess;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class SyncJournalFileRecord;
|
||||
|
||||
class SyncJournalDb;
|
||||
|
||||
class OwncloudPropagator;
|
||||
|
||||
void csyncLogCatcher(int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/);
|
||||
|
||||
class CSyncThread : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CSyncThread(CSYNC *, const QString &localPath, const QString &remotePath, SyncJournalDb *journal);
|
||||
CSyncThread(CSYNC *);
|
||||
~CSyncThread();
|
||||
|
||||
static QString csyncErrorToString( CSYNC_STATUS);
|
||||
static QString csyncErrorToString( CSYNC_ERROR_CODE, const char * );
|
||||
|
||||
Q_INVOKABLE void startSync();
|
||||
|
||||
/* Abort the sync. Called from the main thread */
|
||||
void abort();
|
||||
|
||||
signals:
|
||||
void fileReceived( const QString& );
|
||||
void fileRemoved( const QString& );
|
||||
void csyncError( const QString& );
|
||||
void csyncWarning( const QString& );
|
||||
void csyncUnavailable();
|
||||
void treeWalkResult(const SyncFileItemVector&);
|
||||
|
||||
void transmissionProgress( const Progress::Info& progress );
|
||||
void transmissionProblem( const Progress::SyncProblem& problem );
|
||||
|
||||
void csyncStateDbFile( const QString& );
|
||||
void wipeDb();
|
||||
|
||||
@@ -78,19 +61,20 @@ signals:
|
||||
|
||||
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
|
||||
|
||||
private slots:
|
||||
void transferCompleted(const SyncFileItem& item);
|
||||
void slotFinished();
|
||||
void slotProgress(Progress::Kind kind, const SyncFileItem &item, quint64 curr = 0, quint64 total = 0);
|
||||
|
||||
private:
|
||||
void handleSyncError(CSYNC *ctx, const char *state);
|
||||
void progressProblem(Progress::Kind kind, const SyncFileItem& item);
|
||||
|
||||
static void cb_progress( CSYNC_PROGRESS *progress, void *userdata );
|
||||
|
||||
static int treewalkLocal( TREE_WALK_FILE*, void *);
|
||||
static int treewalkRemote( TREE_WALK_FILE*, void *);
|
||||
int treewalkFile( TREE_WALK_FILE*, bool );
|
||||
bool checkBlacklisting( SyncFileItem *item );
|
||||
int treewalkError( TREE_WALK_FILE* );
|
||||
|
||||
Progress::Kind csyncToProgressKind( enum csync_notify_type_e kind );
|
||||
static int walkFinalize(TREE_WALK_FILE*, void* );
|
||||
|
||||
|
||||
|
||||
static QMutex _mutex;
|
||||
static QMutex _syncMutex;
|
||||
@@ -98,28 +82,8 @@ private:
|
||||
|
||||
CSYNC *_csync_ctx;
|
||||
bool _needsUpdate;
|
||||
QString _localPath;
|
||||
QString _remotePath;
|
||||
SyncJournalDb *_journal;
|
||||
QScopedPointer <OwncloudPropagator> _propagator;
|
||||
QElapsedTimer _syncTime;
|
||||
QString _lastDeleted; // if the last item was a path and it has been deleted
|
||||
QHash <QString, QString> _seenFiles;
|
||||
|
||||
|
||||
// maps the origin and the target of the folders that have been renamed
|
||||
QHash<QString, QString> _renamedFolders;
|
||||
QString adjustRenamedPath(const QString &original);
|
||||
|
||||
bool _hasFiles; // true if there is at least one file that is not ignored or removed
|
||||
Progress::Info _progressInfo;
|
||||
int _downloadLimit;
|
||||
int _uploadLimit;
|
||||
qint64 _currentFileNo;
|
||||
qint64 _overallFileCount;
|
||||
quint64 _lastOverallBytes;
|
||||
|
||||
QAtomicInt _abortRequested;
|
||||
|
||||
friend struct CSyncRunScopeHelper;
|
||||
};
|
||||
|
||||
@@ -15,35 +15,16 @@
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/folderwatcher.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
#include "mirall/syncjournalfilerecord.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/utility.h"
|
||||
|
||||
#include "folderman.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
enum csync_exclude_type_e {
|
||||
CSYNC_NOT_EXCLUDED = 0,
|
||||
CSYNC_FILE_SILENTLY_EXCLUDED,
|
||||
CSYNC_FILE_EXCLUDE_AND_REMOVE,
|
||||
CSYNC_FILE_EXCLUDE_LIST,
|
||||
CSYNC_FILE_EXCLUDE_INVALID_CHAR
|
||||
};
|
||||
typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;
|
||||
|
||||
CSYNC_EXCLUDE_TYPE csync_excluded(CSYNC *ctx, const char *path, int filetype);
|
||||
|
||||
}
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
@@ -54,19 +35,28 @@ CSYNC_EXCLUDE_TYPE csync_excluded(CSYNC *ctx, const char *path, int filetype);
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
void csyncLogCatcher(CSYNC */*ctx*/,
|
||||
int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
Folder::Folder(const QString &alias, const QString &path, const QString& secondPath, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _path(path)
|
||||
, _remotePath(secondPath)
|
||||
, _secondPath(secondPath)
|
||||
, _alias(alias)
|
||||
, _enabled(true)
|
||||
, _userSyncEnabled(true)
|
||||
, _thread(0)
|
||||
, _csync(0)
|
||||
, _csyncError(false)
|
||||
, _csyncUnavail(false)
|
||||
, _wipeDb(false)
|
||||
, _proxyDirty(true)
|
||||
, _journal(path)
|
||||
, _csync_ctx(0)
|
||||
{
|
||||
qsrand(QTime::currentTime().msec());
|
||||
@@ -97,7 +87,7 @@ Folder::Folder(const QString &alias, const QString &path, const QString& secondP
|
||||
|
||||
bool Folder::init()
|
||||
{
|
||||
QString url = Utility::toCSyncScheme(remoteUrl().toString());
|
||||
QString url = Utility::toCSyncScheme(ownCloudInfo::instance()->webdavUrl() + secondPath());
|
||||
QString localpath = path();
|
||||
|
||||
if( csync_create( &_csync_ctx, localpath.toUtf8().data(), url.toUtf8().data() ) < 0 ) {
|
||||
@@ -105,29 +95,19 @@ bool Folder::init()
|
||||
slotCSyncError(tr("Unable to create csync-context"));
|
||||
_csync_ctx = 0;
|
||||
} else {
|
||||
csync_set_log_callback( csyncLogCatcher );
|
||||
csync_set_log_level( 11 );
|
||||
csync_set_log_callback( _csync_ctx, csyncLogCatcher );
|
||||
csync_set_log_verbosity(_csync_ctx, 11);
|
||||
|
||||
MirallConfigFile cfgFile;
|
||||
csync_set_config_dir( _csync_ctx, cfgFile.configPath().toUtf8() );
|
||||
|
||||
csync_enable_conflictcopys(_csync_ctx);
|
||||
setIgnoredFiles();
|
||||
if (Account *account = AccountManager::instance()->account()) {
|
||||
account->credentials()->syncContextPreInit(_csync_ctx);
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << "No default Account object, huh?";
|
||||
}
|
||||
cfgFile.getCredentials()->syncContextPreInit(_csync_ctx);
|
||||
|
||||
if( csync_init( _csync_ctx ) < 0 ) {
|
||||
qDebug() << "Could not initialize csync!" << csync_get_status(_csync_ctx) << csync_get_status_string(_csync_ctx);
|
||||
QString errStr = CSyncThread::csyncErrorToString(CSYNC_STATUS(csync_get_status(_csync_ctx)));
|
||||
const char *errMsg = csync_get_status_string(_csync_ctx);
|
||||
if( errMsg ) {
|
||||
errStr += QLatin1String("<br/>");
|
||||
errStr += QString::fromUtf8(errMsg);
|
||||
}
|
||||
slotCSyncError(errStr);
|
||||
qDebug() << "Could not initialize csync!" << csync_get_error(_csync_ctx) << csync_get_error_string(_csync_ctx);
|
||||
slotCSyncError(CSyncThread::csyncErrorToString(csync_get_error(_csync_ctx), csync_get_error_string(_csync_ctx)));
|
||||
csync_destroy(_csync_ctx);
|
||||
_csync_ctx = 0;
|
||||
}
|
||||
@@ -138,7 +118,8 @@ bool Folder::init()
|
||||
Folder::~Folder()
|
||||
{
|
||||
if( _thread ) {
|
||||
_csync->abort();
|
||||
_thread->quit();
|
||||
csync_request_abort(_csync_ctx);
|
||||
_thread->wait();
|
||||
}
|
||||
delete _csync;
|
||||
@@ -201,23 +182,9 @@ bool Folder::isBusy() const
|
||||
return ( _thread && _thread->isRunning() );
|
||||
}
|
||||
|
||||
QString Folder::remotePath() const
|
||||
QString Folder::secondPath() const
|
||||
{
|
||||
return _remotePath;
|
||||
}
|
||||
|
||||
QUrl Folder::remoteUrl() const
|
||||
{
|
||||
Account *account = AccountManager::instance()->account();
|
||||
QUrl url = account->davUrl();
|
||||
QString path = url.path();
|
||||
if (!path.endsWith('/')) {
|
||||
path.append('/');
|
||||
}
|
||||
path.append(_remotePath);
|
||||
url.setPath(path);
|
||||
qDebug() << url;
|
||||
return url;
|
||||
return _secondPath;
|
||||
}
|
||||
|
||||
QString Folder::nativePath() const
|
||||
@@ -234,16 +201,32 @@ void Folder::setSyncEnabled( bool doit )
|
||||
{
|
||||
_enabled = doit;
|
||||
|
||||
if( doit ) {
|
||||
if( doit && userSyncEnabled() ) {
|
||||
// qDebug() << "Syncing enabled on folder " << name();
|
||||
_pollTimer.start();
|
||||
_watcher->clearPendingEvents(); // FIXME 1.5: Why isn't that happening in setEventsEnabled?
|
||||
_watcher->setEventsEnabled(true);
|
||||
_timeSinceLastSync.restart();
|
||||
} else {
|
||||
// do not stop or start the watcher here, that is done internally by
|
||||
// folder class. Even if the watcher fires, the folder does not
|
||||
// schedule itself because it checks the var. _enabled before.
|
||||
_pollTimer.stop();
|
||||
_watcher->setEventsEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Folder::userSyncEnabled()
|
||||
{
|
||||
return _userSyncEnabled;
|
||||
}
|
||||
|
||||
void Folder::slotSetSyncUserEnabled( bool enable )
|
||||
{
|
||||
_userSyncEnabled = enable;
|
||||
setSyncEnabled( syncEnabled() ); // no change on the system enable flag.
|
||||
}
|
||||
|
||||
void Folder::setSyncState(SyncResult::Status state)
|
||||
{
|
||||
_syncResult.setStatus(state);
|
||||
@@ -256,11 +239,15 @@ SyncResult Folder::syncResult() const
|
||||
|
||||
void Folder::evaluateSync(const QStringList &/*pathList*/)
|
||||
{
|
||||
if( !_enabled ) {
|
||||
if( !syncEnabled() ) {
|
||||
qDebug() << "*" << alias() << "sync skipped, disabled!";
|
||||
return;
|
||||
}
|
||||
|
||||
if( !userSyncEnabled() ) {
|
||||
qDebug() << "*" << alias() << "sync skipped, user disabled!";
|
||||
return;
|
||||
}
|
||||
_syncResult.setStatus( SyncResult::NotYetStarted );
|
||||
_syncResult.clearErrors();
|
||||
emit scheduleToSync( alias() );
|
||||
@@ -269,18 +256,18 @@ void Folder::evaluateSync(const QStringList &/*pathList*/)
|
||||
|
||||
void Folder::slotPollTimerTimeout()
|
||||
{
|
||||
qDebug() << "* Polling" << alias() << "for changes. (time since last sync:" << (_timeSinceLastSync.elapsed() / 1000) << "s)";
|
||||
qDebug() << "* Polling" << alias() << "for changes. (time since next sync:" << (_timeSinceLastSync.elapsed() / 1000) << "s)";
|
||||
|
||||
// Force sync if the last sync is a long time ago or if there was a serious problem.
|
||||
if (quint64(_timeSinceLastSync.elapsed()) > MirallConfigFile().forceSyncInterval() ||
|
||||
_syncResult.status() != SyncResult::Success ) {
|
||||
!(_syncResult.status() == SyncResult::Success || _syncResult.status() == SyncResult::Problem)) {
|
||||
qDebug() << "** Force Sync now";
|
||||
evaluateSync(QStringList());
|
||||
} else {
|
||||
RequestEtagJob* job = new RequestEtagJob(AccountManager::instance()->account(), remotePath(), this);
|
||||
RequestEtagJob* job = new RequestEtagJob(secondPath(), this);
|
||||
// check if the etag is different
|
||||
QObject::connect(job, SIGNAL(etagRetreived(QString)), this, SLOT(etagRetreived(QString)));
|
||||
QObject::connect(job, SIGNAL(networkError(QNetworkReply*)), this, SLOT(slotNetworkUnavailable()));
|
||||
job->start();
|
||||
QObject::connect(job, SIGNAL(networkError()), this, SLOT(slotNetworkUnavailable()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +286,6 @@ void Folder::etagRetreived(const QString& etag)
|
||||
|
||||
void Folder::slotNetworkUnavailable()
|
||||
{
|
||||
AccountManager::instance()->account()->setState(Account::Disconnected);
|
||||
_syncResult.setStatus(SyncResult::Unavailable);
|
||||
emit syncStateChange();
|
||||
}
|
||||
@@ -317,16 +303,15 @@ void Folder::bubbleUpSyncResult()
|
||||
int removedItems = 0;
|
||||
int updatedItems = 0;
|
||||
int ignoredItems = 0;
|
||||
int renamedItems = 0;
|
||||
|
||||
SyncFileItem firstItemNew;
|
||||
SyncFileItem firstItemDeleted;
|
||||
SyncFileItem firstItemUpdated;
|
||||
SyncFileItem firstItemRenamed;
|
||||
|
||||
Logger *logger = Logger::instance();
|
||||
|
||||
foreach (const SyncFileItem &item, _syncResult.syncFileItemVector() ) {
|
||||
if( item._status == SyncFileItem::FatalError || item._status == SyncFileItem::NormalError ) {
|
||||
if( item._instruction == CSYNC_INSTRUCTION_ERROR ) {
|
||||
slotCSyncError( tr("File %1: %2").arg(item._file).arg(item._errorString) );
|
||||
logger->postOptionalGuiLog(tr("File %1").arg(item._file), item._errorString);
|
||||
|
||||
@@ -353,8 +338,7 @@ void Folder::bubbleUpSyncResult()
|
||||
}
|
||||
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
updatedItems++;
|
||||
if (firstItemUpdated.isEmpty())
|
||||
firstItemUpdated = item;
|
||||
@@ -362,12 +346,6 @@ void Folder::bubbleUpSyncResult()
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
qDebug() << "Got Instruction ERROR. " << _syncResult.errorString();
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
if (firstItemRenamed.isEmpty()) {
|
||||
firstItemRenamed = item;
|
||||
}
|
||||
renamedItems++;
|
||||
break;
|
||||
default:
|
||||
// nothing.
|
||||
break;
|
||||
@@ -382,48 +360,31 @@ void Folder::bubbleUpSyncResult()
|
||||
|
||||
_syncResult.setWarnCount(ignoredItems);
|
||||
|
||||
|
||||
createGuiLog( firstItemNew._file, tr("downloaded"), newItems );
|
||||
createGuiLog( firstItemDeleted._file, tr("removed"), removedItems );
|
||||
createGuiLog( firstItemUpdated._file, tr("updated"), updatedItems );
|
||||
|
||||
if( !firstItemRenamed.isEmpty() ) {
|
||||
QString renameVerb = tr("renamed");
|
||||
// if the path changes it's rather a move
|
||||
QDir renTarget = QFileInfo(firstItemRenamed._renameTarget).dir();
|
||||
QDir renSource = QFileInfo(firstItemRenamed._file).dir();
|
||||
if(renTarget != renSource) {
|
||||
renameVerb = tr("moved");
|
||||
}
|
||||
createGuiLog( firstItemRenamed._file, tr("%1 to %2").arg(renameVerb).arg(firstItemRenamed._renameTarget), renamedItems );
|
||||
}
|
||||
|
||||
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
||||
}
|
||||
|
||||
void Folder::createGuiLog( const QString& filename, const QString& verb, int count )
|
||||
{
|
||||
if(count > 0) {
|
||||
Logger *logger = Logger::instance();
|
||||
|
||||
QString file = QDir::toNativeSeparators(filename);
|
||||
if (count == 1) {
|
||||
logger->postOptionalGuiLog(tr("File %1").arg(verb), tr("'%1' has been %2.").arg(file).arg(verb));
|
||||
} else {
|
||||
logger->postOptionalGuiLog(tr("Files %1").arg(verb),
|
||||
tr("'%1' and %2 other files have been %3.").arg(file).arg(count-1).arg(verb));
|
||||
}
|
||||
if (newItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemNew._file);
|
||||
if (newItems == 1)
|
||||
logger->postOptionalGuiLog(tr("New file available"), tr("'%1' has been synced to this machine.").arg(file));
|
||||
else
|
||||
logger->postOptionalGuiLog(tr("New files available"), tr("'%1' and %n other file(s) have been synced to this machine.",
|
||||
"", newItems-1).arg(file));
|
||||
}
|
||||
if (removedItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemDeleted._file);
|
||||
if (removedItems == 1)
|
||||
logger->postOptionalGuiLog(tr("File removed"), tr("'%1' has been removed.").arg(file));
|
||||
else
|
||||
logger->postOptionalGuiLog(tr("Files removed"), tr("'%1' and %n other file(s) have been removed.",
|
||||
"", removedItems-1).arg(file));
|
||||
}
|
||||
if (updatedItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemUpdated._file);
|
||||
if (updatedItems == 1)
|
||||
logger->postOptionalGuiLog(tr("File updated"), tr("'%1' has been updated.").arg(file));
|
||||
else
|
||||
logger->postOptionalGuiLog(tr("Files updated"), tr("'%1' and %n other file(s) have been updated.",
|
||||
"", updatedItems-1).arg(file));
|
||||
}
|
||||
}
|
||||
|
||||
int Folder::blackListEntryCount()
|
||||
{
|
||||
return _journal.blackListEntryCount();
|
||||
}
|
||||
|
||||
int Folder::slotWipeBlacklist()
|
||||
{
|
||||
return _journal.wipeBlacklist();
|
||||
}
|
||||
|
||||
void Folder::slotLocalPathChanged( const QString& dir )
|
||||
@@ -466,27 +427,36 @@ void Folder::slotCatchWatcherError(const QString& error)
|
||||
Logger::instance()->postOptionalGuiLog(tr("Error"), error);
|
||||
}
|
||||
|
||||
void Folder::slotTerminateSync(bool block)
|
||||
void Folder::slotTerminateSync()
|
||||
{
|
||||
qDebug() << "folder " << alias() << " Terminating!";
|
||||
MirallConfigFile cfg;
|
||||
QString configDir = cfg.configPath();
|
||||
qDebug() << "csync's Config Dir: " << configDir;
|
||||
|
||||
if( _thread && _csync ) {
|
||||
_csync->abort();
|
||||
|
||||
// Do not display an error message, user knows his own actions.
|
||||
// _errors.append( tr("The CSync thread terminated.") );
|
||||
// _csyncError = true;
|
||||
if (!block) {
|
||||
setSyncState(SyncResult::SyncAbortRequested);
|
||||
return;
|
||||
}
|
||||
|
||||
csync_request_abort(_csync_ctx);
|
||||
_thread->quit();
|
||||
_thread->wait();
|
||||
_csync->deleteLater();
|
||||
delete _thread;
|
||||
_csync = 0;
|
||||
_thread = 0;
|
||||
slotCSyncFinished();
|
||||
csync_resume(_csync_ctx);
|
||||
}
|
||||
|
||||
if( ! configDir.isEmpty() ) {
|
||||
QFile file( configDir + QLatin1String("/lock"));
|
||||
if( file.exists() ) {
|
||||
qDebug() << "After termination, lock file exists and gets removed.";
|
||||
file.remove();
|
||||
}
|
||||
}
|
||||
|
||||
_errors.append( tr("The CSync thread terminated.") );
|
||||
_csyncError = true;
|
||||
qDebug() << "-> CSync Terminated!";
|
||||
slotCSyncFinished();
|
||||
setSyncEnabled(false);
|
||||
}
|
||||
|
||||
@@ -498,8 +468,6 @@ void Folder::wipe()
|
||||
{
|
||||
QString stateDbFile = path()+QLatin1String(".csync_journal.db");
|
||||
|
||||
_journal.close(); // close the sync journal
|
||||
|
||||
QFile file(stateDbFile);
|
||||
if( file.exists() ) {
|
||||
if( !file.remove()) {
|
||||
@@ -536,26 +504,31 @@ void Folder::setIgnoredFiles()
|
||||
|
||||
void Folder::setProxy()
|
||||
{
|
||||
if( _csync_ctx ) {
|
||||
/* Store proxy */
|
||||
QUrl proxyUrl(ownCloudInfo::instance()->webdavUrl());
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(proxyUrl);
|
||||
// We set at least one in Application
|
||||
Q_ASSERT(proxies.count() > 0);
|
||||
QNetworkProxy proxy = proxies.first();
|
||||
if (proxy.type() == QNetworkProxy::NoProxy) {
|
||||
qDebug() << "Passing NO proxy to csync for" << proxyUrl;
|
||||
} else {
|
||||
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
|
||||
<< " to csync for" << proxyUrl;
|
||||
}
|
||||
int proxyPort = proxy.port();
|
||||
|
||||
/* Store proxy */
|
||||
QUrl proxyUrl(AccountManager::instance()->account()->url());
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(QNetworkProxyQuery(proxyUrl));
|
||||
// We set at least one in Application
|
||||
Q_ASSERT(proxies.count() > 0);
|
||||
QNetworkProxy proxy = proxies.first();
|
||||
if (proxy.type() == QNetworkProxy::NoProxy) {
|
||||
qDebug() << "Passing NO proxy to csync for" << proxyUrl;
|
||||
csync_set_module_property(_csync_ctx, "proxy_type", (char*) proxyTypeToCStr(proxy.type()) );
|
||||
csync_set_module_property(_csync_ctx, "proxy_host", proxy.hostName().toUtf8().data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_port", &proxyPort );
|
||||
csync_set_module_property(_csync_ctx, "proxy_user", proxy.user().toUtf8().data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_pwd" , proxy.password().toUtf8().data() );
|
||||
|
||||
setProxyDirty(false);
|
||||
} else {
|
||||
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
|
||||
<< " to csync for" << proxyUrl;
|
||||
qDebug() << "WRN: Unable to set Proxy without csync-ctx!";
|
||||
}
|
||||
_proxy_type = proxyTypeToCStr(proxy.type());
|
||||
_proxy_host = proxy.hostName().toUtf8();
|
||||
_proxy_port = proxy.port();
|
||||
_proxy_user = proxy.user().toUtf8();
|
||||
_proxy_pwd = proxy.password().toUtf8();
|
||||
|
||||
setProxyDirty(false);
|
||||
}
|
||||
|
||||
void Folder::setProxyDirty(bool value)
|
||||
@@ -594,6 +567,7 @@ void Folder::startSync(const QStringList &pathList)
|
||||
if (!_csync_ctx) {
|
||||
// no _csync_ctx yet, initialize it.
|
||||
init();
|
||||
setProxy();
|
||||
|
||||
if (!_csync_ctx) {
|
||||
qDebug() << Q_FUNC_INFO << "init failed.";
|
||||
@@ -601,15 +575,9 @@ void Folder::startSync(const QStringList &pathList)
|
||||
QMetaObject::invokeMethod(this, "slotCSyncFinished", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
setProxy();
|
||||
} else if (proxyDirty()) {
|
||||
setProxy();
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "proxy_type", const_cast<char*>(_proxy_type) );
|
||||
csync_set_module_property(_csync_ctx, "proxy_host", _proxy_host.data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_port", &_proxy_port );
|
||||
csync_set_module_property(_csync_ctx, "proxy_user", _proxy_user.data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_pwd", _proxy_pwd.data() );
|
||||
|
||||
if (_thread && _thread->isRunning()) {
|
||||
qCritical() << "* ERROR csync is still running and new sync requested.";
|
||||
@@ -631,9 +599,10 @@ void Folder::startSync(const QStringList &pathList)
|
||||
qDebug() << "*** Start syncing";
|
||||
_thread = new QThread(this);
|
||||
setIgnoredFiles();
|
||||
_csync = new CSyncThread( _csync_ctx, path(), remoteUrl().path(), &_journal);
|
||||
_csync = new CSyncThread( _csync_ctx );
|
||||
_csync->moveToThread(_thread);
|
||||
|
||||
|
||||
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
|
||||
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
|
||||
|
||||
@@ -649,11 +618,9 @@ void Folder::startSync(const QStringList &pathList)
|
||||
connect(_csync, SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
||||
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)), Qt::BlockingQueuedConnection);
|
||||
connect(_csync, SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info)));
|
||||
connect(_csync, SIGNAL(transmissionProblem(Progress::SyncProblem)), this, SLOT(slotTransmissionProblem(Progress::SyncProblem)));
|
||||
|
||||
_thread->start();
|
||||
_thread->setPriority(QThread::LowPriority);
|
||||
|
||||
QMetaObject::invokeMethod(_csync, "startSync", Qt::QueuedConnection);
|
||||
|
||||
// disable events until syncing is done
|
||||
@@ -682,10 +649,13 @@ void Folder::slotCsyncUnavailable()
|
||||
|
||||
void Folder::slotCSyncFinished()
|
||||
{
|
||||
qDebug() << "-> CSync Finished slot with error " << _csyncError << "warn count" << _syncResult.warnCount();
|
||||
_watcher->setEventsEnabledDelayed(2000);
|
||||
_pollTimer.start();
|
||||
_timeSinceLastSync.restart();
|
||||
qDebug() << "-> CSync Finished slot for" << alias() << "with error" << _csyncError;
|
||||
if( syncEnabled() && userSyncEnabled() ) {
|
||||
qDebug() << "Sync is enabled - starting the polltimer again.";
|
||||
_watcher->setEventsEnabledDelayed(2000);
|
||||
_pollTimer.start();
|
||||
_timeSinceLastSync.restart();
|
||||
}
|
||||
|
||||
bubbleUpSyncResult();
|
||||
|
||||
@@ -707,35 +677,10 @@ void Folder::slotCSyncFinished()
|
||||
_thread->quit();
|
||||
}
|
||||
emit syncStateChange();
|
||||
ownCloudInfo::instance()->getQuotaRequest("/");
|
||||
emit syncFinished( _syncResult );
|
||||
}
|
||||
|
||||
// the problem comes without a folder and the valid path set. Add that here
|
||||
// and hand the result over to the progress dispatcher.
|
||||
void Folder::slotTransmissionProblem( const Progress::SyncProblem& problem )
|
||||
{
|
||||
Progress::SyncProblem newProb = problem;
|
||||
newProb.folder = alias();
|
||||
|
||||
if(newProb.current_file.startsWith(QLatin1String("ownclouds://")) ||
|
||||
newProb.current_file.startsWith(QLatin1String("owncloud://")) ) {
|
||||
// rip off the whole ownCloud URL.
|
||||
newProb.current_file.remove(Utility::toCSyncScheme(remoteUrl().toString()));
|
||||
}
|
||||
QString localPath = path();
|
||||
if( newProb.current_file.startsWith(localPath) ) {
|
||||
// remove the local dir.
|
||||
newProb.current_file = newProb.current_file.right( newProb.current_file.length() - localPath.length());
|
||||
}
|
||||
|
||||
// Count all error conditions.
|
||||
_syncResult.setWarnCount( _syncResult.warnCount()+1 );
|
||||
|
||||
ProgressDispatcher::instance()->setProgressProblem(alias(), newProb);
|
||||
}
|
||||
|
||||
// the progress comes without a folder and the valid path set. Add that here
|
||||
// and hand the result over to the progress dispatcher.
|
||||
void Folder::slotTransmissionProgress(const Progress::Info& progress)
|
||||
{
|
||||
Progress::Info newInfo = progress;
|
||||
@@ -744,7 +689,8 @@ void Folder::slotTransmissionProgress(const Progress::Info& progress)
|
||||
if(newInfo.current_file.startsWith(QLatin1String("ownclouds://")) ||
|
||||
newInfo.current_file.startsWith(QLatin1String("owncloud://")) ) {
|
||||
// rip off the whole ownCloud URL.
|
||||
newInfo.current_file.remove(Utility::toCSyncScheme(remoteUrl().toString()));
|
||||
QString remotePathUrl = ownCloudInfo::instance()->webdavUrl() + secondPath();
|
||||
newInfo.current_file.remove(Utility::toCSyncScheme(remotePathUrl));
|
||||
}
|
||||
QString localPath = path();
|
||||
if( newInfo.current_file.startsWith(localPath) ) {
|
||||
@@ -756,6 +702,9 @@ void Folder::slotTransmissionProgress(const Progress::Info& progress)
|
||||
if( newInfo.kind == Progress::StartSync ) {
|
||||
_syncResult.setWarnCount(0);
|
||||
}
|
||||
if( newInfo.kind == Progress::Error ) {
|
||||
_syncResult.setWarnCount( _syncResult.warnCount()+1 );
|
||||
}
|
||||
|
||||
ProgressDispatcher::instance()->setProgressInfo(alias(), newInfo);
|
||||
}
|
||||
@@ -784,65 +733,6 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *
|
||||
}
|
||||
}
|
||||
|
||||
SyncFileStatus Folder::fileStatus( const QString& fileName )
|
||||
{
|
||||
/*
|
||||
STATUS_NONE,
|
||||
+ STATUS_EVAL,
|
||||
STATUS_REMOVE, (invalid for this case because it asks for local files)
|
||||
STATUS_RENAME,
|
||||
+ STATUS_NEW,
|
||||
STATUS_CONFLICT,(probably also invalid as we know the conflict only with server involvement)
|
||||
+ STATUS_IGNORE,
|
||||
+ STATUS_SYNC,
|
||||
+ STATUS_STAT_ERROR,
|
||||
STATUS_ERROR,
|
||||
STATUS_UPDATED
|
||||
*/
|
||||
|
||||
// FIXME: Find a way for STATUS_ERROR
|
||||
SyncFileStatus stat = FILE_STATUS_NONE;
|
||||
|
||||
QString file = path() + fileName;
|
||||
QFileInfo fi(file);
|
||||
|
||||
if( !fi.exists() ) {
|
||||
stat = FILE_STATUS_STAT_ERROR; // not really possible.
|
||||
}
|
||||
|
||||
// file is ignored?
|
||||
if( fi.isSymLink() ) {
|
||||
stat = FILE_STATUS_IGNORE;
|
||||
}
|
||||
int type = CSYNC_FTW_TYPE_FILE;
|
||||
if( fi.isDir() ) {
|
||||
type = CSYNC_FTW_TYPE_DIR;
|
||||
}
|
||||
|
||||
if( stat == FILE_STATUS_NONE ) {
|
||||
CSYNC_EXCLUDE_TYPE excl = csync_excluded(_csync_ctx, file.toUtf8(), type);
|
||||
|
||||
if( excl != CSYNC_NOT_EXCLUDED ) {
|
||||
stat = FILE_STATUS_IGNORE;
|
||||
}
|
||||
}
|
||||
|
||||
SyncJournalFileRecord rec = _journal.getFileRecord(fileName);
|
||||
if( stat == FILE_STATUS_NONE && !rec.isValid() ) {
|
||||
stat = FILE_STATUS_NEW;
|
||||
}
|
||||
|
||||
// file was locally modified.
|
||||
if( stat == FILE_STATUS_NONE && fi.lastModified() != rec._modtime ) {
|
||||
stat = FILE_STATUS_EVAL;
|
||||
}
|
||||
|
||||
if( stat == FILE_STATUS_NONE ) {
|
||||
stat = FILE_STATUS_SYNC;
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/csyncthread.h"
|
||||
#include "mirall/syncjournaldb.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
@@ -41,27 +40,28 @@ namespace Mirall {
|
||||
class FolderWatcher;
|
||||
|
||||
typedef enum SyncFileStatus_s {
|
||||
FILE_STATUS_NONE,
|
||||
FILE_STATUS_EVAL,
|
||||
FILE_STATUS_REMOVE,
|
||||
FILE_STATUS_RENAME,
|
||||
FILE_STATUS_NEW,
|
||||
FILE_STATUS_CONFLICT,
|
||||
FILE_STATUS_IGNORE,
|
||||
FILE_STATUS_SYNC,
|
||||
FILE_STATUS_STAT_ERROR,
|
||||
FILE_STATUS_ERROR,
|
||||
FILE_STATUS_UPDATED
|
||||
STATUS_NONE,
|
||||
STATUS_EVAL,
|
||||
STATUS_REMOVE,
|
||||
STATUS_RENAME,
|
||||
STATUS_NEW,
|
||||
STATUS_CONFLICT,
|
||||
STATUS_IGNORE,
|
||||
STATUS_SYNC,
|
||||
STATUS_STAT_ERROR,
|
||||
STATUS_ERROR,
|
||||
STATUS_UPDATED
|
||||
} SyncFileStatus;
|
||||
|
||||
|
||||
class Folder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
protected:
|
||||
friend class FolderMan;
|
||||
Folder(const QString&, const QString&, const QString& , QObject*parent = 0L);
|
||||
|
||||
public:
|
||||
~Folder();
|
||||
|
||||
typedef QHash<QString, Folder*> Map;
|
||||
@@ -81,16 +81,10 @@ public:
|
||||
* local folder path
|
||||
*/
|
||||
QString path() const;
|
||||
|
||||
/**
|
||||
* remote folder path
|
||||
*/
|
||||
QString remotePath() const;
|
||||
|
||||
/**
|
||||
* remote folder path with server url
|
||||
*/
|
||||
QUrl remoteUrl() const;
|
||||
QString secondPath() const;
|
||||
|
||||
/**
|
||||
* local folder path with native separators
|
||||
@@ -143,10 +137,8 @@ public slots:
|
||||
|
||||
/**
|
||||
* terminate the current sync run
|
||||
*
|
||||
* If block is true, this will block synchroniously for the sync thread to finish.
|
||||
*/
|
||||
void slotTerminateSync(bool block);
|
||||
void slotTerminateSync();
|
||||
|
||||
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
|
||||
|
||||
@@ -158,17 +150,15 @@ public slots:
|
||||
*/
|
||||
void startSync(const QStringList &pathList = QStringList());
|
||||
|
||||
/**
|
||||
* Starts a sync (calling startSync)
|
||||
* if the policies allow for it
|
||||
*/
|
||||
void evaluateSync(const QStringList &pathList);
|
||||
|
||||
void setProxyDirty(bool value);
|
||||
bool proxyDirty();
|
||||
|
||||
int slotWipeBlacklist();
|
||||
int blackListEntryCount();
|
||||
/**
|
||||
* @brief slotSetSyncUserEnabled - slot that sets the enable/disable flag from the GUI
|
||||
* @param enable
|
||||
*/
|
||||
void slotSetSyncUserEnabled( bool enable );
|
||||
bool userSyncEnabled();
|
||||
|
||||
private slots:
|
||||
void slotCSyncStarted();
|
||||
@@ -177,7 +167,6 @@ private slots:
|
||||
void slotCSyncFinished();
|
||||
|
||||
void slotTransmissionProgress(const Progress::Info& progress);
|
||||
void slotTransmissionProblem( const Progress::SyncProblem& problem );
|
||||
|
||||
void slotPollTimerTimeout();
|
||||
void etagRetreived(const QString &);
|
||||
@@ -190,7 +179,7 @@ private slots:
|
||||
void slotThreadTreeWalkResult(const SyncFileItemVector& );
|
||||
void slotCatchWatcherError( const QString& );
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool init();
|
||||
|
||||
void setSyncState(SyncResult::Status state);
|
||||
@@ -201,16 +190,21 @@ private:
|
||||
|
||||
void bubbleUpSyncResult();
|
||||
|
||||
/**
|
||||
* Starts a sync (calling startSync)
|
||||
* if the policies allow for it
|
||||
*/
|
||||
void evaluateSync(const QStringList &pathList);
|
||||
|
||||
void checkLocalPath();
|
||||
|
||||
void createGuiLog( const QString& filename, const QString& verb, int count );
|
||||
|
||||
QString _path;
|
||||
QString _remotePath;
|
||||
QString _secondPath;
|
||||
QString _alias;
|
||||
QString _configFile;
|
||||
QFileSystemWatcher *_pathWatcher;
|
||||
bool _enabled;
|
||||
bool _userSyncEnabled; // enabled by user interaction?
|
||||
FolderWatcher *_watcher;
|
||||
SyncResult _syncResult;
|
||||
QThread *_thread;
|
||||
@@ -225,16 +219,8 @@ private:
|
||||
QString _lastEtag;
|
||||
QElapsedTimer _timeSinceLastSync;
|
||||
|
||||
SyncJournalDb _journal;
|
||||
|
||||
CSYNC *_csync_ctx;
|
||||
|
||||
const char *_proxy_type;
|
||||
QByteArray _proxy_host;
|
||||
int _proxy_port;
|
||||
QByteArray _proxy_user;
|
||||
QByteArray _proxy_pwd;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/inotify.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
#include <neon/ne_socket.h>
|
||||
#include "owncloudinfo.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <CoreServices/CoreServices.h>
|
||||
@@ -54,10 +53,8 @@ FolderMan::FolderMan(QObject *parent) :
|
||||
|
||||
FolderMan *FolderMan::instance()
|
||||
{
|
||||
if(!_instance) {
|
||||
if(!_instance)
|
||||
_instance = new FolderMan;
|
||||
ne_sock_init();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
@@ -65,7 +62,6 @@ FolderMan *FolderMan::instance()
|
||||
FolderMan::~FolderMan()
|
||||
{
|
||||
qDeleteAll(_folderMap);
|
||||
ne_sock_exit();
|
||||
}
|
||||
|
||||
Mirall::Folder::Map FolderMan::map()
|
||||
@@ -73,6 +69,18 @@ Mirall::Folder::Map FolderMan::map()
|
||||
return _folderMap;
|
||||
}
|
||||
|
||||
|
||||
int FolderMan::setupFolders()
|
||||
{
|
||||
// setup a handler to look for configuration changes
|
||||
return setupKnownFolders();
|
||||
}
|
||||
|
||||
void FolderMan::slotReparseConfiguration()
|
||||
{
|
||||
setupKnownFolders();
|
||||
}
|
||||
|
||||
int FolderMan::unloadAllFolders()
|
||||
{
|
||||
int cnt = 0;
|
||||
@@ -84,11 +92,10 @@ int FolderMan::unloadAllFolders()
|
||||
delete _folderMap.take( i.key() );
|
||||
cnt++;
|
||||
}
|
||||
_currentSyncFolder.clear();
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int FolderMan::setupFolders()
|
||||
int FolderMan::setupKnownFolders()
|
||||
{
|
||||
qDebug() << "* Setup folders from " << _folderConfigPath;
|
||||
|
||||
@@ -218,7 +225,7 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
QSettings settings( _folderConfigPath + QLatin1Char('/') + escapedAlias, QSettings::IniFormat);
|
||||
QSettings settings( cfgFile.filePath(), QSettings::IniFormat);
|
||||
qDebug() << " -> file path: " << settings.fileName();
|
||||
|
||||
// Check if the filename is equal to the group setting. If not, use the group
|
||||
@@ -231,10 +238,9 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
|
||||
settings.beginGroup( escapedAlias ); // read the group with the same name as the file which is the folder alias
|
||||
|
||||
QString path = settings.value(QLatin1String("localPath")).toString();
|
||||
QString path = settings.value(QLatin1String("localpath")).toString();
|
||||
QString backend = settings.value(QLatin1String("backend")).toString();
|
||||
QString targetPath = settings.value( QLatin1String("targetPath")).toString();
|
||||
bool paused = settings.value( QLatin1String("paused"), false).toBool();
|
||||
QString targetPath = settings.value( QLatin1String("targetPath") ).toString();
|
||||
// QString connection = settings.value( QLatin1String("connection") ).toString();
|
||||
QString alias = unescapeAlias( escapedAlias );
|
||||
|
||||
@@ -252,9 +258,6 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
folder->setConfigFile(file);
|
||||
qDebug() << "Adding folder to Folder Map " << folder;
|
||||
_folderMap[alias] = folder;
|
||||
if (paused) {
|
||||
_disabledFolders.insert(folder);
|
||||
}
|
||||
|
||||
/* Use a signal mapper to connect the signals to the alias */
|
||||
connect(folder, SIGNAL(scheduleToSync(const QString&)), SLOT(slotScheduleSync(const QString&)));
|
||||
@@ -266,7 +269,7 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
void FolderMan::slotEnableFolder( const QString& alias, bool enable )
|
||||
void FolderMan::slotGuiPauseFolder( const QString& alias, bool enable )
|
||||
{
|
||||
if( ! _folderMap.contains( alias ) ) {
|
||||
qDebug() << "!! Can not enable alias " << alias << ", can not be found in folderMap.";
|
||||
@@ -275,18 +278,8 @@ void FolderMan::slotEnableFolder( const QString& alias, bool enable )
|
||||
|
||||
Folder *f = _folderMap[alias];
|
||||
if( f ) {
|
||||
f->setSyncEnabled(enable);
|
||||
f->slotSetSyncUserEnabled(enable);
|
||||
f->evaluateSync(QStringList());
|
||||
|
||||
QSettings settings(_folderConfigPath + QLatin1Char('/') + f->configFile(), QSettings::IniFormat);
|
||||
settings.beginGroup(escapeAlias(f->alias()));
|
||||
if (enable) {
|
||||
settings.remove("paused");
|
||||
_disabledFolders.remove(f);
|
||||
} else {
|
||||
settings.setValue("paused", true);
|
||||
_disabledFolders.insert(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +294,8 @@ void FolderMan::terminateSyncProcess( const QString& alias )
|
||||
if( ! folderAlias.isEmpty() ) {
|
||||
Folder *f = _folderMap[folderAlias];
|
||||
if( f ) {
|
||||
f->slotTerminateSync(true);
|
||||
f->slotTerminateSync();
|
||||
|
||||
if(_currentSyncFolder == folderAlias )
|
||||
_currentSyncFolder.clear();
|
||||
}
|
||||
@@ -348,7 +342,7 @@ void FolderMan::slotScheduleSync( const QString& alias )
|
||||
|
||||
qDebug() << "Schedule folder " << alias << " to sync!";
|
||||
if( _currentSyncFolder == alias ) {
|
||||
qDebug() << " the current folder is currently syncing.";
|
||||
// the current folder is currently syncing.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -369,7 +363,7 @@ void FolderMan::setSyncEnabled( bool enabled )
|
||||
_syncEnabled = enabled;
|
||||
|
||||
foreach( Folder *f, _folderMap.values() ) {
|
||||
f->setSyncEnabled(enabled && !_disabledFolders.contains(f));
|
||||
f->setSyncEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,6 +388,7 @@ void FolderMan::slotScheduleFolderSync()
|
||||
if( ! _scheduleQueue.isEmpty() ) {
|
||||
const QString alias = _scheduleQueue.dequeue();
|
||||
if( _folderMap.contains( alias ) ) {
|
||||
ownCloudInfo::instance()->getQuotaRequest("/");
|
||||
Folder *f = _folderMap[alias];
|
||||
if( f->syncEnabled() ) {
|
||||
_currentSyncFolder = alias;
|
||||
@@ -434,23 +429,6 @@ void FolderMan::addFolderDefinition(const QString& alias, const QString& sourceF
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
Folder *FolderMan::folderForPath(const QUrl &path)
|
||||
{
|
||||
QString absolutePath = path.toLocalFile();
|
||||
absolutePath.append("/");
|
||||
|
||||
foreach(Folder* folder, map().values())
|
||||
{
|
||||
if(absolutePath.startsWith(folder->path()))
|
||||
{
|
||||
qDebug() << "found folder: " << folder->path() << " for " << absolutePath;
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FolderMan::removeAllFolderDefinitions()
|
||||
{
|
||||
foreach( Folder *f, _folderMap.values() ) {
|
||||
@@ -575,7 +553,7 @@ SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
|
||||
case SyncResult::Unavailable:
|
||||
overallResult.setStatus( SyncResult::Unavailable );
|
||||
break;
|
||||
case SyncResult::Problem: // don't show the problem icon in tray.
|
||||
case SyncResult::Problem:
|
||||
case SyncResult::Success:
|
||||
if( overallResult.status() == SyncResult::Undefined )
|
||||
overallResult.setStatus( SyncResult::Success );
|
||||
@@ -587,9 +565,8 @@ SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
|
||||
if ( overallResult.status() != SyncResult::Error )
|
||||
overallResult.setStatus( SyncResult::SetupError );
|
||||
break;
|
||||
case SyncResult::SyncAbortRequested:
|
||||
break;
|
||||
// no default case on purpose, check compiler warnings
|
||||
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
}
|
||||
return overallResult;
|
||||
@@ -625,9 +602,6 @@ QString FolderMan::statusToString( SyncResult syncStatus, bool enabled ) const
|
||||
case SyncResult::SetupError:
|
||||
folderMessage = tr( "Setup Error." );
|
||||
break;
|
||||
case SyncResult::SyncAbortRequested:
|
||||
folderMessage = tr( "User Abort." );
|
||||
break;
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
if( !enabled ) {
|
||||
|
||||
@@ -50,9 +50,6 @@ public:
|
||||
*/
|
||||
void addFolderDefinition(const QString&, const QString&, const QString& );
|
||||
|
||||
/** Returns the folder which the file or directory stored in path is in */
|
||||
Folder* folderForPath(const QUrl& path);
|
||||
|
||||
/** Returns the folder by alias or NULL if no folder with the alias exists. */
|
||||
Folder *folder( const QString& );
|
||||
|
||||
@@ -94,11 +91,13 @@ signals:
|
||||
|
||||
public slots:
|
||||
void slotRemoveFolder( const QString& );
|
||||
void slotEnableFolder( const QString&, bool );
|
||||
void slotGuiPauseFolder( const QString&, bool );
|
||||
|
||||
void slotFolderSyncStarted();
|
||||
void slotFolderSyncFinished( const SyncResult& );
|
||||
|
||||
void slotReparseConfiguration();
|
||||
|
||||
void terminateSyncProcess( const QString& alias = QString::null );
|
||||
|
||||
/* delete all folder objects */
|
||||
@@ -122,6 +121,7 @@ private slots:
|
||||
private:
|
||||
// finds all folder configuration files
|
||||
// and create the folders
|
||||
int setupKnownFolders();
|
||||
void terminateCurrentSync();
|
||||
QString getBackupName( const QString& ) const;
|
||||
|
||||
@@ -132,7 +132,6 @@ private:
|
||||
|
||||
void removeFolder( const QString& );
|
||||
|
||||
QSet<Folder*> _disabledFolders;
|
||||
Folder::Map _folderMap;
|
||||
QString _folderConfigPath;
|
||||
QSignalMapper *_folderChangeSignalMapper;
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <QtWidgets>
|
||||
#endif
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
|
||||
#include "mirall/folderwizard.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
@@ -36,24 +35,8 @@
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
QString FormatWarningsWizardPage::formatWarnings(const QStringList &warnings) const
|
||||
{
|
||||
QString ret;
|
||||
if (warnings.count() == 1) {
|
||||
ret = tr("<b>Warning:</b> ") + warnings.first();
|
||||
} else if (warnings.count() > 1) {
|
||||
ret = tr("<b>Warning:</b> ") + "<ul>";
|
||||
Q_FOREACH(QString warning, warnings) {
|
||||
ret += QString::fromLatin1("<li>%1</li>").arg(warning);
|
||||
}
|
||||
ret += "</ul>";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FolderWizardSourcePage::FolderWizardSourcePage()
|
||||
: FormatWarningsWizardPage()
|
||||
: QWizardPage()
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
registerField(QLatin1String("sourceFolder*"), _ui.localFolderLineEdit);
|
||||
@@ -61,7 +44,7 @@ FolderWizardSourcePage::FolderWizardSourcePage()
|
||||
_ui.localFolderLineEdit->setText( QDir::toNativeSeparators( defaultPath ) );
|
||||
registerField(QLatin1String("alias*"), _ui.aliasLineEdit);
|
||||
_ui.aliasLineEdit->setText( Theme::instance()->appNameGUI() );
|
||||
_ui.warnLabel->setTextFormat(Qt::RichText);
|
||||
|
||||
_ui.warnLabel->hide();
|
||||
}
|
||||
|
||||
@@ -85,16 +68,16 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
QFileInfo selFile( QDir::fromNativeSeparators(_ui.localFolderLineEdit->text()) );
|
||||
QString userInput = selFile.canonicalFilePath();
|
||||
|
||||
QStringList warnStrings;
|
||||
QString warnString;
|
||||
|
||||
bool isOk = selFile.isDir();
|
||||
if( !isOk ) {
|
||||
warnStrings.append(tr("No valid local folder selected!"));
|
||||
warnString = tr("No local folder selected!");
|
||||
}
|
||||
|
||||
if (isOk && !selFile.isWritable()) {
|
||||
isOk = false;
|
||||
warnStrings.append(tr("You have no permission to write to the selected folder!"));
|
||||
warnString += tr("You have no permission to write to the selected folder!");
|
||||
}
|
||||
|
||||
// check if the local directory isn't used yet in another ownCloud sync
|
||||
@@ -115,40 +98,21 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
if( ! folderDir.endsWith(QLatin1Char('/')) ) folderDir.append(QLatin1Char('/'));
|
||||
|
||||
qDebug() << "Checking local path: " << folderDir << " <-> " << userInput;
|
||||
if( QDir::cleanPath(f->path()) == QDir::cleanPath(userInput) &&
|
||||
QDir::cleanPath(QDir(f->path()).canonicalPath()) == QDir(userInput).canonicalPath() ) {
|
||||
if( QFileInfo( f->path() ) == userInput ) {
|
||||
isOk = false;
|
||||
warnStrings.append( tr("The local path %1 is already an upload folder. Please pick another one!")
|
||||
warnString.append( tr("The local path %1 is already an upload folder.<br/>Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(userInput)) );
|
||||
}
|
||||
if( isOk && QDir::cleanPath(folderDir).startsWith(QDir::cleanPath(userInput)+'/') ) {
|
||||
if( isOk && folderDir.startsWith( userInput )) {
|
||||
qDebug() << "A already configured folder is child of the current selected";
|
||||
warnStrings.append( tr("An already configured folder is contained in the current entry."));
|
||||
warnString.append( tr("An already configured folder is contained in the current entry."));
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
QString absCleanUserFolder = QDir::cleanPath(QDir(userInput).canonicalPath())+'/';
|
||||
if( isOk && QDir::cleanPath(folderDir).startsWith(absCleanUserFolder) ) {
|
||||
qDebug() << "A already configured folder is child of the current selected";
|
||||
warnStrings.append( tr("The selected folder is a symbolic link. An already configured"
|
||||
"folder is contained in the folder this link is pointing to."));
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
if( isOk && QDir::cleanPath(QString(userInput+'/')).startsWith( QDir::cleanPath(folderDir)) ) {
|
||||
if( isOk && userInput.startsWith( folderDir ) ) {
|
||||
qDebug() << "An already configured folder is parent of the current selected";
|
||||
warnStrings.append( tr("An already configured folder contains the currently entered folder."));
|
||||
warnString.append( tr("An already configured folder contains the currently entered directory."));
|
||||
isOk = false;
|
||||
}
|
||||
if( isOk && absCleanUserFolder.startsWith( QDir::cleanPath(folderDir)) ) {
|
||||
qDebug() << "The selected folder is a symbolic link. An already configured folder is\n"
|
||||
"the parent of the current selected contains the folder this link is pointing to.";
|
||||
warnStrings.append( tr("The selected folder is a symbolic link. An already configured folder "
|
||||
"is the parent of the current selected contains the folder this link is "
|
||||
"pointing to."));
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -156,7 +120,7 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
// check if the alias is unique.
|
||||
QString alias = _ui.aliasLineEdit->text();
|
||||
if( alias.isEmpty() ) {
|
||||
warnStrings.append( tr("The alias can not be empty. Please provide a descriptive alias word.") );
|
||||
warnString.append( tr("The alias can not be empty. Please provide a descriptive alias word.") );
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
@@ -167,7 +131,7 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
qDebug() << "Checking local alias: " << f->alias();
|
||||
if( f ) {
|
||||
if( f->alias() == alias ) {
|
||||
warnStrings.append( tr("The alias <i>%1</i> is already in use. Please pick another alias.").arg(alias) );
|
||||
warnString.append( tr("<br/>The alias <i>%1</i> is already in use. Please pick another alias.").arg(alias) );
|
||||
isOk = false;
|
||||
goon = false;
|
||||
}
|
||||
@@ -175,14 +139,12 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
i++;
|
||||
}
|
||||
|
||||
_ui.warnLabel->setWordWrap(true);
|
||||
if( isOk ) {
|
||||
_ui.warnLabel->hide();
|
||||
_ui.warnLabel->setText( QString::null );
|
||||
} else {
|
||||
_ui.warnLabel->show();
|
||||
QString warnings = formatWarnings(warnStrings);
|
||||
_ui.warnLabel->setText( warnings );
|
||||
_ui.warnLabel->setText( warnString );
|
||||
}
|
||||
return isOk;
|
||||
}
|
||||
@@ -205,8 +167,7 @@ void FolderWizardSourcePage::on_localFolderLineEdit_textChanged()
|
||||
|
||||
// =================================================================================
|
||||
FolderWizardTargetPage::FolderWizardTargetPage()
|
||||
: FormatWarningsWizardPage()
|
||||
,_warnWasVisible(false)
|
||||
: _warnWasVisible(false)
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
_ui.warnFrame->hide();
|
||||
@@ -215,8 +176,6 @@ FolderWizardTargetPage::FolderWizardTargetPage()
|
||||
connect(_ui.refreshButton, SIGNAL(clicked()), SLOT(slotRefreshFolders()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotAddRemoteFolder()
|
||||
@@ -237,32 +196,24 @@ void FolderWizardTargetPage::slotAddRemoteFolder()
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolder(const QString &folder)
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolder(QString folder)
|
||||
{
|
||||
if( folder.isEmpty() ) return;
|
||||
|
||||
MkColJob *job = new MkColJob(AccountManager::instance()->account(), folder, this);
|
||||
/* check the owncloud configuration file and query the ownCloud */
|
||||
connect(job, SIGNAL(finished(QNetworkReply::NetworkError)),
|
||||
SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotHandleNetworkError(QNetworkReply*)));
|
||||
job->start();
|
||||
ownCloudInfo::instance()->mkdirRequest( folder );
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error)
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolderFinished( QNetworkReply::NetworkError error )
|
||||
{
|
||||
if (error == QNetworkReply::NoError) {
|
||||
qDebug() << "** webdav mkdir request finished";
|
||||
showWarn(tr("Folder was successfully created on %1.").arg(Theme::instance()->appNameGUI()));
|
||||
slotRefreshFolders();
|
||||
}
|
||||
}
|
||||
qDebug() << "** webdav mkdir request finished " << error;
|
||||
|
||||
void FolderWizardTargetPage::slotHandleNetworkError(QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << "** webdav mkdir request failed:" << reply->error();
|
||||
showWarn(tr("Failed to create the folder on %1. Please check manually.")
|
||||
.arg(Theme::instance()->appNameGUI()));
|
||||
// the webDAV server seems to return a 202 even if mkdir was successful.
|
||||
if( error == QNetworkReply::NoError ||
|
||||
error == QNetworkReply::ContentOperationNotPermittedError) {
|
||||
showWarn( tr("Folder was successfully created on %1.").arg( Theme::instance()->appNameGUI() ) );
|
||||
slotRefreshFolders();
|
||||
} else {
|
||||
showWarn( tr("Failed to create the folder on %1.<br/>Please check manually.").arg( Theme::instance()->appNameGUI() ) );
|
||||
}
|
||||
}
|
||||
|
||||
static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& text)
|
||||
@@ -276,11 +227,11 @@ static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& t
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path)
|
||||
static void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
if (pathTrail.size() == 0) {
|
||||
if (pathTrail.size() == 0) {
|
||||
if (path.endsWith('/')) {
|
||||
path.chop(1);
|
||||
}
|
||||
@@ -301,9 +252,9 @@ void FolderWizardTargetPage::recursiveInsert(QTreeWidgetItem *parent, QStringLis
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotUpdateDirectories(const QStringList &list)
|
||||
void FolderWizardTargetPage::slotUpdateDirectories(QStringList list)
|
||||
{
|
||||
QString webdavFolder = QUrl(AccountManager::instance()->account()->davUrl()).path();
|
||||
QString webdavFolder = QUrl(ownCloudInfo::instance()->webdavUrl()).path();
|
||||
|
||||
QTreeWidgetItem *root = _ui.folderTreeWidget->topLevelItem(0);
|
||||
if (!root) {
|
||||
@@ -324,20 +275,13 @@ void FolderWizardTargetPage::slotUpdateDirectories(const QStringList &list)
|
||||
|
||||
void FolderWizardTargetPage::slotRefreshFolders()
|
||||
{
|
||||
LsColJob *job = new LsColJob(AccountManager::instance()->account(), "/", this);
|
||||
connect(job, SIGNAL(directoryListing(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
job->start();
|
||||
ownCloudInfo::instance()->getDirectoryListing("/");
|
||||
_ui.folderTreeWidget->clear();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotItemExpanded(QTreeWidgetItem *item)
|
||||
{
|
||||
QString dir = item->data(0, Qt::UserRole).toString();
|
||||
LsColJob *job = new LsColJob(AccountManager::instance()->account(), dir, this);
|
||||
connect(job, SIGNAL(directoryListing(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
job->start();
|
||||
ownCloudInfo::instance()->getDirectoryListing(item->data(0, Qt::UserRole).toString());
|
||||
}
|
||||
|
||||
FolderWizardTargetPage::~FolderWizardTargetPage()
|
||||
@@ -349,36 +293,32 @@ bool FolderWizardTargetPage::isComplete() const
|
||||
if (!_ui.folderTreeWidget->currentItem())
|
||||
return false;
|
||||
|
||||
QStringList warnStrings;
|
||||
QString dir = _ui.folderTreeWidget->currentItem()->data(0, Qt::UserRole).toString();
|
||||
if (!dir.startsWith(QLatin1Char('/'))) {
|
||||
dir.prepend(QLatin1Char('/'));
|
||||
}
|
||||
wizard()->setProperty("targetPath", dir);
|
||||
|
||||
Folder::Map map = _folderMap;
|
||||
Folder::Map::const_iterator i = map.constBegin();
|
||||
for(i = map.constBegin();i != map.constEnd(); i++ ) {
|
||||
Folder *f = static_cast<Folder*>(i.value());
|
||||
QString curDir = f->remotePath();
|
||||
if (!curDir.startsWith(QLatin1Char('/'))) {
|
||||
curDir.prepend(QLatin1Char('/'));
|
||||
}
|
||||
if (QDir::cleanPath(dir) == QDir::cleanPath(curDir)) {
|
||||
warnStrings.append(tr("This folder is already being synced."));
|
||||
} else if (dir.startsWith(curDir + QLatin1Char('/'))) {
|
||||
warnStrings.append(tr("You are already syncing <i>%1</i>, which is a parent folder of <i>%2</i>.").arg(curDir).arg(dir));
|
||||
}
|
||||
|
||||
if (curDir == QLatin1String("/")) {
|
||||
warnStrings.append(tr("You are already syncing all your files. Syncing another folder is <b>not</b> supported. "
|
||||
"If you want to sync multiple folders, please remove the currently configured "
|
||||
"root folder sync."));
|
||||
QString curDir = f->secondPath();
|
||||
if (dir == curDir) {
|
||||
showWarn( tr("This directory is already being synced.") );
|
||||
return false;
|
||||
} else if (dir.startsWith(curDir)) {
|
||||
if (dir.isEmpty()) dir = QLatin1Char('/');
|
||||
if (curDir.isEmpty()) curDir = QLatin1Char('/');
|
||||
showWarn( tr("You are already syncing <i>%1</i>, which is a parent folder of <i>%2</i>.").arg(curDir).arg(dir) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
showWarn(formatWarnings(warnStrings));
|
||||
return warnStrings.isEmpty();
|
||||
if( dir == QLatin1String("/") ) {
|
||||
showWarn( tr("If you sync the root folder, you can <b>not</b> configure another sync directory."));
|
||||
return true;
|
||||
} else {
|
||||
showWarn();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::cleanupPage()
|
||||
@@ -389,7 +329,21 @@ void FolderWizardTargetPage::cleanupPage()
|
||||
void FolderWizardTargetPage::initializePage()
|
||||
{
|
||||
showWarn();
|
||||
slotRefreshFolders();
|
||||
|
||||
/* check the owncloud configuration file and query the ownCloud */
|
||||
ownCloudInfo *ocInfo = ownCloudInfo::instance();
|
||||
if( ocInfo->isConfigured() ) {
|
||||
connect( ocInfo, SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
SLOT(slotDirCheckReply(QString,QNetworkReply*)));
|
||||
connect( ocInfo, SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
SLOT(slotCreateRemoteFolderFinished( QNetworkReply::NetworkError )));
|
||||
connect( ocInfo, SIGNAL(directoryListingUpdated(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)),
|
||||
SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
|
||||
slotRefreshFolders();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::showWarn( const QString& msg ) const
|
||||
@@ -414,7 +368,6 @@ FolderWizard::FolderWizard( QWidget *parent )
|
||||
_folderWizardSourcePage(new FolderWizardSourcePage),
|
||||
_folderWizardTargetPage(0)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setPage(Page_Source, _folderWizardSourcePage );
|
||||
if (!Theme::instance()->singleSyncFolder()) {
|
||||
_folderWizardTargetPage = new FolderWizardTargetPage();
|
||||
|
||||
@@ -28,15 +28,10 @@ namespace Mirall {
|
||||
|
||||
class ownCloudInfo;
|
||||
|
||||
class FormatWarningsWizardPage : public QWizardPage {
|
||||
protected:
|
||||
QString formatWarnings(const QStringList &warnings) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* page to ask for the local source folder
|
||||
*/
|
||||
class FolderWizardSourcePage : public FormatWarningsWizardPage
|
||||
class FolderWizardSourcePage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -62,7 +57,7 @@ private:
|
||||
* page to ask for the target folder
|
||||
*/
|
||||
|
||||
class FolderWizardTargetPage : public FormatWarningsWizardPage
|
||||
class FolderWizardTargetPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -80,14 +75,12 @@ protected slots:
|
||||
|
||||
void showWarn( const QString& = QString() ) const;
|
||||
void slotAddRemoteFolder();
|
||||
void slotCreateRemoteFolder(const QString&);
|
||||
void slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error);
|
||||
void slotHandleNetworkError(QNetworkReply*);
|
||||
void slotUpdateDirectories(const QStringList&);
|
||||
void slotCreateRemoteFolder(QString);
|
||||
void slotCreateRemoteFolderFinished( QNetworkReply::NetworkError error );
|
||||
void slotUpdateDirectories(QStringList);
|
||||
void slotRefreshFolders();
|
||||
void slotItemExpanded(QTreeWidgetItem*);
|
||||
private:
|
||||
void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path);
|
||||
Ui_FolderWizardTargetPage _ui;
|
||||
ownCloudInfo *_ownCloudDirCheck;
|
||||
bool _dirChecked;
|
||||
|
||||
@@ -145,6 +145,9 @@
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
|
||||
@@ -109,6 +109,9 @@
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="generalGroupBox">
|
||||
<property name="title">
|
||||
<string>General Setttings</string>
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
@@ -77,4 +77,7 @@
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="proxyButtonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <QDir>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QColorGroup>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
|
||||
@@ -29,7 +30,6 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::IgnoreListEditor)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->descriptionLabel->setText(tr("Files or directories matching a pattern will not be synchronized.\n\n"
|
||||
|
||||
337
src/mirall/itemprogressdialog.cpp
Normal file
337
src/mirall/itemprogressdialog.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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 <QtGui>
|
||||
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/syncfileitem.h"
|
||||
|
||||
#include "ui_itemprogressdialog.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
ItemProgressDialog::ItemProgressDialog(Application*, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ErrorIndicatorRole( Qt::UserRole +1 ),
|
||||
_ui(new Ui::ItemProgressDialog)
|
||||
{
|
||||
_ui->setupUi(this);
|
||||
connect(_ui->_dialogButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()),
|
||||
this, SLOT(accept()));
|
||||
|
||||
connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString,Progress::Info)),
|
||||
this, SLOT(slotProgressInfo(QString,Progress::Info)));
|
||||
connect(ProgressDispatcher::instance(), SIGNAL(progressSyncProblem(const QString&,const Progress::SyncProblem&)),
|
||||
this, SLOT(slotProgressErrors(const QString&, const Progress::SyncProblem&)));
|
||||
|
||||
// Adjust copyToClipboard() when making changes here!
|
||||
QStringList header;
|
||||
header << tr("Time");
|
||||
header << tr("File");
|
||||
header << tr("Folder");
|
||||
header << tr("Action");
|
||||
header << tr("Size");
|
||||
|
||||
_ui->_treeWidget->setHeaderLabels( header );
|
||||
_ui->_treeWidget->setColumnWidth(1, 180);
|
||||
_ui->_treeWidget->setColumnCount(5);
|
||||
_ui->_treeWidget->setRootIsDecorated(false);
|
||||
|
||||
connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
|
||||
|
||||
QPushButton *copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
|
||||
connect(copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
|
||||
|
||||
setWindowTitle(tr("Sync Protocol"));
|
||||
|
||||
}
|
||||
|
||||
void ItemProgressDialog::setSyncResultStatus(const SyncResult& result )
|
||||
{
|
||||
if( result.errorStrings().count() ) {
|
||||
_ui->_errorLabel->setVisible(true);
|
||||
_ui->_errorLabel->setTextFormat(Qt::RichText);
|
||||
|
||||
QString errStr;
|
||||
QStringList errors = result.errorStrings();
|
||||
int cnt = errors.size();
|
||||
bool appendDots = false;
|
||||
if( cnt > 3 ) {
|
||||
cnt = 3;
|
||||
appendDots = true;
|
||||
}
|
||||
|
||||
for( int i = 0; i < cnt; i++) {
|
||||
errStr.append(QString("%1<br/>").arg(errors.at(i)));
|
||||
}
|
||||
if( appendDots ) {
|
||||
errStr.append(QString("..."));
|
||||
}
|
||||
_ui->_errorLabel->setText(errStr);
|
||||
} else {
|
||||
_ui->_errorLabel->setText(QString::null);
|
||||
_ui->_errorLabel->setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ItemProgressDialog::setSyncResult( const SyncResult& result )
|
||||
{
|
||||
setSyncResultStatus(result);
|
||||
|
||||
const QString& folder = result.folder();
|
||||
qDebug() << "Setting sync result for folder " << folder;
|
||||
|
||||
SyncFileItemVector::const_iterator i;
|
||||
const SyncFileItemVector& items = result.syncFileItemVector();
|
||||
QDateTime dt = QDateTime::currentDateTime();
|
||||
|
||||
for (i = items.begin(); i != items.end(); ++i) {
|
||||
const SyncFileItem& item = *i;
|
||||
QString errMsg;
|
||||
QString tooltip;
|
||||
// handle ignored files here.
|
||||
|
||||
if( item._instruction == CSYNC_INSTRUCTION_IGNORE
|
||||
|| item._instruction == CSYNC_INSTRUCTION_CONFLICT ) {
|
||||
QStringList columns;
|
||||
QString timeStr = timeString(dt);
|
||||
QString longTimeStr = timeString(dt, QLocale::LongFormat);
|
||||
|
||||
columns << timeStr;
|
||||
columns << item._file;
|
||||
columns << folder;
|
||||
if( item._instruction == CSYNC_INSTRUCTION_IGNORE) {
|
||||
if( item._type == SyncFileItem::SoftLink ) {
|
||||
errMsg = tr("Soft Link ignored");
|
||||
tooltip = tr("Softlinks break the semantics of synchronization.\nPlease do not "
|
||||
"use them in synced directories");
|
||||
} else {
|
||||
QString obj = tr("file");
|
||||
if( item._type == SyncFileItem::Directory ) {
|
||||
obj = tr("directory");
|
||||
}
|
||||
tooltip = tr("The %1 was ignored because it is listed in the clients ignore list\n"
|
||||
"or the %1 name contains characters that are not syncable\nin a cross platform "
|
||||
"environment").arg(obj);
|
||||
errMsg = tr("Item ignored");
|
||||
if( item._errorString == QLatin1String("File listed on ignore list.") ) {
|
||||
errMsg = tr("%1 on ignore list").arg(obj);
|
||||
tooltip = tr("The %1 was skipped because it is listed on the clients\n"
|
||||
"list of names to ignore").arg(obj);
|
||||
} else if( item._errorString == QLatin1String("File contains invalid characters.") ) {
|
||||
errMsg = tr("Invalid characters");
|
||||
tooltip = tr("The %1 name contains one or more invalid characters which break\n"
|
||||
"syncing in a cross platform environment").arg(obj);
|
||||
}
|
||||
}
|
||||
} else if( item._instruction == CSYNC_INSTRUCTION_CONFLICT ) {
|
||||
errMsg = tr("Conflict file.");
|
||||
tooltip = tr("The file was changed on server and local repository and as a result it\n"
|
||||
"created a so called conflict. The local change is copied to the conflict\n"
|
||||
"file while the file from the server side is available under the original\n"
|
||||
"name");
|
||||
} else {
|
||||
Q_ASSERT(!"unhandled instruction.");
|
||||
}
|
||||
columns << errMsg;
|
||||
|
||||
QTreeWidgetItem *twitem = new QTreeWidgetItem(columns);
|
||||
twitem->setData(0, ErrorIndicatorRole, QVariant(true) );
|
||||
twitem->setToolTip(0, longTimeStr);
|
||||
twitem->setToolTip(3, tooltip);
|
||||
twitem->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Problem, true));
|
||||
_ui->_treeWidget->insertTopLevelItem(0, twitem);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ItemProgressDialog::setupList()
|
||||
{
|
||||
// get the folders to set up the top level list.
|
||||
Folder::Map map = FolderMan::instance()->map();
|
||||
SyncResult lastResult;
|
||||
QDateTime dt;
|
||||
bool haveSyncResult = false;
|
||||
|
||||
foreach( Folder *f, map.values() ) {
|
||||
if( f->syncResult().syncTime() > dt ) {
|
||||
dt = f->syncResult().syncTime();
|
||||
lastResult = f->syncResult();
|
||||
haveSyncResult = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( haveSyncResult ) {
|
||||
setSyncResult(lastResult);
|
||||
}
|
||||
|
||||
QList<Progress::Info> progressList = ProgressDispatcher::instance()->recentChangedItems(0); // All.
|
||||
|
||||
QHash <QString, int> folderHash;
|
||||
|
||||
foreach( Progress::Info info, progressList ) {
|
||||
slotProgressInfo( info.folder, info );
|
||||
folderHash[info.folder] = 1;
|
||||
}
|
||||
|
||||
QList<Progress::SyncProblem> problemList = ProgressDispatcher::instance()->recentProblems(0);
|
||||
foreach( Progress::SyncProblem prob, problemList ) {
|
||||
slotProgressErrors(prob.folder, prob);
|
||||
folderHash[prob.folder] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ItemProgressDialog::~ItemProgressDialog()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void ItemProgressDialog::copyToClipboard()
|
||||
{
|
||||
QString text;
|
||||
QTextStream ts(&text);
|
||||
|
||||
int topLevelItems = _ui->_treeWidget->topLevelItemCount();
|
||||
for (int i = 0; i < topLevelItems; i++) {
|
||||
QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i);
|
||||
ts << left
|
||||
// time stamp
|
||||
<< qSetFieldWidth(10)
|
||||
<< child->data(0,Qt::DisplayRole).toString()
|
||||
// file name
|
||||
<< qSetFieldWidth(64)
|
||||
<< child->data(1,Qt::DisplayRole).toString()
|
||||
// folder
|
||||
<< qSetFieldWidth(15)
|
||||
<< child->data(2, Qt::DisplayRole).toString()
|
||||
// action
|
||||
<< qSetFieldWidth(15)
|
||||
<< child->data(3, Qt::DisplayRole).toString()
|
||||
// size
|
||||
<< qSetFieldWidth(10)
|
||||
<< child->data(4, Qt::DisplayRole).toString()
|
||||
<< qSetFieldWidth(0)
|
||||
<< endl;
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setText(text);
|
||||
emit guiLog(tr("Copied to clipboard"), tr("The sync protocol has been copied to the clipboard."));
|
||||
}
|
||||
|
||||
void ItemProgressDialog::accept()
|
||||
{
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ItemProgressDialog::cleanErrors( const QString& /* folder */ ) // FIXME: Use the folder to detect which errors can be deleted.
|
||||
{
|
||||
_problemCounter = 0;
|
||||
QList<QTreeWidgetItem*> wipeList;
|
||||
|
||||
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
||||
|
||||
for( int cnt = 0; cnt < itemCnt; cnt++ ) {
|
||||
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt);
|
||||
bool isErrorItem = item->data(0, ErrorIndicatorRole).toBool();
|
||||
if( isErrorItem ) {
|
||||
wipeList.append(item);
|
||||
}
|
||||
}
|
||||
qDeleteAll(wipeList.begin(), wipeList.end());
|
||||
}
|
||||
|
||||
QString ItemProgressDialog::timeString(QDateTime dt, QLocale::FormatType format) const
|
||||
{
|
||||
QLocale loc = QLocale::system();
|
||||
QString timeStr;
|
||||
QDate today = QDate::currentDate();
|
||||
|
||||
if( format == QLocale::NarrowFormat ) {
|
||||
if( dt.date().day() == today.day() ) {
|
||||
timeStr = loc.toString(dt.time(), QLocale::NarrowFormat);
|
||||
} else {
|
||||
timeStr = loc.toString(dt, QLocale::NarrowFormat);
|
||||
}
|
||||
} else {
|
||||
timeStr = loc.toString(dt, format);
|
||||
}
|
||||
return timeStr;
|
||||
}
|
||||
|
||||
void ItemProgressDialog::slotProgressErrors( const QString& folder, const Progress::SyncProblem& problem )
|
||||
{
|
||||
QStringList columns;
|
||||
QString timeStr = timeString(problem.timestamp);
|
||||
QString longTimeStr = timeString(problem.timestamp, QLocale::LongFormat);
|
||||
|
||||
columns << timeStr;
|
||||
columns << problem.current_file;
|
||||
columns << folder;
|
||||
QString errMsg = tr("Problem: %1").arg(problem.error_message);
|
||||
#if 0
|
||||
if( problem.error_code == 507 ) {
|
||||
errMsg = tr("No more storage space available on server.");
|
||||
}
|
||||
#endif
|
||||
columns << errMsg;
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(columns);
|
||||
item->setData(0, ErrorIndicatorRole, QVariant(true) );
|
||||
// Maybe we should not set the error icon for all problems but distinguish
|
||||
// by error_code. A quota problem is considered an error, others might not??
|
||||
item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Error, true));
|
||||
item->setToolTip(0, longTimeStr);
|
||||
_ui->_treeWidget->insertTopLevelItem(0, item);
|
||||
Q_UNUSED(item);
|
||||
}
|
||||
|
||||
void ItemProgressDialog::slotProgressInfo( const QString& folder, const Progress::Info& progress )
|
||||
{
|
||||
if( progress.kind == Progress::StartSync ) {
|
||||
cleanErrors( folder );
|
||||
}
|
||||
|
||||
if( progress.kind == Progress::EndSync ) {
|
||||
// decorateFolderItem( folder );
|
||||
}
|
||||
|
||||
// Ingore other events than finishing an individual up- or download.
|
||||
if( !(progress.kind == Progress::EndDownload || progress.kind == Progress::EndUpload || progress.kind == Progress::EndDelete)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList columns;
|
||||
QString timeStr = timeString(progress.timestamp);
|
||||
QString longTimeStr = timeString(progress.timestamp, QLocale::LongFormat);
|
||||
|
||||
columns << timeStr;
|
||||
columns << progress.current_file;
|
||||
columns << progress.folder;
|
||||
columns << Progress::asResultString(progress.kind);
|
||||
columns << Utility::octetsToString( progress.file_size );
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(columns);
|
||||
item->setToolTip(0, longTimeStr);
|
||||
_ui->_treeWidget->insertTopLevelItem(0, item);
|
||||
Q_UNUSED(item);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef PROTOCOLWIDGET_H
|
||||
#define PROTOCOLWIDGET_H
|
||||
#ifndef FILEITEMDIALOG_H
|
||||
#define FILEITEMDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDateTime>
|
||||
@@ -20,56 +20,48 @@
|
||||
|
||||
#include "mirall/progressdispatcher.h"
|
||||
|
||||
#include "ui_protocolwidget.h"
|
||||
|
||||
class QPushButton;
|
||||
#include "ui_itemprogressdialog.h"
|
||||
|
||||
namespace Mirall {
|
||||
class SyncResult;
|
||||
|
||||
namespace Ui {
|
||||
class ProtocolWidget;
|
||||
class ItemProgressDialog;
|
||||
}
|
||||
class Application;
|
||||
|
||||
class ProtocolWidget : public QWidget
|
||||
class ItemProgressDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProtocolWidget(QWidget *parent = 0);
|
||||
~ProtocolWidget();
|
||||
explicit ItemProgressDialog(Application *app, QWidget *parent = 0);
|
||||
~ItemProgressDialog();
|
||||
|
||||
void initializeList();
|
||||
void setupList();
|
||||
void setSyncResult( const SyncResult& result );
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
void slotProgressInfo( const QString& folder, const Progress::Info& progress );
|
||||
void slotProgressProblem(const QString& folder, const Progress::SyncProblem& problem );
|
||||
void slotOpenFile( QTreeWidgetItem* item, int );
|
||||
void slotProgressErrors( const QString& folder, const Progress::SyncProblem& problem );
|
||||
|
||||
protected slots:
|
||||
void copyToClipboard();
|
||||
void slotClearBlacklist();
|
||||
|
||||
signals:
|
||||
void guiLog(const QString&, const QString&);
|
||||
|
||||
private:
|
||||
void setSyncResultStatus(const SyncResult& result );
|
||||
void cleanErrorItems( const QString& folder );
|
||||
void computeResyncButtonEnabled();
|
||||
|
||||
QTreeWidgetItem* createProgressTreewidgetItem(const Progress::Info& progress );
|
||||
QTreeWidgetItem* createProblemTreewidgetItem( const Progress::SyncProblem& problem);
|
||||
QList<QTreeWidgetItem*> errorItems(const QString &folder);
|
||||
|
||||
void cleanErrors( const QString& folder );
|
||||
QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat) const;
|
||||
|
||||
const int ErrorIndicatorRole;
|
||||
Ui::ProtocolWidget *_ui;
|
||||
QPushButton *_clearBlacklistBtn;
|
||||
Ui::ItemProgressDialog *_ui;
|
||||
int _problemCounter;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // PROTOCOLWIDGET_H
|
||||
#endif // FILEITEMDIALOG_H
|
||||
87
src/mirall/itemprogressdialog.ui
Normal file
87
src/mirall/itemprogressdialog.ui
Normal file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Mirall::ItemProgressDialog</class>
|
||||
<widget class="QWidget" name="Mirall::ItemProgressDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>612</width>
|
||||
<height>543</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Detailed Sync Protocol</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="_treeWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_errorLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="_dialogButtonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -52,9 +52,9 @@ LogWidget::LogWidget(QWidget *parent)
|
||||
|
||||
LogBrowser::LogBrowser(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
_logWidget( new LogWidget(parent) )
|
||||
_logWidget( new LogWidget(parent) ),
|
||||
_doFileFlush(false)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setObjectName("LogBrowser"); // for save/restoreGeometry()
|
||||
setWindowTitle(tr("Log Output"));
|
||||
setMinimumWidth(600);
|
||||
@@ -142,8 +142,39 @@ void LogBrowser::slotNewLog( const QString& msg )
|
||||
if( _logWidget->isVisible() ) {
|
||||
_logWidget->appendPlainText( msg );
|
||||
}
|
||||
|
||||
if( _logstream ) {
|
||||
(*_logstream) << msg << endl;
|
||||
if( _doFileFlush ) _logstream->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void LogBrowser::setLogFile( const QString & name, bool flush )
|
||||
{
|
||||
if( _logstream ) {
|
||||
_logFile.close();
|
||||
}
|
||||
|
||||
bool openSucceeded = false;
|
||||
if (name == QLatin1String("-")) {
|
||||
openSucceeded = _logFile.open(1, QIODevice::WriteOnly);
|
||||
} else {
|
||||
_logFile.setFileName( name );
|
||||
openSucceeded = _logFile.open(QIODevice::WriteOnly);
|
||||
}
|
||||
|
||||
if(!openSucceeded) {
|
||||
QMessageBox::warning(
|
||||
this,
|
||||
tr("Error"),
|
||||
QString(tr("<nobr>File '%1'<br/>cannot be opened for writing.<br/><br/>"
|
||||
"The log output can <b>not</b> be saved!</nobr>"))
|
||||
.arg(name));
|
||||
return;
|
||||
}
|
||||
_doFileFlush = flush;
|
||||
_logstream.reset(new QTextStream( &_logFile ));
|
||||
}
|
||||
|
||||
void LogBrowser::slotFind()
|
||||
{
|
||||
|
||||
@@ -64,6 +64,9 @@ private:
|
||||
QPushButton *_clearBtn;
|
||||
QLabel *_statusLabel;
|
||||
|
||||
QFile _logFile;
|
||||
bool _doFileFlush;
|
||||
QScopedPointer<QTextStream> _logstream;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -14,19 +14,8 @@
|
||||
|
||||
#include "mirall/logger.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QStringList>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
// logging handler.
|
||||
void mirallLogCatcher(QtMsgType type, const char *msg)
|
||||
{
|
||||
Q_UNUSED(type)
|
||||
// qDebug() exports to local8Bit, which is not always UTF-8
|
||||
Logger::instance()->mirallLog( QString::fromLocal8Bit(msg) );
|
||||
}
|
||||
|
||||
Logger* Logger::_instance=0;
|
||||
|
||||
Logger::Logger( QObject* parent)
|
||||
@@ -37,10 +26,7 @@ Logger::Logger( QObject* parent)
|
||||
|
||||
Logger *Logger::instance()
|
||||
{
|
||||
if( !Logger::_instance ) {
|
||||
Logger::_instance = new Logger;
|
||||
qInstallMsgHandler( mirallLogCatcher );
|
||||
}
|
||||
if( !Logger::_instance ) Logger::_instance = new Logger;
|
||||
return Logger::_instance;
|
||||
}
|
||||
|
||||
@@ -64,7 +50,7 @@ void Logger::postOptionalGuiLog(const QString &title, const QString &message)
|
||||
|
||||
void Logger::postGuiMessage(const QString &title, const QString &message)
|
||||
{
|
||||
emit guiMessage(title, message);
|
||||
emit postGuiMessage(title, message);
|
||||
}
|
||||
|
||||
void Logger::log(Log log)
|
||||
@@ -82,15 +68,6 @@ void Logger::log(Log log)
|
||||
msg += log.message;
|
||||
// _logs.append(log);
|
||||
// std::cout << qPrintable(log.message) << std::endl;
|
||||
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
if( _logstream ) {
|
||||
(*_logstream) << msg << endl;
|
||||
if( _doFileFlush ) _logstream->flush();
|
||||
}
|
||||
}
|
||||
|
||||
emit newLog(msg);
|
||||
}
|
||||
|
||||
@@ -114,83 +91,4 @@ void Logger::mirallLog( const QString& message )
|
||||
Logger::instance()->log( log_ );
|
||||
}
|
||||
|
||||
void Logger::setLogFile(const QString & name)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
if( _logstream ) {
|
||||
_logstream.reset(0);
|
||||
_logFile.close();
|
||||
}
|
||||
|
||||
if( name.isEmpty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool openSucceeded = false;
|
||||
if (name == QLatin1String("-")) {
|
||||
openSucceeded = _logFile.open(1, QIODevice::WriteOnly);
|
||||
} else {
|
||||
_logFile.setFileName( name );
|
||||
openSucceeded = _logFile.open(QIODevice::WriteOnly);
|
||||
}
|
||||
|
||||
if(!openSucceeded) {
|
||||
locker.unlock(); // Just in case postGuiMessage has a qDebug()
|
||||
postGuiMessage( tr("Error"),
|
||||
QString(tr("<nobr>File '%1'<br/>cannot be opened for writing.<br/><br/>"
|
||||
"The log output can <b>not</b> be saved!</nobr>"))
|
||||
.arg(name));
|
||||
return;
|
||||
}
|
||||
|
||||
_logstream.reset(new QTextStream( &_logFile ));
|
||||
}
|
||||
|
||||
void Logger::setLogExpire( int expire )
|
||||
{
|
||||
_logExpire = expire;
|
||||
}
|
||||
|
||||
void Logger::setLogDir( const QString& dir )
|
||||
{
|
||||
_logDirectory = dir;
|
||||
}
|
||||
|
||||
void Logger::setLogFlush( bool flush )
|
||||
{
|
||||
_doFileFlush = flush;
|
||||
}
|
||||
|
||||
void Logger::enterNextLogFile()
|
||||
{
|
||||
if (!_logDirectory.isEmpty()) {
|
||||
QDir dir(_logDirectory);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
|
||||
// Find out what is the file with the highest nymber if any
|
||||
QStringList files = dir.entryList(QStringList("owncloud.log.*"),
|
||||
QDir::Files);
|
||||
QRegExp rx("owncloud.log.(\\d+)");
|
||||
uint maxNumber = 0;
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
foreach(const QString &s, files) {
|
||||
if (rx.exactMatch(s)) {
|
||||
maxNumber = qMax(maxNumber, rx.cap(1).toUInt());
|
||||
if (_logExpire > 0) {
|
||||
QFileInfo fileInfo = dir.absoluteFilePath(s);
|
||||
if (fileInfo.lastModified().addSecs(60*60 * _logExpire) < now) {
|
||||
dir.remove(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString filename = _logDirectory + "/owncloud.log." + QString::number(maxNumber+1);
|
||||
setLogFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <qmutex.h>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
@@ -53,20 +50,12 @@ public:
|
||||
void postOptionalGuiLog(const QString& title, const QString& message);
|
||||
void postGuiMessage(const QString& title, const QString& message);
|
||||
|
||||
void setLogFile( const QString & name );
|
||||
void setLogExpire( int expire );
|
||||
void setLogDir( const QString& dir );
|
||||
void setLogFlush( bool flush );
|
||||
|
||||
signals:
|
||||
void newLog(const QString&);
|
||||
void guiLog(const QString&, const QString&);
|
||||
void guiMessage(const QString&, const QString&);
|
||||
void optionalGuiLog(const QString&, const QString&);
|
||||
|
||||
public slots:
|
||||
void enterNextLogFile();
|
||||
|
||||
protected:
|
||||
Logger(QObject* parent=0);
|
||||
QList<Log> _logs;
|
||||
@@ -74,14 +63,6 @@ protected:
|
||||
bool _doLogging;
|
||||
|
||||
static Logger* _instance;
|
||||
|
||||
QFile _logFile;
|
||||
bool _doFileFlush;
|
||||
int _logExpire;
|
||||
QScopedPointer<QTextStream> _logstream;
|
||||
QMutex _mutex;
|
||||
QString _logDirectory;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -21,20 +21,14 @@ namespace Mirall
|
||||
|
||||
MirallAccessManager::MirallAccessManager(QObject* parent)
|
||||
: QNetworkAccessManager (parent)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
QNetworkReply* MirallAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
|
||||
{
|
||||
QNetworkRequest newRequest(request);
|
||||
newRequest.setRawHeader(QByteArray("User-Agent"), Utility::userAgentString());
|
||||
QByteArray verb = newRequest.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray();
|
||||
// For PROPFIND (assumed to be a WebDAV op), set xml/utf8 as content type/encoding
|
||||
// This needs extension
|
||||
if (verb == "PROPFIND") {
|
||||
newRequest.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String("text/xml; charset=utf-8"));
|
||||
}
|
||||
return QNetworkAccessManager::createRequest(op, newRequest, outgoingData);
|
||||
|
||||
newRequest.setRawHeader( QByteArray("User-Agent"), Utility::userAgentString());
|
||||
return QNetworkAccessManager::createRequest (op, newRequest, outgoingData);
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -15,27 +15,22 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/owncloudtheme.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/utility.h"
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "creds/credentialsfactory.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
#include <QNetworkProxy>
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
|
||||
#define DEFAULT_REMOTE_POLL_INTERVAL 30000 // default remote poll time in milliseconds
|
||||
#define DEFAULT_MAX_LOG_LINES 20000
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
static const char urlC[] = "url";
|
||||
static const char authTypeC[] = "authType";
|
||||
|
||||
static const char caCertsKeyC[] = "CaCertificates";
|
||||
static const char remotePollIntervalC[] = "remotePollInterval";
|
||||
static const char forceSyncIntervalC[] = "forceSyncInterval";
|
||||
@@ -62,18 +57,39 @@ static const char maxLogLinesC[] = "Logging/maxLogLines";
|
||||
QString MirallConfigFile::_oCVersion;
|
||||
QString MirallConfigFile::_confDir = QString::null;
|
||||
bool MirallConfigFile::_askedUser = false;
|
||||
QMap< QString, MirallConfigFile::SharedCreds > MirallConfigFile::credentialsPerConfig;
|
||||
|
||||
MirallConfigFile::MirallConfigFile()
|
||||
MirallConfigFile::MirallConfigFile( const QString& appendix, bool useOldConfig )
|
||||
{
|
||||
|
||||
if (useOldConfig && !appendix.isEmpty()) {
|
||||
QString oldConfigFile = configFile();
|
||||
_customHandle = appendix;
|
||||
QString newConfigFile = configFile();
|
||||
QFile::copy(oldConfigFile, newConfigFile);
|
||||
} else {
|
||||
_customHandle = appendix;
|
||||
}
|
||||
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
if (! credentialsPerConfig.contains(_customHandle)) {
|
||||
QString con( _customHandle );
|
||||
if( _customHandle.isEmpty() ) con = defaultConnection();
|
||||
|
||||
const QString config = configFile();
|
||||
const QString config = configFile();
|
||||
qDebug() << "Loading config: " << config;
|
||||
|
||||
|
||||
QSettings settings(config, QSettings::IniFormat);
|
||||
settings.beginGroup( defaultConnection() );
|
||||
QSettings settings(config, QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup( con );
|
||||
|
||||
qDebug() << "Loading config: " << config << " (URL is " << settings.value("url").toString() << ")";
|
||||
QString type = settings.value( QLatin1String(authTypeC) ).toString();
|
||||
|
||||
qDebug() << "Getting credentials of type " << type << " for " << _customHandle;
|
||||
|
||||
credentialsPerConfig.insert(_customHandle, SharedCreds(CredentialsFactory::create (type)));
|
||||
}
|
||||
}
|
||||
|
||||
void MirallConfigFile::setConfDir(const QString &value)
|
||||
@@ -98,12 +114,14 @@ void MirallConfigFile::setConfDir(const QString &value)
|
||||
bool MirallConfigFile::optionalDesktopNotifications() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
return settings.value(QLatin1String(optionalDesktopNoficationsC), true).toBool();
|
||||
}
|
||||
|
||||
void MirallConfigFile::setOptionalDesktopNotifications(bool show)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.setValue(QLatin1String(optionalDesktopNoficationsC), show);
|
||||
settings.sync();
|
||||
}
|
||||
@@ -111,12 +129,14 @@ void MirallConfigFile::setOptionalDesktopNotifications(bool show)
|
||||
QString MirallConfigFile::seenVersion() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
return settings.value(QLatin1String(seenVersionC)).toString();
|
||||
}
|
||||
|
||||
void MirallConfigFile::setSeenVersion(const QString &version)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.setValue(QLatin1String(seenVersionC), version);
|
||||
settings.sync();
|
||||
}
|
||||
@@ -125,6 +145,7 @@ void MirallConfigFile::saveGeometry(QWidget *w)
|
||||
{
|
||||
Q_ASSERT(!w->objectName().isNull());
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup(w->objectName());
|
||||
settings.setValue(QLatin1String(geometryC), w->saveGeometry());
|
||||
settings.sync();
|
||||
@@ -138,7 +159,7 @@ void MirallConfigFile::restoreGeometry(QWidget *w)
|
||||
QString MirallConfigFile::configPath() const
|
||||
{
|
||||
if( _confDir.isEmpty() ) {
|
||||
_confDir = Utility::dataLocation();
|
||||
_confDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
}
|
||||
QString dir = _confDir;
|
||||
|
||||
@@ -146,12 +167,6 @@ QString MirallConfigFile::configPath() const
|
||||
return dir;
|
||||
}
|
||||
|
||||
QString MirallConfigFile::configPathWithAppName() const
|
||||
{
|
||||
//HACK
|
||||
return QFileInfo( configFile() ).dir().absolutePath().append("/");
|
||||
}
|
||||
|
||||
QString MirallConfigFile::excludeFile(Scope scope) const
|
||||
{
|
||||
// prefer sync-exclude.lst, but if it does not exist, check for
|
||||
@@ -175,14 +190,14 @@ QString MirallConfigFile::excludeFile(Scope scope) const
|
||||
// Check alternative places...
|
||||
if( ! fi.isReadable() ) {
|
||||
#ifdef Q_OS_WIN
|
||||
fi.setFile( QCoreApplication::applicationDirPath(), exclFile );
|
||||
fi.setFile( QApplication::applicationDirPath(), exclFile );
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
fi.setFile( QString( SYSCONFDIR "/%1").arg(Theme::instance()->appName()), exclFile );
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
// exec path is inside the bundle
|
||||
fi.setFile( QCoreApplication::applicationDirPath(),
|
||||
fi.setFile( QApplication::applicationDirPath(),
|
||||
QLatin1String("../Resources/") + exclFile );
|
||||
#endif
|
||||
}
|
||||
@@ -198,7 +213,13 @@ QString MirallConfigFile::configFile() const
|
||||
if( qApp->applicationName().isEmpty() ) {
|
||||
qApp->setApplicationName( Theme::instance()->appNameGUI() );
|
||||
}
|
||||
return configPath() + Theme::instance()->configFileName();
|
||||
QString file = configPath() + Theme::instance()->configFileName();
|
||||
if( !_customHandle.isEmpty() ) {
|
||||
file.append( QLatin1Char('_'));
|
||||
file.append( _customHandle );
|
||||
qDebug() << __PRETTY_FUNCTION__ << " OO Custom config file in use: " << file;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
bool MirallConfigFile::exists()
|
||||
@@ -212,10 +233,47 @@ QString MirallConfigFile::defaultConnection() const
|
||||
return Theme::instance()->appName();
|
||||
}
|
||||
|
||||
bool MirallConfigFile::connectionExists( const QString& conn )
|
||||
{
|
||||
QString con = conn;
|
||||
if( conn.isEmpty() ) con = defaultConnection();
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.beginGroup(conn);
|
||||
return settings.contains( QLatin1String(urlC) );
|
||||
}
|
||||
|
||||
|
||||
void MirallConfigFile::writeOwncloudConfig( const QString& connection,
|
||||
const QString& url,
|
||||
AbstractCredentials* credentials)
|
||||
{
|
||||
const QString file = configFile();
|
||||
qDebug() << "*** writing mirall config to " << file;
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.beginGroup( connection );
|
||||
settings.setValue( QLatin1String(urlC), url );
|
||||
settings.setValue(QLatin1String(authTypeC), credentials->authType());
|
||||
credentialsPerConfig.insert(_customHandle, SharedCreds(credentials));
|
||||
settings.sync();
|
||||
// check the perms, only read-write for the owner.
|
||||
QFile::setPermissions( file, QFile::ReadOwner|QFile::WriteOwner );
|
||||
|
||||
// Store credentials temporar until the config is finalized.
|
||||
//ownCloudInfo::instance()->setCredentials( user, passwd, _customHandle );
|
||||
|
||||
}
|
||||
|
||||
void MirallConfigFile::storeData(const QString& group, const QString& key, const QVariant& value)
|
||||
{
|
||||
const QString con(group.isEmpty() ? defaultConnection() : group);
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.beginGroup(con);
|
||||
settings.setValue(key, value);
|
||||
@@ -226,6 +284,7 @@ QVariant MirallConfigFile::retrieveData(const QString& group, const QString& key
|
||||
{
|
||||
const QString con(group.isEmpty() ? defaultConnection() : group);
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.beginGroup(con);
|
||||
return settings.value(key);
|
||||
@@ -235,6 +294,7 @@ void MirallConfigFile::removeData(const QString& group, const QString& key)
|
||||
{
|
||||
const QString con(group.isEmpty() ? defaultConnection() : group);
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.beginGroup(con);
|
||||
settings.remove(key);
|
||||
@@ -244,6 +304,7 @@ bool MirallConfigFile::dataExists(const QString& group, const QString& key) cons
|
||||
{
|
||||
const QString con(group.isEmpty() ? defaultConnection() : group);
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.beginGroup(con);
|
||||
return settings.contains(key);
|
||||
@@ -252,23 +313,66 @@ bool MirallConfigFile::dataExists(const QString& group, const QString& key) cons
|
||||
QByteArray MirallConfigFile::caCerts( )
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
return settings.value( QLatin1String(caCertsKeyC) ).toByteArray();
|
||||
}
|
||||
|
||||
void MirallConfigFile::setCaCerts( const QByteArray & certs )
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.setIniCodec( "UTF-8" );
|
||||
settings.setValue( QLatin1String(caCertsKeyC), certs );
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
|
||||
void MirallConfigFile::removeConnection( const QString& connection )
|
||||
{
|
||||
QString con( connection );
|
||||
if( connection.isEmpty() ) con = defaultConnection();
|
||||
|
||||
qDebug() << " removing the config file for connection " << con;
|
||||
|
||||
// Currently its just removing the entire config file
|
||||
// TODO: Eh? Shouldn't it try to load a file under configFile() and set it to INI?
|
||||
QSettings settings;
|
||||
settings.setIniCodec( "UTF-8" );
|
||||
settings.beginGroup( con );
|
||||
settings.remove(QString::null); // removes all content from the group
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
/*
|
||||
* returns the configured owncloud url if its already configured, otherwise an empty
|
||||
* string.
|
||||
* The returned url always has a trailing hash.
|
||||
*/
|
||||
QString MirallConfigFile::ownCloudUrl( const QString& connection) const
|
||||
{
|
||||
QString con( connection );
|
||||
if( connection.isEmpty() ) con = defaultConnection();
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup( con );
|
||||
|
||||
QString url = settings.value( QLatin1String(urlC) ).toString();
|
||||
if( ! url.isEmpty() ) {
|
||||
if( ! url.endsWith(QLatin1Char('/'))) url.append(QLatin1String("/"));
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
int MirallConfigFile::remotePollInterval( const QString& connection ) const
|
||||
{
|
||||
QString con( connection );
|
||||
if( connection.isEmpty() ) con = defaultConnection();
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup( con );
|
||||
|
||||
int remoteInterval = settings.value( QLatin1String(remotePollIntervalC), DEFAULT_REMOTE_POLL_INTERVAL ).toInt();
|
||||
@@ -289,6 +393,7 @@ void MirallConfigFile::setRemotePollInterval(int interval, const QString &connec
|
||||
return;
|
||||
}
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup( con );
|
||||
settings.setValue(QLatin1String(remotePollIntervalC), interval );
|
||||
settings.sync();
|
||||
@@ -301,6 +406,7 @@ quint64 MirallConfigFile::forceSyncInterval(const QString& connection) const
|
||||
QString con( connection );
|
||||
if( connection.isEmpty() ) con = defaultConnection();
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup( con );
|
||||
|
||||
quint64 interval = settings.value( QLatin1String(forceSyncIntervalC), 10 * pollInterval ).toULongLong();
|
||||
@@ -329,6 +435,7 @@ bool MirallConfigFile::ownCloudSkipUpdateCheck( const QString& connection ) cons
|
||||
if( connection.isEmpty() ) con = defaultConnection();
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup( con );
|
||||
|
||||
bool skipIt = settings.value( QLatin1String(skipUpdateCheckC), false ).toBool();
|
||||
@@ -342,6 +449,7 @@ void MirallConfigFile::setOwnCloudSkipUpdateCheck( bool skip, const QString& con
|
||||
if( connection.isEmpty() ) con = defaultConnection();
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.beginGroup( con );
|
||||
|
||||
settings.setValue( QLatin1String(skipUpdateCheckC), QVariant(skip) );
|
||||
@@ -352,16 +460,67 @@ void MirallConfigFile::setOwnCloudSkipUpdateCheck( bool skip, const QString& con
|
||||
int MirallConfigFile::maxLogLines() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
return settings.value( QLatin1String(maxLogLinesC), DEFAULT_MAX_LOG_LINES ).toInt();
|
||||
}
|
||||
|
||||
void MirallConfigFile::setMaxLogLines( int lines )
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.setValue(QLatin1String(maxLogLinesC), lines);
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
// remove a custom config file.
|
||||
void MirallConfigFile::cleanupCustomConfig()
|
||||
{
|
||||
if( _customHandle.isEmpty() ) {
|
||||
qDebug() << "SKipping to erase the main configuration.";
|
||||
return;
|
||||
}
|
||||
QString file = configFile();
|
||||
if( QFile::exists( file ) ) {
|
||||
QFile::remove( file );
|
||||
}
|
||||
}
|
||||
|
||||
// accept a config identified by the customHandle as general config.
|
||||
void MirallConfigFile::acceptCustomConfig()
|
||||
{
|
||||
if( _customHandle.isEmpty() ) {
|
||||
qDebug() << "WRN: Custom Handle is empty. Can not accept.";
|
||||
return;
|
||||
}
|
||||
|
||||
QString srcConfig = configFile(); // this considers the custom handle
|
||||
|
||||
credentialsPerConfig.insert(QString(), credentialsPerConfig[_customHandle]);
|
||||
credentialsPerConfig.remove(_customHandle);
|
||||
_customHandle.clear();
|
||||
QString targetConfig = configFile();
|
||||
QString targetBak = targetConfig + QLatin1String(".bak");
|
||||
|
||||
bool bakOk = false;
|
||||
// remove an evtl existing old config backup.
|
||||
if( QFile::exists( targetBak ) ) {
|
||||
QFile::remove( targetBak );
|
||||
}
|
||||
// create a backup of the current config.
|
||||
bakOk = QFile::rename( targetConfig, targetBak );
|
||||
|
||||
// move the custom config to the master place.
|
||||
if( ! QFile::rename( srcConfig, targetConfig ) ) {
|
||||
// if the move from custom to master failed, put old backup back.
|
||||
if( bakOk ) {
|
||||
QFile::rename( targetBak, targetConfig );
|
||||
}
|
||||
}
|
||||
QFile::remove( targetBak );
|
||||
|
||||
credentialsPerConfig[QString()]->persistForUrl(ownCloudUrl());
|
||||
}
|
||||
|
||||
void MirallConfigFile::setProxyType(int proxyType,
|
||||
const QString& host,
|
||||
int port, bool needsAuth,
|
||||
@@ -369,6 +528,7 @@ void MirallConfigFile::setProxyType(int proxyType,
|
||||
const QString& pass)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.setValue(QLatin1String(proxyTypeC), proxyType);
|
||||
|
||||
@@ -387,6 +547,7 @@ QVariant MirallConfigFile::getValue(const QString& param, const QString& group,
|
||||
const QVariant& defaultValue) const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
if (!group.isEmpty())
|
||||
settings.beginGroup(group);
|
||||
|
||||
@@ -396,6 +557,7 @@ QVariant MirallConfigFile::getValue(const QString& param, const QString& group,
|
||||
void MirallConfigFile::setValue(const QString& key, const QVariant &value)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
|
||||
settings.setValue(key, value);
|
||||
}
|
||||
@@ -474,13 +636,20 @@ void MirallConfigFile::setDownloadLimit(int kbytes)
|
||||
bool MirallConfigFile::monoIcons() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
return settings.value(QLatin1String(monoIconsC), false).toBool();
|
||||
}
|
||||
|
||||
void MirallConfigFile::setMonoIcons(bool useMonoIcons)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setIniCodec("UTF-8");
|
||||
settings.setValue(QLatin1String(monoIconsC), useMonoIcons);
|
||||
}
|
||||
|
||||
AbstractCredentials* MirallConfigFile::getCredentials() const
|
||||
{
|
||||
return credentialsPerConfig[_customHandle].data();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,19 +28,29 @@ class AbstractCredentials;
|
||||
class MirallConfigFile
|
||||
{
|
||||
public:
|
||||
MirallConfigFile();
|
||||
MirallConfigFile( const QString& appendix = QString(), bool useOldConfig = false );
|
||||
|
||||
enum Scope { UserScope, SystemScope };
|
||||
|
||||
QString configPath() const;
|
||||
QString configPathWithAppName() const;
|
||||
QString configFile() const;
|
||||
QString excludeFile(Scope scope) const;
|
||||
|
||||
bool exists();
|
||||
|
||||
bool connectionExists( const QString& = QString() );
|
||||
QString defaultConnection() const;
|
||||
|
||||
void writeOwncloudConfig( const QString& connection,
|
||||
const QString& url,
|
||||
AbstractCredentials* credentials);
|
||||
|
||||
AbstractCredentials* getCredentials() const;
|
||||
|
||||
void removeConnection( const QString& connection = QString() );
|
||||
|
||||
QString ownCloudUrl( const QString& connection = QString() ) const;
|
||||
|
||||
// the certs do not depend on a connection.
|
||||
QByteArray caCerts();
|
||||
void setCaCerts( const QByteArray& );
|
||||
@@ -65,6 +75,11 @@ public:
|
||||
/* Force sync interval, in milliseconds */
|
||||
quint64 forceSyncInterval(const QString &connection = QString()) const;
|
||||
|
||||
// Custom Config: accept the custom config to become the main one.
|
||||
void acceptCustomConfig();
|
||||
// Custom Config: remove the custom config file.
|
||||
void cleanupCustomConfig();
|
||||
|
||||
bool monoIcons() const;
|
||||
void setMonoIcons(bool);
|
||||
|
||||
@@ -121,6 +136,8 @@ private:
|
||||
static bool _askedUser;
|
||||
static QString _oCVersion;
|
||||
static QString _confDir;
|
||||
static QMap< QString, SharedCreds > credentialsPerConfig;
|
||||
QString _customHandle;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,534 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSslConfiguration>
|
||||
#include <QBuffer>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QStringList>
|
||||
#include <QStack>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
#include "mirall/networkjobs.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
#include "creds/credentialsfactory.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
AbstractNetworkJob::AbstractNetworkJob(Account *account, const QString &path, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _ignoreCredentialFailure(false)
|
||||
, _reply(0)
|
||||
, _account(account)
|
||||
, _path(path)
|
||||
, _timer(0)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::setReply(QNetworkReply *reply)
|
||||
{
|
||||
if (_reply) {
|
||||
_reply->deleteLater();
|
||||
}
|
||||
_reply = reply;
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::setTimeout(qint64 msec)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << msec;
|
||||
if (_timer)
|
||||
_timer->deleteLater();
|
||||
_timer = new QTimer(this);
|
||||
_timer->setSingleShot(true);
|
||||
connect(_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
|
||||
_timer->start(msec);
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::resetTimeout()
|
||||
{
|
||||
qint64 interval = _timer->interval();
|
||||
_timer->stop();
|
||||
_timer->start(interval);
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::setIgnoreCredentialFailure(bool ignore)
|
||||
{
|
||||
_ignoreCredentialFailure = ignore;
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::setAccount(Account *account)
|
||||
{
|
||||
_account = account;
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::setPath(const QString &path)
|
||||
{
|
||||
_path = path;
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::slotError(QNetworkReply::NetworkError)
|
||||
{
|
||||
qDebug() << metaObject()->className() << "Error:" << _reply->errorString();
|
||||
emit networkError(_reply);
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::setupConnections(QNetworkReply *reply)
|
||||
{
|
||||
connect(reply, SIGNAL(finished()), SLOT(slotFinished()));
|
||||
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(slotError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
||||
QNetworkReply* AbstractNetworkJob::davRequest(const QByteArray &verb, const QString &relPath,
|
||||
QNetworkRequest req, QIODevice *data)
|
||||
{
|
||||
return _account->davRequest(verb, relPath, req, data);
|
||||
}
|
||||
|
||||
QNetworkReply *AbstractNetworkJob::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
||||
{
|
||||
return _account->davRequest(verb, url, req, data);
|
||||
}
|
||||
|
||||
QNetworkReply* AbstractNetworkJob::getRequest(const QString &relPath)
|
||||
{
|
||||
return _account->getRequest(relPath);
|
||||
}
|
||||
|
||||
QNetworkReply *AbstractNetworkJob::getRequest(const QUrl &url)
|
||||
{
|
||||
return _account->getRequest(url);
|
||||
}
|
||||
|
||||
QNetworkReply *AbstractNetworkJob::headRequest(const QString &relPath)
|
||||
{
|
||||
return _account->headRequest(relPath);
|
||||
}
|
||||
|
||||
QNetworkReply *AbstractNetworkJob::headRequest(const QUrl &url)
|
||||
{
|
||||
return _account->headRequest(url);
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::slotFinished()
|
||||
{
|
||||
static QMutex mutex;
|
||||
AbstractCredentials *creds = _account->credentials();
|
||||
if (creds->stillValid(_reply) || _ignoreCredentialFailure) {
|
||||
finished();
|
||||
} else {
|
||||
// If other jobs that still were created from before
|
||||
// the account was put offline by the code below,
|
||||
// we do want them to fail silently while we
|
||||
// query the user
|
||||
if (mutex.tryLock()) {
|
||||
Account *a = account();
|
||||
bool fetched = creds->fetchFromUser(a);
|
||||
if (fetched) {
|
||||
a->setState(Account::Connected);
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
AbstractNetworkJob::~AbstractNetworkJob() {
|
||||
_reply->deleteLater();
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::start()
|
||||
{
|
||||
qDebug() << "!!!" << metaObject()->className() << "created for" << account()->url() << "querying" << path();
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
RequestEtagJob::RequestEtagJob(Account *account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void RequestEtagJob::start()
|
||||
{
|
||||
QNetworkRequest req;
|
||||
if (path().isEmpty() || path() == QLatin1String("/")) {
|
||||
/* For the root directory, we need to query the etags of all the sub directories
|
||||
* because, at the time I am writing this comment (Owncloud 5.0.9), the etag of the
|
||||
* root directory is not updated when the sub directories changes */
|
||||
req.setRawHeader("Depth", "1");
|
||||
} else {
|
||||
req.setRawHeader("Depth", "0");
|
||||
}
|
||||
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
||||
"<d:propfind xmlns:d=\"DAV:\">\n"
|
||||
" <d:prop>\n"
|
||||
" <d:getetag/>\n"
|
||||
" </d:prop>\n"
|
||||
"</d:propfind>\n");
|
||||
QBuffer *buf = new QBuffer(this);
|
||||
buf->setData(xml);
|
||||
buf->open(QIODevice::ReadOnly);
|
||||
// assumes ownership
|
||||
setReply(davRequest("PROPFIND", path(), req, buf));
|
||||
buf->setParent(reply());
|
||||
setupConnections(reply());
|
||||
|
||||
if( reply()->error() != QNetworkReply::NoError ) {
|
||||
qDebug() << "getting etag: request network error: " << reply()->errorString();
|
||||
}
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void RequestEtagJob::finished()
|
||||
{
|
||||
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(reply());
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
QString etag;
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
QString name = reader.name().toString();
|
||||
if (name == QLatin1String("getetag")) {
|
||||
etag += reader.readElementText();
|
||||
}
|
||||
}
|
||||
}
|
||||
emit etagRetreived(etag);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
MkColJob::MkColJob(Account *account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void MkColJob::start()
|
||||
{
|
||||
// assumes ownership
|
||||
QNetworkReply *reply = davRequest("MKCOL", path());
|
||||
setReply(reply);
|
||||
setupConnections(reply);
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void MkColJob::finished()
|
||||
{
|
||||
emit finished(reply()->error());
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
LsColJob::LsColJob(Account *account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void LsColJob::start()
|
||||
{
|
||||
QNetworkRequest req;
|
||||
req.setRawHeader("Depth", "1");
|
||||
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
||||
"<d:propfind xmlns:d=\"DAV:\">\n"
|
||||
" <d:prop>\n"
|
||||
" <d:resourcetype/>\n"
|
||||
" </d:prop>\n"
|
||||
"</d:propfind>\n");
|
||||
QBuffer *buf = new QBuffer(this);
|
||||
buf->setData(xml);
|
||||
buf->open(QIODevice::ReadOnly);
|
||||
QNetworkReply *reply = davRequest("PROPFIND", path(), req, buf);
|
||||
buf->setParent(reply);
|
||||
setReply(reply);
|
||||
setupConnections(reply);
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void LsColJob::finished()
|
||||
{
|
||||
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(reply());
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
|
||||
QStringList folders;
|
||||
QString currentItem;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
QString name = reader.name().toString();
|
||||
if (name == QLatin1String("href")) {
|
||||
currentItem = reader.readElementText();
|
||||
} else if (name == QLatin1String("collection") &&
|
||||
!currentItem.isEmpty()) {
|
||||
folders.append(QUrl::fromEncoded(currentItem.toLatin1()).path());
|
||||
currentItem.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
emit directoryListing(folders);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
CheckServerJob::CheckServerJob(Account *account, bool followRedirect, QObject *parent)
|
||||
: AbstractNetworkJob(account, QLatin1String("status.php") , parent)
|
||||
, _followRedirects(followRedirect)
|
||||
, _redirectCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
void CheckServerJob::start()
|
||||
{
|
||||
setReply(getRequest(path()));
|
||||
setupConnections(reply());
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void CheckServerJob::slotTimeout()
|
||||
{
|
||||
qDebug() << "TIMEOUT" << Q_FUNC_INFO;
|
||||
if (reply()->isRunning())
|
||||
emit timeout(reply()->url());
|
||||
}
|
||||
|
||||
QString CheckServerJob::version(const QVariantMap &info)
|
||||
{
|
||||
return info.value(QLatin1String("version")).toString();
|
||||
}
|
||||
|
||||
QString CheckServerJob::versionString(const QVariantMap &info)
|
||||
{
|
||||
return info.value(QLatin1String("versionstring")).toString();
|
||||
}
|
||||
|
||||
bool CheckServerJob::installed(const QVariantMap &info)
|
||||
{
|
||||
return info.value(QLatin1String("installed")).toBool();
|
||||
}
|
||||
|
||||
void CheckServerJob::finished()
|
||||
{
|
||||
account()->setCertificateChain(reply()->sslConfiguration().peerCertificateChain());
|
||||
|
||||
// ### the qDebugs here should be exported via displayErrors() so they
|
||||
// ### can be presented to the user if the job executor has a GUI
|
||||
QUrl requestedUrl = reply()->request().url();
|
||||
QUrl redirectUrl = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
if (!redirectUrl.isEmpty()) {
|
||||
_redirectCount++;
|
||||
if (requestedUrl.scheme() == QLatin1String("https") &&
|
||||
redirectUrl.scheme() == QLatin1String("http")) {
|
||||
qDebug() << Q_FUNC_INFO << "HTTPS->HTTP downgrade detected!";
|
||||
} else if (requestedUrl == redirectUrl || _redirectCount >= maxRedirects()) {
|
||||
qDebug() << Q_FUNC_INFO << "Redirect loop detected!";
|
||||
} else {
|
||||
resetTimeout();
|
||||
setReply(getRequest(redirectUrl));
|
||||
setupConnections(reply());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
QVariantMap status = QtJson::parse(QString::fromUtf8(reply()->readAll()), success).toMap();
|
||||
// empty or invalid response
|
||||
if (!success || status.isEmpty()) {
|
||||
qDebug() << "status.php from server is not valid JSON!";
|
||||
}
|
||||
|
||||
qDebug() << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
|
||||
if( status.contains("installed")
|
||||
&& status.contains("version")
|
||||
&& status.contains("versionstring") ) {
|
||||
emit instanceFound(reply()->url(), status);
|
||||
} else {
|
||||
qDebug() << "No proper answer on " << requestedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
PropfindJob::PropfindJob(Account *account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PropfindJob::start()
|
||||
{
|
||||
QList<QByteArray> properties = _properties;
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
properties << "allprop";
|
||||
}
|
||||
QNetworkRequest req;
|
||||
req.setRawHeader("Depth", "0");
|
||||
QByteArray propStr;
|
||||
foreach (const QByteArray &prop, properties) {
|
||||
propStr += " <d:" + prop + " />\n";
|
||||
}
|
||||
QByteArray xml = "<?xml version=\"1.0\" ?>\n"
|
||||
"<d:propfind xmlns:d=\"DAV:\">\n"
|
||||
" <d:prop>\n"
|
||||
+ propStr +
|
||||
" </d:prop>\n"
|
||||
"</d:propfind>\n";
|
||||
|
||||
QBuffer *buf = new QBuffer(this);
|
||||
buf->setData(xml);
|
||||
buf->open(QIODevice::ReadOnly);
|
||||
setReply(davRequest("PROPFIND", path(), req, buf));
|
||||
buf->setParent(reply());
|
||||
setupConnections(reply());
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void PropfindJob::setProperties(QList<QByteArray> properties)
|
||||
{
|
||||
_properties = properties;
|
||||
}
|
||||
|
||||
QList<QByteArray> PropfindJob::properties() const
|
||||
{
|
||||
return _properties;
|
||||
}
|
||||
|
||||
void PropfindJob::finished()
|
||||
{
|
||||
int http_result_code = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (http_result_code == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(reply());
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
|
||||
QVariantMap items;
|
||||
// introduced to nesting is ignored
|
||||
QStack<QString> curElement;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
if (curElement.isEmpty()) {
|
||||
curElement.push(reader.name().toString());
|
||||
items.insert(reader.name().toString(), reader.text().toString());
|
||||
}
|
||||
}
|
||||
if (type == QXmlStreamReader::EndElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
if(curElement.top() == reader.name()) {
|
||||
curElement.pop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
emit result(items);
|
||||
} else {
|
||||
qDebug() << "Quota request *not* successful, http result code is " << http_result_code;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
EntityExistsJob::EntityExistsJob(Account *account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void EntityExistsJob::start()
|
||||
{
|
||||
setReply(headRequest(path()));
|
||||
setupConnections(reply());
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void EntityExistsJob::finished()
|
||||
{
|
||||
emit exists(reply());
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
CheckQuotaJob::CheckQuotaJob(Account *account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void CheckQuotaJob::start()
|
||||
{
|
||||
QNetworkRequest req;
|
||||
req.setRawHeader("Depth", "0");
|
||||
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
||||
"<d:propfind xmlns:d=\"DAV:\">\n"
|
||||
" <d:prop>\n"
|
||||
" <d:quota-available-bytes/>\n"
|
||||
" <d:quota-used-bytes/>\n"
|
||||
" </d:prop>\n"
|
||||
"</d:propfind>\n");
|
||||
QBuffer *buf = new QBuffer(this);
|
||||
buf->setData(xml);
|
||||
buf->open(QIODevice::ReadOnly);
|
||||
// assumes ownership
|
||||
setReply(davRequest("PROPFIND", path(), req, buf));
|
||||
buf->setParent(reply());
|
||||
setupConnections(reply());
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void CheckQuotaJob::finished()
|
||||
{
|
||||
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(reply());
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
qint64 quotaAvailableBytes = 0;
|
||||
qint64 quotaUsedBytes = 0;
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
QString name = reader.name().toString();
|
||||
if (name == QLatin1String("quota-available-bytes")) {
|
||||
quotaAvailableBytes = reader.readElementText().toLongLong();
|
||||
} else if (name == QLatin1String("quota-used-bytes")) {
|
||||
quotaUsedBytes = reader.readElementText().toLongLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
qint64 total = quotaUsedBytes + quotaAvailableBytes;
|
||||
emit quotaRetrieved(total, quotaUsedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef NETWORKJOBS_H
|
||||
#define NETWORKJOBS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class QUrl;
|
||||
class QTimer;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class Account;
|
||||
class AbstractSslErrorHandler;
|
||||
|
||||
/**
|
||||
* @brief The AbstractNetworkJob class
|
||||
*/
|
||||
class AbstractNetworkJob : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AbstractNetworkJob(Account *account, const QString &path, QObject* parent = 0);
|
||||
virtual ~AbstractNetworkJob();
|
||||
|
||||
virtual void start();
|
||||
|
||||
void setAccount(Account *account);
|
||||
Account* account() const { return _account; }
|
||||
void setPath(const QString &path);
|
||||
QString path() const { return _path; }
|
||||
|
||||
void setReply(QNetworkReply *reply);
|
||||
QNetworkReply* reply() const { return _reply; }
|
||||
|
||||
void setTimeout(qint64 msec);
|
||||
void resetTimeout();
|
||||
|
||||
void setIgnoreCredentialFailure(bool ignore);
|
||||
bool ignoreCredentialFailure() const { return _ignoreCredentialFailure; }
|
||||
|
||||
signals:
|
||||
void networkError(QNetworkReply *reply);
|
||||
protected:
|
||||
void setupConnections(QNetworkReply *reply);
|
||||
QNetworkReply* davRequest(const QByteArray& verb, const QString &relPath,
|
||||
QNetworkRequest req = QNetworkRequest(),
|
||||
QIODevice *data = 0);
|
||||
QNetworkReply* davRequest(const QByteArray& verb, const QUrl &url,
|
||||
QNetworkRequest req = QNetworkRequest(),
|
||||
QIODevice *data = 0);
|
||||
QNetworkReply* getRequest(const QString &relPath);
|
||||
QNetworkReply* getRequest(const QUrl &url);
|
||||
QNetworkReply* headRequest(const QString &relPath);
|
||||
QNetworkReply* headRequest(const QUrl &url);
|
||||
|
||||
int maxRedirects() const { return 10; }
|
||||
virtual void finished() = 0;
|
||||
|
||||
private slots:
|
||||
void slotFinished();
|
||||
void slotError(QNetworkReply::NetworkError);
|
||||
virtual void slotTimeout() {}
|
||||
|
||||
private:
|
||||
bool _ignoreCredentialFailure;
|
||||
QNetworkReply *_reply;
|
||||
Account *_account;
|
||||
QString _path;
|
||||
QTimer *_timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The EntityExistsJob class
|
||||
*/
|
||||
class EntityExistsJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EntityExistsJob(Account *account, const QString &path, QObject* parent = 0);
|
||||
void start();
|
||||
|
||||
signals:
|
||||
void exists(QNetworkReply*);
|
||||
|
||||
private slots:
|
||||
virtual void finished();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The LsColJob class
|
||||
*/
|
||||
class LsColJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LsColJob(Account *account, const QString &path, QObject *parent = 0);
|
||||
void start();
|
||||
|
||||
signals:
|
||||
void directoryListing(const QStringList &items);
|
||||
|
||||
private slots:
|
||||
virtual void finished();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The PropfindJob class
|
||||
*/
|
||||
class PropfindJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PropfindJob(Account *account, const QString &path, QObject *parent = 0);
|
||||
void start();
|
||||
void setProperties(QList<QByteArray> properties);
|
||||
QList<QByteArray> properties() const;
|
||||
|
||||
signals:
|
||||
void result(const QVariantMap &values);
|
||||
|
||||
private slots:
|
||||
virtual void finished();
|
||||
|
||||
private:
|
||||
QList<QByteArray> _properties;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The MkColJob class
|
||||
*/
|
||||
class MkColJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MkColJob(Account *account, const QString &path, QObject *parent = 0);
|
||||
void start();
|
||||
|
||||
signals:
|
||||
void finished(QNetworkReply::NetworkError);
|
||||
|
||||
private slots:
|
||||
virtual void finished();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The CheckServerJob class
|
||||
*/
|
||||
class CheckServerJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CheckServerJob(Account *account, bool followRedirect = false, QObject *parent = 0);
|
||||
void start();
|
||||
|
||||
static QString version(const QVariantMap &info);
|
||||
static QString versionString(const QVariantMap &info);
|
||||
static bool installed(const QVariantMap &info);
|
||||
|
||||
signals:
|
||||
void instanceFound(const QUrl&url, const QVariantMap &info);
|
||||
void timeout(const QUrl&url);
|
||||
|
||||
private slots:
|
||||
virtual void finished();
|
||||
virtual void slotTimeout();
|
||||
|
||||
private:
|
||||
bool _followRedirects;
|
||||
int _redirectCount;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The RequestEtagJob class
|
||||
*/
|
||||
class RequestEtagJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RequestEtagJob(Account *account, const QString &path, QObject *parent = 0);
|
||||
void start();
|
||||
|
||||
signals:
|
||||
void etagRetreived(const QString &etag);
|
||||
|
||||
private slots:
|
||||
virtual void finished();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The CheckQuota class
|
||||
*/
|
||||
class CheckQuotaJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CheckQuotaJob(Account *account, const QString &path, QObject *parent = 0);
|
||||
void start();
|
||||
|
||||
signals:
|
||||
void quotaRetrieved(qint64 totalBytes, qint64 availableBytes);
|
||||
|
||||
private slots:
|
||||
virtual void finished();
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
#endif // NETWORKJOBS_H
|
||||
101
src/mirall/networklocation.cpp
Normal file
101
src/mirall/networklocation.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "mirall/networklocation.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
NetworkLocation::NetworkLocation(const QString &encoded)
|
||||
: _encoded(encoded)
|
||||
{
|
||||
}
|
||||
|
||||
NetworkLocation::NetworkLocation()
|
||||
{
|
||||
}
|
||||
|
||||
NetworkLocation::~NetworkLocation()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* for now our data is just the MAC address of the default gateway
|
||||
*/
|
||||
NetworkLocation NetworkLocation::currentLocation()
|
||||
{
|
||||
QProcess ip;
|
||||
ip.start(QLatin1String("/sbin/ip"), QStringList() << QLatin1String("route"));
|
||||
|
||||
if (!ip.waitForStarted())
|
||||
return NetworkLocation();
|
||||
|
||||
if (!ip.waitForFinished())
|
||||
return NetworkLocation();
|
||||
|
||||
QByteArray gwIp;
|
||||
while (ip.canReadLine()) {
|
||||
QByteArray line = ip.readLine();
|
||||
if ( line.startsWith("default") ) { // krazy:exclude=strings
|
||||
QList<QByteArray> parts = line.split(' ');
|
||||
gwIp = parts[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gwIp.isEmpty())
|
||||
return NetworkLocation();
|
||||
|
||||
QProcess arp;
|
||||
arp.start(QLatin1String("/sbin/arp"), QStringList() << QLatin1String("-a"));
|
||||
|
||||
if (!arp.waitForStarted())
|
||||
return NetworkLocation();
|
||||
|
||||
if (!arp.waitForFinished())
|
||||
return NetworkLocation();
|
||||
|
||||
QByteArray gwMAC;
|
||||
while (arp.canReadLine()) {
|
||||
QByteArray line = arp.readLine();
|
||||
if (line.contains(gwIp)) {
|
||||
QList<QByteArray> parts = line.split(' ');
|
||||
gwMAC = parts[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gwMAC.isEmpty())
|
||||
return NetworkLocation();
|
||||
|
||||
return NetworkLocation(QString::fromLatin1(gwMAC));
|
||||
}
|
||||
|
||||
|
||||
NetworkLocation::Proximity NetworkLocation::compareWith(const NetworkLocation &location) const
|
||||
{
|
||||
if (location.encoded().isEmpty() || encoded().isEmpty())
|
||||
return Unknown;
|
||||
if (location.encoded() == encoded())
|
||||
return Same;
|
||||
return Different;
|
||||
}
|
||||
|
||||
QString NetworkLocation::encoded() const
|
||||
{
|
||||
return _encoded;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
57
src/mirall/networklocation.h
Normal file
57
src/mirall/networklocation.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_NETWORK_LOCATION_H
|
||||
#define MIRALL_NETWORK_LOCATION_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class NetworkLocation
|
||||
{
|
||||
public:
|
||||
|
||||
enum Proximity {
|
||||
Unknown,
|
||||
Same,
|
||||
Different
|
||||
};
|
||||
|
||||
/**
|
||||
* constructs a location from its encoded
|
||||
* form
|
||||
*/
|
||||
NetworkLocation(const QString &encoded);
|
||||
|
||||
/**
|
||||
* Unknown location
|
||||
*/
|
||||
NetworkLocation();
|
||||
|
||||
~NetworkLocation();
|
||||
|
||||
QString encoded() const;
|
||||
|
||||
static NetworkLocation currentLocation();
|
||||
|
||||
Proximity compareWith(const NetworkLocation &location) const;
|
||||
private:
|
||||
QString _encoded;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <QString>
|
||||
#include <QDomElement>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QtXml/QXmlStreamWriter>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
|
||||
@@ -1,528 +0,0 @@
|
||||
/*
|
||||
* 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 "mirall/application.h"
|
||||
#include "mirall/owncloudgui.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/owncloudsetupwizard.h"
|
||||
#include "mirall/settingsdialog.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/logbrowser.h"
|
||||
#include "mirall/account.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
#include <QSignalMapper>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
ownCloudGui::ownCloudGui(Application *parent) :
|
||||
QObject(parent),
|
||||
_tray(0),
|
||||
_settingsDialog(0),
|
||||
_logBrowser(0),
|
||||
_contextMenu(0),
|
||||
_recentActionsMenu(0),
|
||||
_quotaInfo(0),
|
||||
_folderOpenActionMapper(new QSignalMapper(this)),
|
||||
_recentItemsMapper(new QSignalMapper(this)),
|
||||
_app(parent)
|
||||
{
|
||||
_tray = new Systray();
|
||||
_tray->setParent(this);
|
||||
_tray->setIcon( Theme::instance()->syncStateIcon( SyncResult::NotYetStarted, true ) );
|
||||
|
||||
connect(_tray.data(), SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
SLOT(slotTrayClicked(QSystemTrayIcon::ActivationReason)));
|
||||
|
||||
setupActions();
|
||||
setupContextMenu();
|
||||
|
||||
_tray->show();
|
||||
|
||||
/* use a signal mapper to map the open requests to the alias names */
|
||||
connect(_folderOpenActionMapper, SIGNAL(mapped(QString)),
|
||||
this, SLOT(slotFolderOpenAction(QString)));
|
||||
|
||||
connect(_recentItemsMapper, SIGNAL(mapped(QString)),
|
||||
this, SLOT(slotOpenPath(QString)));
|
||||
|
||||
ProgressDispatcher *pd = ProgressDispatcher::instance();
|
||||
connect( pd, SIGNAL(progressInfo(QString,Progress::Info)), this,
|
||||
SLOT(slotUpdateProgress(QString,Progress::Info)) );
|
||||
connect( pd, SIGNAL(progressSyncProblem(QString,Progress::SyncProblem)),
|
||||
SLOT(slotProgressSyncProblem(QString,Progress::SyncProblem)));
|
||||
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
connect( folderMan, SIGNAL(folderSyncStateChange(QString)),
|
||||
this,SLOT(slotSyncStateChange(QString)));
|
||||
|
||||
connect( Logger::instance(), SIGNAL(guiLog(QString,QString)),
|
||||
SLOT(slotShowTrayMessage(QString,QString)));
|
||||
connect( Logger::instance(), SIGNAL(optionalGuiLog(QString,QString)),
|
||||
SLOT(slotShowOptionalTrayMessage(QString,QString)));
|
||||
connect( Logger::instance(), SIGNAL(guiMessage(QString,QString)),
|
||||
SLOT(slotShowGuiMessage(QString,QString)));
|
||||
}
|
||||
|
||||
// This should rather be in application.... or rather in MirallConfigFile?
|
||||
void ownCloudGui::slotOpenSettingsDialog( bool openSettings )
|
||||
{
|
||||
// if account is set up, start the configuration wizard.
|
||||
if( AccountManager::instance()->account() ) {
|
||||
if( openSettings ) {
|
||||
if (_settingsDialog.isNull()) {
|
||||
slotShowSettings();
|
||||
} else {
|
||||
_settingsDialog->close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qDebug() << "No configured folders yet, starting setup wizard";
|
||||
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)));
|
||||
}
|
||||
}
|
||||
|
||||
QuotaInfo *ownCloudGui::quotaInfo() const
|
||||
{
|
||||
return _quotaInfo;
|
||||
}
|
||||
|
||||
void ownCloudGui::slotTrayClicked( QSystemTrayIcon::ActivationReason reason )
|
||||
{
|
||||
// A click on the tray icon should only open the status window on Win and
|
||||
// Linux, not on Mac. They want a menu entry.
|
||||
#if !defined Q_OS_MAC
|
||||
if( reason == QSystemTrayIcon::Trigger ) {
|
||||
slotOpenSettingsDialog(true); // start settings if config is existing.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ownCloudGui::slotSyncStateChange( const QString& alias )
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
const SyncResult& result = folderMan->syncResult( alias );
|
||||
|
||||
slotComputeOverallSyncStatus();
|
||||
|
||||
qDebug() << "Sync state changed for folder " << alias << ": " << result.statusString();
|
||||
|
||||
// Promote sync result to settings-dialog for sync protocol?
|
||||
// if( _progressDialog ) {
|
||||
// _progressDialog->setSyncResult(result);
|
||||
// }
|
||||
if (result.status() == SyncResult::Success || result.status() == SyncResult::Error) {
|
||||
Logger::instance()->enterNextLogFile();
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::slotFoldersChanged()
|
||||
{
|
||||
slotComputeOverallSyncStatus();
|
||||
setupContextMenu();
|
||||
}
|
||||
|
||||
void ownCloudGui::slotOpenPath(const QString &path)
|
||||
{
|
||||
Utility::showInFileManager(path);
|
||||
}
|
||||
|
||||
void ownCloudGui::slotAccountStateChanged()
|
||||
{
|
||||
setupContextMenu();
|
||||
}
|
||||
|
||||
void ownCloudGui::startupConnected( bool connected, const QStringList& fails )
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
if( connected ) {
|
||||
qDebug() << "######## connected to ownCloud Server!";
|
||||
folderMan->setSyncEnabled(true);
|
||||
_tray->setIcon( Theme::instance()->syncStateIcon( SyncResult::NotYetStarted, true ) );
|
||||
_tray->show();
|
||||
}
|
||||
|
||||
_startupFails = fails; // store that for the settings dialog once it appears.
|
||||
|
||||
}
|
||||
|
||||
void ownCloudGui::slotComputeOverallSyncStatus()
|
||||
{
|
||||
if (Account *a = AccountManager::instance()->account()) {
|
||||
if (a->state() == Account::SignedOut) {
|
||||
_tray->setIcon(Theme::instance()->syncStateIcon( SyncResult::Unavailable, true));
|
||||
_tray->setToolTip(tr("Please sign in"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// display the info of the least successful sync (eg. not just display the result of the latest sync
|
||||
QString trayMessage;
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder::Map map = folderMan->map();
|
||||
SyncResult overallResult = FolderMan::accountStatus(map.values());
|
||||
|
||||
// if there have been startup problems, show an error message.
|
||||
if( !_settingsDialog.isNull() )
|
||||
_settingsDialog->setGeneralErrors( _startupFails );
|
||||
|
||||
if( !_startupFails.isEmpty() ) {
|
||||
trayMessage = _startupFails.join(QLatin1String("\n"));
|
||||
QIcon statusIcon;
|
||||
if (_app->_startupNetworkError) {
|
||||
statusIcon = Theme::instance()->syncStateIcon( SyncResult::NotYetStarted, true );
|
||||
} else {
|
||||
statusIcon = Theme::instance()->syncStateIcon( SyncResult::Error, true );
|
||||
}
|
||||
|
||||
_tray->setIcon( statusIcon );
|
||||
_tray->setToolTip(trayMessage);
|
||||
} else {
|
||||
// create the tray blob message, check if we have an defined state
|
||||
if( overallResult.status() != SyncResult::Undefined ) {
|
||||
QStringList allStatusStrings;
|
||||
foreach(Folder* folder, map.values()) {
|
||||
qDebug() << "Folder in overallStatus Message: " << folder << " with name " << folder->alias();
|
||||
QString folderMessage = folderMan->statusToString(folder->syncResult().status(), folder->syncEnabled());
|
||||
allStatusStrings += tr("Folder %1: %2").arg(folder->alias(), folderMessage);
|
||||
}
|
||||
|
||||
if( ! allStatusStrings.isEmpty() )
|
||||
trayMessage = allStatusStrings.join(QLatin1String("\n"));
|
||||
else
|
||||
trayMessage = tr("No sync folders configured.");
|
||||
|
||||
QIcon statusIcon = Theme::instance()->syncStateIcon( overallResult.status(), true);
|
||||
_tray->setIcon( statusIcon );
|
||||
_tray->setToolTip(trayMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::setupContextMenu()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
Account *a = AccountManager::instance()->account();
|
||||
|
||||
bool isConfigured = (a != 0);
|
||||
_actionOpenoC->setEnabled(isConfigured);
|
||||
bool isConnected = false;
|
||||
if (isConfigured) {
|
||||
isConnected = (a->state() == Account::Connected);
|
||||
}
|
||||
|
||||
if ( _contextMenu ) {
|
||||
_contextMenu->clear();
|
||||
_recentActionsMenu->clear();
|
||||
_recentActionsMenu->addAction(tr("None."));
|
||||
_recentActionsMenu->addAction(_actionRecent);
|
||||
} else {
|
||||
_contextMenu = new QMenu(_contextMenu);
|
||||
_recentActionsMenu = new QMenu(tr("Recent Changes"));
|
||||
// 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).
|
||||
_tray->setContextMenu(_contextMenu);
|
||||
}
|
||||
_contextMenu->setTitle(Theme::instance()->appNameGUI() );
|
||||
_contextMenu->addAction(_actionOpenoC);
|
||||
|
||||
int folderCnt = folderMan->map().size();
|
||||
// add open actions for all sync folders to the tray menu
|
||||
if( Theme::instance()->singleSyncFolder() ) {
|
||||
// there should be exactly one folder. No sync-folder add action will be shown.
|
||||
QStringList li = folderMan->map().keys();
|
||||
if( li.size() == 1 ) {
|
||||
Folder *folder = folderMan->map().value(li.first());
|
||||
if( folder ) {
|
||||
// if there is singleFolder mode, a generic open action is displayed.
|
||||
QAction *action = new QAction( tr("Open %1 folder").arg(Theme::instance()->appNameGUI()), this);
|
||||
connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
|
||||
_folderOpenActionMapper->setMapping( action, folder->alias() );
|
||||
|
||||
_contextMenu->addAction(action);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// show a grouping with more than one folder.
|
||||
if ( folderCnt > 1) {
|
||||
_contextMenu->addAction(tr("Managed Folders:"))->setDisabled(true);
|
||||
}
|
||||
foreach (Folder *folder, folderMan->map() ) {
|
||||
QAction *action = new QAction( tr("Open folder '%1'").arg(folder->alias()), this );
|
||||
connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
|
||||
_folderOpenActionMapper->setMapping( action, folder->alias() );
|
||||
|
||||
_contextMenu->addAction(action);
|
||||
}
|
||||
}
|
||||
_contextMenu->addSeparator();
|
||||
|
||||
if (isConfigured && isConnected) {
|
||||
_contextMenu->addAction(_actionQuota);
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionStatus);
|
||||
_contextMenu->addMenu(_recentActionsMenu);
|
||||
_contextMenu->addSeparator();
|
||||
}
|
||||
_contextMenu->addAction(_actionSettings);
|
||||
if (!Theme::instance()->helpUrl().isEmpty()) {
|
||||
_contextMenu->addAction(_actionHelp);
|
||||
}
|
||||
_contextMenu->addSeparator();
|
||||
if (isConfigured && isConnected) {
|
||||
_contextMenu->addAction(_actionLogout);
|
||||
} else {
|
||||
_contextMenu->addAction(_actionLogin);
|
||||
}
|
||||
_contextMenu->addAction(_actionQuit);
|
||||
|
||||
// Populate once at start
|
||||
slotRebuildRecentMenus();
|
||||
}
|
||||
|
||||
|
||||
void ownCloudGui::slotShowTrayMessage(const QString &title, const QString &msg)
|
||||
{
|
||||
if( _tray )
|
||||
_tray->showMessage(title, msg);
|
||||
else
|
||||
qDebug() << "Tray not ready: " << msg;
|
||||
}
|
||||
|
||||
void ownCloudGui::slotShowOptionalTrayMessage(const QString &title, const QString &msg)
|
||||
{
|
||||
MirallConfigFile cfg;
|
||||
if (cfg.optionalDesktopNotifications()) {
|
||||
slotShowTrayMessage(title, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* open the folder with the given Alais
|
||||
*/
|
||||
void ownCloudGui::slotFolderOpenAction( const QString& alias )
|
||||
{
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
qDebug() << "opening local url " << f->path();
|
||||
if( f ) {
|
||||
QUrl url(f->path(), QUrl::TolerantMode);
|
||||
url.setScheme( QLatin1String("file") );
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = f->path();
|
||||
|
||||
if (filePath.startsWith(QLatin1String("\\\\")) || filePath.startsWith(QLatin1String("//")))
|
||||
url.setUrl(QDir::toNativeSeparators(filePath));
|
||||
else
|
||||
url = QUrl::fromLocalFile(filePath);
|
||||
#endif
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::setupActions()
|
||||
{
|
||||
_actionOpenoC = new QAction(tr("Open %1 in browser").arg(Theme::instance()->appNameGUI()), this);
|
||||
QObject::connect(_actionOpenoC, SIGNAL(triggered(bool)), SLOT(slotOpenOwnCloud()));
|
||||
_actionQuota = new QAction(tr("Calculating quota..."), this);
|
||||
_actionQuota->setEnabled( false );
|
||||
_actionStatus = new QAction(tr("Unknown status"), this);
|
||||
_actionStatus->setEnabled( false );
|
||||
_actionSettings = new QAction(tr("Settings..."), this);
|
||||
_actionRecent = new QAction(tr("Details..."), this);
|
||||
_actionRecent->setEnabled( true );
|
||||
|
||||
QObject::connect(_actionRecent, SIGNAL(triggered(bool)), SLOT(slotShowSyncProtocol()));
|
||||
QObject::connect(_actionSettings, SIGNAL(triggered(bool)), SLOT(slotShowSettings()));
|
||||
_actionHelp = new QAction(tr("Help"), this);
|
||||
QObject::connect(_actionHelp, SIGNAL(triggered(bool)), SLOT(slotHelp()));
|
||||
_actionQuit = new QAction(tr("Quit %1").arg(Theme::instance()->appNameGUI()), this);
|
||||
QObject::connect(_actionQuit, SIGNAL(triggered(bool)), _app, SLOT(quit()));
|
||||
|
||||
_actionLogin = new QAction(tr("Sign in..."), this);
|
||||
connect(_actionLogin, SIGNAL(triggered()), _app, SLOT(slotLogin()));
|
||||
_actionLogout = new QAction(tr("Sign out"), this);
|
||||
connect(_actionLogout, SIGNAL(triggered()), _app, SLOT(slotLogout()));
|
||||
|
||||
_quotaInfo = new QuotaInfo(this);
|
||||
connect(_quotaInfo, SIGNAL(quotaUpdated(qint64,qint64)), SLOT(slotRefreshQuotaDisplay(qint64,qint64)));
|
||||
}
|
||||
|
||||
void ownCloudGui::slotRefreshQuotaDisplay( qint64 total, qint64 used )
|
||||
{
|
||||
if (total == 0) {
|
||||
_actionQuota->setText(tr("Quota n/a"));
|
||||
return;
|
||||
}
|
||||
|
||||
double percent = used/(double)total*100;
|
||||
QString percentFormatted = Utility::compactFormatDouble(percent, 1);
|
||||
QString totalFormatted = Utility::octetsToString(total);
|
||||
_actionQuota->setText(tr("%1% of %2 in use").arg(percentFormatted).arg(totalFormatted));
|
||||
}
|
||||
|
||||
void ownCloudGui::slotProgressSyncProblem(const QString& folder, const Progress::SyncProblem& problem)
|
||||
{
|
||||
Q_UNUSED(folder);
|
||||
Q_UNUSED(problem);
|
||||
|
||||
// display a warn icon if warnings happend.
|
||||
QIcon warnIcon(":/mirall/resources/warning-16");
|
||||
_actionRecent->setIcon(warnIcon);
|
||||
|
||||
slotRebuildRecentMenus();
|
||||
}
|
||||
|
||||
void ownCloudGui::slotRebuildRecentMenus()
|
||||
{
|
||||
_recentActionsMenu->clear();
|
||||
const QList<Progress::Info>& progressInfoList = ProgressDispatcher::instance()->recentChangedItems(5);
|
||||
|
||||
if( progressInfoList.size() == 0 ) {
|
||||
_recentActionsMenu->addAction(tr("No items synced recently"))->setEnabled(false);
|
||||
} else {
|
||||
QListIterator<Progress::Info> i(progressInfoList);
|
||||
|
||||
while(i.hasNext()) {
|
||||
Progress::Info info = i.next();
|
||||
QString kindStr = Progress::asResultString(info);
|
||||
QString timeStr = info.timestamp.toString("hh:mm");
|
||||
|
||||
QString actionText = tr("%1 (%2, %3)").arg(info.current_file).arg(kindStr).arg(timeStr);
|
||||
QAction *action = _recentActionsMenu->addAction( actionText );
|
||||
|
||||
Folder *folder = FolderMan::instance()->folder(info.folder);
|
||||
if (folder) {
|
||||
QString fullPath = folder->path() + '/' + info.current_file;
|
||||
if (QFile(fullPath).exists()) {
|
||||
_recentItemsMapper->setMapping(action, fullPath);
|
||||
connect(action, SIGNAL(triggered()), _recentItemsMapper, SLOT(map()));
|
||||
} else {
|
||||
action->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// add a more... entry.
|
||||
_recentActionsMenu->addSeparator();
|
||||
_recentActionsMenu->addAction(_actionRecent);
|
||||
}
|
||||
|
||||
|
||||
void ownCloudGui::slotUpdateProgress(const QString &folder, const Progress::Info& progress)
|
||||
{
|
||||
Q_UNUSED(folder);
|
||||
|
||||
// shows an entry in the context menu.
|
||||
QString curAmount = Utility::octetsToString(progress.overall_current_bytes);
|
||||
QString totalAmount = Utility::octetsToString(progress.overall_transmission_size);
|
||||
_actionStatus->setText(tr("Syncing %1 of %2 (%3 of %4) ").arg(progress.current_file_no)
|
||||
.arg(progress.overall_file_count).arg(curAmount, totalAmount));
|
||||
|
||||
// wipe the problem list at start of sync.
|
||||
if( progress.kind == Progress::StartSync ) {
|
||||
_actionRecent->setIcon( QIcon() ); // Fixme: Set a "in-progress"-item eventually.
|
||||
}
|
||||
|
||||
// If there was a change in the file list, redo the progress menu.
|
||||
if( progress.kind == Progress::EndDownload || progress.kind == Progress::EndUpload ||
|
||||
progress.kind == Progress::EndDelete || progress.kind == Progress::EndRename ) {
|
||||
slotRebuildRecentMenus();
|
||||
}
|
||||
|
||||
if (progress.kind == Progress::EndSync) {
|
||||
slotRebuildRecentMenus(); // show errors.
|
||||
QTimer::singleShot(2000, this, SLOT(slotDisplayIdle()));
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::slotDisplayIdle()
|
||||
{
|
||||
_actionStatus->setText(tr("Up to date"));
|
||||
}
|
||||
|
||||
void ownCloudGui::slotShowGuiMessage(const QString &title, const QString &message)
|
||||
{
|
||||
QMessageBox *msgBox = new QMessageBox;
|
||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
msgBox->setText(message);
|
||||
msgBox->setWindowTitle(title);
|
||||
msgBox->setIcon(QMessageBox::Information);
|
||||
msgBox->open();
|
||||
}
|
||||
|
||||
void ownCloudGui::slotShowSettings()
|
||||
{
|
||||
if (_settingsDialog.isNull()) {
|
||||
_settingsDialog = new SettingsDialog(this);
|
||||
_settingsDialog->setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
_settingsDialog->show();
|
||||
}
|
||||
|
||||
_settingsDialog->setGeneralErrors( _startupFails );
|
||||
Utility::raiseDialog(_settingsDialog.data());
|
||||
_settingsDialog->slotRefreshResultList();
|
||||
}
|
||||
|
||||
void ownCloudGui::slotShowSyncProtocol()
|
||||
{
|
||||
slotShowSettings();
|
||||
_settingsDialog->showActivityPage();
|
||||
}
|
||||
|
||||
|
||||
void ownCloudGui::slotShutdown()
|
||||
{
|
||||
// those do delete on close
|
||||
if (!_settingsDialog.isNull()) _settingsDialog->close();
|
||||
if (!_logBrowser.isNull()) _logBrowser->deleteLater();
|
||||
}
|
||||
|
||||
void ownCloudGui::slotToggleLogBrowser()
|
||||
{
|
||||
if (_logBrowser.isNull()) {
|
||||
// init the log browser.
|
||||
_logBrowser = new LogBrowser;
|
||||
// ## TODO: allow new log name maybe?
|
||||
}
|
||||
|
||||
if (_logBrowser->isVisible() ) {
|
||||
_logBrowser->hide();
|
||||
} else {
|
||||
Utility::raiseDialog(_logBrowser);
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::slotOpenOwnCloud()
|
||||
{
|
||||
if (Account *account = AccountManager::instance()->account()) {
|
||||
QDesktopServices::openUrl(account->url());
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::slotHelp()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(Theme::instance()->helpUrl()));
|
||||
}
|
||||
|
||||
|
||||
} // end namespace
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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 OWNCLOUDGUI_H
|
||||
#define OWNCLOUDGUI_H
|
||||
|
||||
#include "mirall/systray.h"
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/quotainfo.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QSignalMapper>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class SettingsDialog;
|
||||
class Application;
|
||||
class LogBrowser;
|
||||
|
||||
class ownCloudGui : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ownCloudGui(Application *parent = 0);
|
||||
|
||||
void setupContextMenu();
|
||||
void startupConnected(bool connected , const QStringList &fails);
|
||||
|
||||
bool checkAccountExists(bool openSettings);
|
||||
|
||||
QuotaInfo *quotaInfo() const;
|
||||
|
||||
signals:
|
||||
void setupProxy();
|
||||
|
||||
public slots:
|
||||
void slotComputeOverallSyncStatus();
|
||||
void slotShowTrayMessage(const QString &title, const QString &msg);
|
||||
void slotShowOptionalTrayMessage(const QString &title, const QString &msg);
|
||||
void slotFolderOpenAction( const QString& alias );
|
||||
void slotRefreshQuotaDisplay( qint64 total, qint64 used );
|
||||
void slotRebuildRecentMenus();
|
||||
void slotProgressSyncProblem(const QString& folder, const Progress::SyncProblem& problem);
|
||||
void slotUpdateProgress(const QString &folder, const Progress::Info& progress);
|
||||
void slotShowGuiMessage(const QString &title, const QString &message);
|
||||
void slotFoldersChanged();
|
||||
void slotShowSettings();
|
||||
void slotShowSyncProtocol();
|
||||
void slotShutdown();
|
||||
void slotSyncStateChange( const QString& alias );
|
||||
void slotTrayClicked( QSystemTrayIcon::ActivationReason reason );
|
||||
void slotToggleLogBrowser();
|
||||
void slotOpenOwnCloud();
|
||||
void slotOpenSettingsDialog( bool openSettings );
|
||||
void slotHelp();
|
||||
void slotOpenPath(const QString& path);
|
||||
void slotAccountStateChanged();
|
||||
|
||||
private slots:
|
||||
void slotDisplayIdle();
|
||||
|
||||
private:
|
||||
void setupActions();
|
||||
|
||||
QPointer<Systray> _tray;
|
||||
QPointer<SettingsDialog> _settingsDialog;
|
||||
QPointer<LogBrowser>_logBrowser;
|
||||
// tray's menu
|
||||
QMenu *_contextMenu;
|
||||
QMenu *_recentActionsMenu;
|
||||
|
||||
QAction *_actionLogin;
|
||||
QAction *_actionLogout;
|
||||
|
||||
QAction *_actionOpenoC;
|
||||
QAction *_actionSettings;
|
||||
QAction *_actionQuota;
|
||||
QAction *_actionStatus;
|
||||
QAction *_actionRecent;
|
||||
QAction *_actionHelp;
|
||||
QAction *_actionQuit;
|
||||
|
||||
QuotaInfo *_quotaInfo;
|
||||
|
||||
QSignalMapper *_folderOpenActionMapper;
|
||||
QSignalMapper *_recentItemsMapper;
|
||||
|
||||
Application *_app;
|
||||
|
||||
QStringList _startupFails;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
#endif // OWNCLOUDGUI_H
|
||||
632
src/mirall/owncloudinfo.cpp
Normal file
632
src/mirall/owncloudinfo.cpp
Normal file
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <QAuthenticator>
|
||||
|
||||
#define DEFAULT_CONNECTION QLatin1String("default");
|
||||
static const char WEBDAV_PATH[] = "remote.php/webdav/";
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
ownCloudInfo *ownCloudInfo::_instance = 0;
|
||||
|
||||
ownCloudInfo* ownCloudInfo::instance()
|
||||
{
|
||||
static QMutex mutex;
|
||||
if (!_instance)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
if (!_instance) {
|
||||
_instance = new ownCloudInfo;
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
ownCloudInfo::ownCloudInfo() :
|
||||
QObject(0),
|
||||
_manager(0),
|
||||
_authAttempts(0),
|
||||
_lastQuotaUsedBytes(0),
|
||||
_lastQuotaTotalBytes(0)
|
||||
{
|
||||
_connection = Theme::instance()->appName();
|
||||
connect(this, SIGNAL(guiLog(QString,QString)),
|
||||
Logger::instance(), SIGNAL(guiLog(QString,QString)));
|
||||
// this will set credentials specific qnam
|
||||
setCustomConfigHandle(QString());
|
||||
}
|
||||
|
||||
void ownCloudInfo::setNetworkAccessManager( QNetworkAccessManager* qnam )
|
||||
{
|
||||
delete _manager;
|
||||
qnam->setParent( this );
|
||||
_manager = qnam;
|
||||
|
||||
MirallConfigFile cfg( _configHandle );
|
||||
QSslSocket::addDefaultCaCertificates(QSslCertificate::fromData(cfg.caCerts()));
|
||||
|
||||
connect( _manager, SIGNAL( sslErrors(QNetworkReply*, QList<QSslError>)),
|
||||
this, SIGNAL(sslFailed(QNetworkReply*, QList<QSslError>)) );
|
||||
|
||||
_certsUntrusted = false;
|
||||
}
|
||||
|
||||
ownCloudInfo::~ownCloudInfo()
|
||||
{
|
||||
}
|
||||
|
||||
void ownCloudInfo::setCustomConfigHandle( const QString& handle )
|
||||
{
|
||||
_configHandle = handle;
|
||||
_authAttempts = 0; // allow a couple of tries again.
|
||||
resetSSLUntrust();
|
||||
MirallConfigFile cfg(_configHandle);
|
||||
setNetworkAccessManager (cfg.getCredentials()->getQNAM());
|
||||
}
|
||||
|
||||
bool ownCloudInfo::isConfigured()
|
||||
{
|
||||
MirallConfigFile cfgFile( _configHandle );
|
||||
return cfgFile.connectionExists( _connection );
|
||||
}
|
||||
|
||||
QNetworkReply *ownCloudInfo::checkInstallation()
|
||||
{
|
||||
_redirectCount = 0;
|
||||
MirallConfigFile cfgFile( _configHandle );
|
||||
QUrl url ( cfgFile.ownCloudUrl( _connection ) + QLatin1String("status.php") );
|
||||
/* No authentication required for this. */
|
||||
return getRequest(url);
|
||||
}
|
||||
|
||||
QNetworkReply* ownCloudInfo::getWebDAVPath( const QString& path )
|
||||
{
|
||||
_redirectCount = 0;
|
||||
QUrl url ( webdavUrl( _connection ) + path );
|
||||
QNetworkReply *reply = getRequest(url);
|
||||
_directories[reply] = path;
|
||||
return reply;
|
||||
}
|
||||
|
||||
QNetworkReply* ownCloudInfo::getRequest( const QUrl& url )
|
||||
{
|
||||
qDebug() << "Get Request to " << url;
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setUrl( url );
|
||||
setupHeaders( request, 0 );
|
||||
|
||||
QNetworkReply *reply = _manager->get( request );
|
||||
connect( reply, SIGNAL(finished()), SLOT(slotReplyFinished()));
|
||||
|
||||
if( !_configHandle.isEmpty() ) {
|
||||
qDebug() << "Setting config handle " << _configHandle;
|
||||
_configHandleMap[reply] = _configHandle;
|
||||
}
|
||||
|
||||
connect( reply, SIGNAL( error(QNetworkReply::NetworkError )),
|
||||
this, SLOT(slotError( QNetworkReply::NetworkError )));
|
||||
return reply;
|
||||
}
|
||||
|
||||
QNetworkReply* ownCloudInfo::mkdirRequest( const QString& dir )
|
||||
{
|
||||
qDebug() << "OCInfo Making dir " << dir;
|
||||
_authAttempts = 0;
|
||||
QNetworkRequest req;
|
||||
QUrl url( webdavUrl(_connection) );
|
||||
// ensure #, ? and co are interpreted as part of the path and nothing else
|
||||
url.setEncodedPath(url.encodedPath()+QUrl::toPercentEncoding(dir, "/"));
|
||||
|
||||
req.setUrl( url );
|
||||
QNetworkReply *reply = davRequest("MKCOL", req, 0);
|
||||
|
||||
// remember the confighandle used for this request
|
||||
if( ! _configHandle.isEmpty() )
|
||||
qDebug() << "Setting config handle " << _configHandle;
|
||||
_configHandleMap[reply] = _configHandle;
|
||||
|
||||
if( reply->error() != QNetworkReply::NoError ) {
|
||||
qDebug() << "mkdir request network error: " << reply->errorString();
|
||||
}
|
||||
|
||||
connect( reply, SIGNAL(finished()), SLOT(slotMkdirFinished()) );
|
||||
connect( reply, SIGNAL( error(QNetworkReply::NetworkError )),
|
||||
this, SLOT(slotError(QNetworkReply::NetworkError )));
|
||||
return reply;
|
||||
}
|
||||
|
||||
QNetworkReply* ownCloudInfo::getQuotaRequest( const QString& dir )
|
||||
{
|
||||
QNetworkRequest req;
|
||||
req.setUrl( QUrl( webdavUrl(_connection) + QUrl::toPercentEncoding(dir, "/") ) );
|
||||
req.setRawHeader("Depth", "0");
|
||||
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
||||
"<d:propfind xmlns:d=\"DAV:\">\n"
|
||||
" <d:prop>\n"
|
||||
" <d:quota-available-bytes/>\n"
|
||||
" <d:quota-used-bytes/>\n"
|
||||
" <d:getetag/>"
|
||||
" </d:prop>\n"
|
||||
"</d:propfind>\n");
|
||||
QBuffer *buf = new QBuffer;
|
||||
buf->setData(xml);
|
||||
buf->open(QIODevice::ReadOnly);
|
||||
QNetworkReply *reply = davRequest("PROPFIND", req, buf);
|
||||
buf->setParent(reply);
|
||||
|
||||
if( reply->error() != QNetworkReply::NoError ) {
|
||||
qDebug() << "getting quota: request network error: " << reply->errorString();
|
||||
}
|
||||
|
||||
connect( reply, SIGNAL( finished()), SLOT(slotGetQuotaFinished()) );
|
||||
connect( reply, SIGNAL( error(QNetworkReply::NetworkError)),
|
||||
this, SLOT( slotError(QNetworkReply::NetworkError)));
|
||||
return reply;
|
||||
}
|
||||
QNetworkReply* ownCloudInfo::getDirectoryListing( const QString& dir )
|
||||
{
|
||||
QNetworkRequest req;
|
||||
QUrl url( webdavUrl(_connection) );
|
||||
// ensure #, ? and co are interpreted as part of the path and nothing else
|
||||
url.setPath(url.path() + dir );
|
||||
req.setUrl( url );
|
||||
req.setRawHeader("Depth", "1");
|
||||
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
||||
"<d:propfind xmlns:d=\"DAV:\">\n"
|
||||
" <d:prop>\n"
|
||||
" <d:resourcetype/>\n"
|
||||
" </d:prop>\n"
|
||||
"</d:propfind>\n");
|
||||
QBuffer *buf = new QBuffer;
|
||||
buf->setData(xml);
|
||||
buf->open(QIODevice::ReadOnly);
|
||||
QNetworkReply *reply = davRequest("PROPFIND", req, buf);
|
||||
buf->setParent(reply);
|
||||
|
||||
if( reply->error() != QNetworkReply::NoError ) {
|
||||
qDebug() << "getting quota: request network error: " << reply->errorString();
|
||||
}
|
||||
|
||||
connect( reply, SIGNAL( finished()), SLOT(slotGetDirectoryListingFinished()) );
|
||||
connect( reply, SIGNAL( error(QNetworkReply::NetworkError)),
|
||||
this, SLOT( slotError(QNetworkReply::NetworkError)));
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
void ownCloudInfo::slotMkdirFinished()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
|
||||
if( ! reply ) {
|
||||
qDebug() << "ownCloudInfo: Reply empty!";
|
||||
return;
|
||||
}
|
||||
|
||||
emit webdavColCreated( reply->error() );
|
||||
qDebug() << "mkdir slot hit with status: " << reply->error();
|
||||
if( _configHandleMap.contains( reply ) ) {
|
||||
_configHandleMap.remove( reply );
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void ownCloudInfo::slotGetQuotaFinished()
|
||||
{
|
||||
bool ok = false;
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
int http_result_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (http_result_code == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(reply);
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
|
||||
qint64 quotaUsedBytes = 0;
|
||||
qint64 quotaAvailableBytes = 0;
|
||||
QString etag;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
QString name = reader.name().toString();
|
||||
if (name == QLatin1String("quota-used-bytes")) {
|
||||
quotaUsedBytes = reader.readElementText().toLongLong(&ok);
|
||||
if (!ok) quotaUsedBytes = 0;
|
||||
} else if (name == QLatin1String("quota-available-bytes")) {
|
||||
quotaAvailableBytes = reader.readElementText().toLongLong(&ok);
|
||||
if (!ok) quotaAvailableBytes = 0;
|
||||
} else if (name == QLatin1String("getetag")) {
|
||||
etag = reader.readElementText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qint64 total = quotaUsedBytes + quotaAvailableBytes;
|
||||
|
||||
_lastQuotaTotalBytes = total;
|
||||
_lastQuotaUsedBytes = quotaUsedBytes;
|
||||
emit quotaUpdated(total, quotaUsedBytes);
|
||||
_lastEtag = etag;
|
||||
} else {
|
||||
qDebug() << "Quota request *not* successful, http result code is " << http_result_code;
|
||||
_lastQuotaTotalBytes = 0;
|
||||
_lastQuotaUsedBytes = 0;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void ownCloudInfo::slotGetDirectoryListingFinished()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
|
||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(reply);
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
|
||||
QStringList folders;
|
||||
QString currentItem;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
QString name = reader.name().toString();
|
||||
if (name == QLatin1String("href")) {
|
||||
currentItem = reader.readElementText();
|
||||
} else if (name == QLatin1String("collection") &&
|
||||
!currentItem.isEmpty()) {
|
||||
folders.append(QUrl::fromEncoded(currentItem.toLatin1()).path());
|
||||
currentItem.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
emit directoryListingUpdated(folders);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
QList<QNetworkCookie> ownCloudInfo::getLastAuthCookies()
|
||||
{
|
||||
QUrl url = QUrl( webdavUrl(_connection));
|
||||
QList<QNetworkCookie> cookies = _manager->cookieJar()->cookiesForUrl(url);
|
||||
return cookies;
|
||||
}
|
||||
|
||||
QString ownCloudInfo::configHandle(QNetworkReply *reply)
|
||||
{
|
||||
QString configHandle;
|
||||
if( _configHandleMap.contains(reply) ) {
|
||||
configHandle = _configHandleMap[reply];
|
||||
}
|
||||
return configHandle;
|
||||
}
|
||||
|
||||
QList<QSslCertificate> ownCloudInfo::certificateChain() const
|
||||
{
|
||||
QMutexLocker lock(const_cast<QMutex*>(&_certChainMutex));
|
||||
return _certificateChain;
|
||||
}
|
||||
|
||||
//
|
||||
// There have been problems with the finish-signal coming from the networkmanager.
|
||||
// To avoid that, the reply-signals were connected and the data is taken from the
|
||||
// sender() method.
|
||||
//
|
||||
void ownCloudInfo::slotReplyFinished()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
QSslConfiguration sslConfig = reply->sslConfiguration();
|
||||
if (!sslConfig.isNull()) {
|
||||
QMutexLocker lock(&_certChainMutex);
|
||||
_certificateChain = sslConfig.peerCertificateChain();
|
||||
}
|
||||
|
||||
if( ! reply ) {
|
||||
qDebug() << "ownCloudInfo: Reply empty!";
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect redirect url
|
||||
QUrl possibleRedirUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
/* We'll deduct if the redirection is valid in the redirectUrl function */
|
||||
|
||||
|
||||
if (!possibleRedirUrl.isEmpty() && _redirectCount++ > 10) {
|
||||
// Are we in a redirect loop
|
||||
qDebug() << "Redirect loop while redirecting to" << possibleRedirUrl;
|
||||
possibleRedirUrl.clear();
|
||||
}
|
||||
|
||||
if(!possibleRedirUrl.isEmpty()) {
|
||||
QString configHandle;
|
||||
|
||||
qDebug() << "Redirected to " << possibleRedirUrl;
|
||||
|
||||
// We'll do another request to the redirection url.
|
||||
// an empty config handle is ok for the default config.
|
||||
if( _configHandleMap.contains(reply) ) {
|
||||
configHandle = _configHandleMap[reply];
|
||||
qDebug() << "Redirect: Have a custom config handle: " << configHandle;
|
||||
}
|
||||
|
||||
QString path = _directories[reply];
|
||||
if (path.isEmpty()) {
|
||||
path = QLatin1String("status.php");
|
||||
} else {
|
||||
path.prepend( QLatin1String(WEBDAV_PATH) );
|
||||
}
|
||||
qDebug() << "This path was redirected: " << path;
|
||||
|
||||
QString newUrl = possibleRedirUrl.toString();
|
||||
if( !path.isEmpty() && newUrl.endsWith( path )) {
|
||||
// cut off the trailing path
|
||||
newUrl.chop( path.length() );
|
||||
_urlRedirectedTo = newUrl;
|
||||
qDebug() << "Updated url to" << newUrl;
|
||||
getRequest( possibleRedirUrl );
|
||||
} else {
|
||||
qDebug() << "WRN: Path is not part of the redirect URL. NO redirect.";
|
||||
}
|
||||
reply->deleteLater();
|
||||
_directories.remove(reply);
|
||||
_configHandleMap.remove(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: check if this is always the correct encoding
|
||||
const QString version = QString::fromUtf8( reply->readAll() );
|
||||
const QString url = reply->url().toString();
|
||||
QString plainUrl(url);
|
||||
plainUrl.remove( QLatin1String("/status.php"));
|
||||
|
||||
QString info( version );
|
||||
|
||||
if( url.endsWith( QLatin1String("status.php")) ) {
|
||||
// it was a call to status.php
|
||||
if( reply->error() == QNetworkReply::NoError && info.isEmpty() ) {
|
||||
// This seems to be a bit strange behaviour of QNetworkAccessManager.
|
||||
// It calls the finised slot multiple times but only the first read wins.
|
||||
// That happend when the code connected the finished signal of the manager.
|
||||
// It did not happen when the code connected to the reply finish signal.
|
||||
qDebug() << "WRN: NetworkReply with not content but also no error! " << reply;
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
qDebug() << "status.php returns: " << info << " " << reply->error() << " Reply: " << reply;
|
||||
if( info.contains(QLatin1String("installed"))
|
||||
&& info.contains(QLatin1String("version"))
|
||||
&& info.contains(QLatin1String("versionstring")) ) {
|
||||
info.remove(0,1); // remove first char which is a "{"
|
||||
info.remove(-1,1); // remove the last char which is a "}"
|
||||
QStringList li = info.split( QLatin1Char(',') );
|
||||
|
||||
QString versionStr;
|
||||
QString version;
|
||||
QString edition;
|
||||
|
||||
foreach ( const QString& infoString, li ) {
|
||||
QStringList touple = infoString.split( QLatin1Char(':'));
|
||||
QString key = touple[0];
|
||||
key.remove(QLatin1Char('"'));
|
||||
QString val = touple[1];
|
||||
val.remove(QLatin1Char('"'));
|
||||
|
||||
if( key == QLatin1String("versionstring") ) {
|
||||
// get the versionstring out.
|
||||
versionStr = val;
|
||||
} else if( key == QLatin1String( "version") ) {
|
||||
// get version out
|
||||
version = val;
|
||||
} else if( key == QLatin1String( "edition") ) {
|
||||
// get version out
|
||||
edition = val;
|
||||
} else if(key == QLatin1String("installed")) {
|
||||
// Silently ignoring "installed = true" information
|
||||
} else {
|
||||
qDebug() << "Unknown info from ownCloud status.php: "<< key << "=" << val;
|
||||
}
|
||||
}
|
||||
emit ownCloudInfoFound( plainUrl, versionStr, version, edition );
|
||||
} else {
|
||||
qDebug() << "No proper answer on " << url;
|
||||
|
||||
emit noOwncloudFound( reply );
|
||||
}
|
||||
} else {
|
||||
// it was a general GET request.
|
||||
QString dir(QLatin1String("unknown"));
|
||||
if( _directories.contains(reply) ) {
|
||||
dir = _directories[reply];
|
||||
}
|
||||
|
||||
emit ownCloudDirExists( dir, reply );
|
||||
}
|
||||
reply->deleteLater();
|
||||
_directories.remove(reply);
|
||||
_configHandleMap.remove(reply);
|
||||
}
|
||||
|
||||
void ownCloudInfo::resetSSLUntrust()
|
||||
{
|
||||
_certsUntrusted = false;
|
||||
}
|
||||
|
||||
void ownCloudInfo::setCertsUntrusted(bool donttrust)
|
||||
{
|
||||
_certsUntrusted = donttrust;
|
||||
}
|
||||
|
||||
bool ownCloudInfo::certsUntrusted()
|
||||
{
|
||||
return _certsUntrusted;
|
||||
}
|
||||
|
||||
void ownCloudInfo::slotError( QNetworkReply::NetworkError err)
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
qDebug() << "ownCloudInfo Network Error"
|
||||
<< err << ":" << reply->errorString();
|
||||
|
||||
switch (err) {
|
||||
case QNetworkReply::ProxyConnectionRefusedError:
|
||||
emit guiLog(tr("Proxy Refused Connection "),
|
||||
tr("The configured proxy has refused the connection. "
|
||||
"Please check the proxy settings."));
|
||||
break;
|
||||
case QNetworkReply::ProxyConnectionClosedError:
|
||||
emit guiLog(tr("Proxy Closed Connection"),
|
||||
tr("The configured proxy has closed the connection. "
|
||||
"Please check the proxy settings."));
|
||||
break;
|
||||
case QNetworkReply::ProxyNotFoundError:
|
||||
emit guiLog(tr("Proxy Not Found"),
|
||||
tr("The configured proxy could not be found. "
|
||||
"Please check the proxy settings."));
|
||||
break;
|
||||
case QNetworkReply::ProxyAuthenticationRequiredError:
|
||||
emit guiLog(tr("Proxy Authentication Error"),
|
||||
tr("The configured proxy requires login but the proxy credentials "
|
||||
"are invalid. Please check the proxy settings."));
|
||||
break;
|
||||
case QNetworkReply::ProxyTimeoutError:
|
||||
emit guiLog(tr("Proxy Connection Timed Out"),
|
||||
tr("The connection to the configured proxy has timed out."));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
void ownCloudInfo::setupHeaders( QNetworkRequest & req, quint64 size )
|
||||
{
|
||||
QUrl url( req.url() );
|
||||
qDebug() << "Setting up host header: " << url.host();
|
||||
|
||||
if (size) {
|
||||
req.setHeader( QNetworkRequest::ContentLengthHeader, size);
|
||||
req.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String("text/xml; charset=utf-8"));
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkReply* ownCloudInfo::davRequest(const QByteArray& reqVerb, QNetworkRequest& req, QIODevice *data)
|
||||
{
|
||||
setupHeaders(req, quint64(data ? data->size() : 0));
|
||||
return _manager->sendCustomRequest(req, reqVerb, data );
|
||||
}
|
||||
|
||||
QString ownCloudInfo::webdavUrl(const QString &connection)
|
||||
{
|
||||
QString url;
|
||||
|
||||
if (!_urlRedirectedTo.isEmpty()) {
|
||||
url = _urlRedirectedTo.toString();
|
||||
} else {
|
||||
MirallConfigFile cfgFile(_configHandle );
|
||||
url = cfgFile.ownCloudUrl( connection );
|
||||
}
|
||||
url.append( QLatin1String( WEBDAV_PATH ) );
|
||||
if (!url.endsWith('/')) url.append('/');
|
||||
return url;
|
||||
}
|
||||
|
||||
RequestEtagJob::RequestEtagJob(const QString& dir, QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
QNetworkRequest req;
|
||||
req.setUrl( QUrl( ownCloudInfo::instance()->webdavUrl(ownCloudInfo::instance()->_connection) + dir ) );
|
||||
if (dir.isEmpty() || dir == "/") {
|
||||
/* For the root directory, we need to query the etags of all the sub directories
|
||||
* because, at the time I am writing this comment (Owncloud 5.0.9), the etag of the
|
||||
* root directory is not updated when the sub directories changes */
|
||||
req.setRawHeader("Depth", "1");
|
||||
} else {
|
||||
req.setRawHeader("Depth", "0");
|
||||
}
|
||||
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
||||
"<d:propfind xmlns:d=\"DAV:\">\n"
|
||||
" <d:prop>\n"
|
||||
" <d:getetag/>"
|
||||
" </d:prop>\n"
|
||||
"</d:propfind>\n");
|
||||
QBuffer *buf = new QBuffer;
|
||||
buf->setData(xml);
|
||||
buf->open(QIODevice::ReadOnly);
|
||||
_reply = ownCloudInfo::instance()->davRequest("PROPFIND", req, buf);
|
||||
buf->setParent(_reply);
|
||||
|
||||
if( _reply->error() != QNetworkReply::NoError ) {
|
||||
qDebug() << "getting etag: request network error: " << _reply->errorString();
|
||||
}
|
||||
|
||||
connect( _reply, SIGNAL( finished()), SLOT(slotFinished()) );
|
||||
connect( _reply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(slotError()));
|
||||
connect( _reply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
ownCloudInfo::instance(), SLOT(slotError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
||||
void RequestEtagJob::slotFinished()
|
||||
{
|
||||
if (_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(_reply);
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
QString etag;
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement &&
|
||||
reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
QString name = reader.name().toString();
|
||||
if (name == QLatin1String("getetag")) {
|
||||
etag += reader.readElementText();
|
||||
}
|
||||
}
|
||||
}
|
||||
emit etagRetreived(etag);
|
||||
}
|
||||
_reply->deleteLater();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void RequestEtagJob::slotError()
|
||||
{
|
||||
qDebug() << "RequestEtagJob Error: " << _reply->errorString();
|
||||
_reply->deleteLater();
|
||||
deleteLater();
|
||||
emit networkError();
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
186
src/mirall/owncloudinfo.h
Normal file
186
src/mirall/owncloudinfo.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef OWNCLOUDINFO_H
|
||||
#define OWNCLOUDINFO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class ownCloudInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
static ownCloudInfo *instance();
|
||||
|
||||
bool isConfigured();
|
||||
|
||||
/**
|
||||
* call status.php
|
||||
*/
|
||||
QNetworkReply* checkInstallation();
|
||||
|
||||
/**
|
||||
* convenience: GET request to the WebDAV server.
|
||||
*/
|
||||
QNetworkReply* getWebDAVPath( const QString& );
|
||||
|
||||
/**
|
||||
* There is a global flag here if the user once decided against trusting the
|
||||
* SSL connection. This method resets it so that the ssl dialog is shown again.
|
||||
*/
|
||||
void resetSSLUntrust();
|
||||
|
||||
/**
|
||||
* Set wether or not to trust errorneus SSL certificates
|
||||
*/
|
||||
void setCertsUntrusted(bool donttrust);
|
||||
|
||||
/**
|
||||
* Do we trust the certificate?.
|
||||
*/
|
||||
bool certsUntrusted();
|
||||
|
||||
/**
|
||||
* Set a NetworkAccessManager to be used
|
||||
*
|
||||
* This method will take ownership of the NetworkAccessManager, so you can just
|
||||
* set it initially and forget about its memory management.
|
||||
*/
|
||||
void setNetworkAccessManager( QNetworkAccessManager *qnam );
|
||||
|
||||
/**
|
||||
* Create a collection via owncloud. Provide a relative path.
|
||||
*/
|
||||
QNetworkReply* mkdirRequest( const QString& );
|
||||
|
||||
/**
|
||||
* Retrieve quota for a path. Provide a relative path.
|
||||
*/
|
||||
QNetworkReply* getQuotaRequest( const QString& );
|
||||
|
||||
/**
|
||||
* provide collections in a directory via owncloud. Provide a relative path.
|
||||
*/
|
||||
QNetworkReply* getDirectoryListing( const QString& dir );
|
||||
|
||||
/**
|
||||
* Use a custom ownCloud configuration file identified by handle
|
||||
*/
|
||||
void setCustomConfigHandle( const QString& );
|
||||
|
||||
/**
|
||||
* Accessor to the config handle.
|
||||
*/
|
||||
QString configHandle(QNetworkReply *reply = 0);
|
||||
|
||||
/**
|
||||
* Certificate chain of the connection est. with ownCloud.
|
||||
* Empty if the connection is HTTP-based
|
||||
*/
|
||||
QList<QSslCertificate> certificateChain() const;
|
||||
|
||||
/**
|
||||
* returns the owncloud webdav url.
|
||||
* It may be different from the one in the config if there was a HTTP redirection
|
||||
* The returned URL is guaranteed to end in a forward slash ('/')
|
||||
*/
|
||||
QString webdavUrl(const QString& connection = QString());
|
||||
|
||||
qint64 lastQuotaUsedBytes() const { return _lastQuotaUsedBytes; }
|
||||
qint64 lastQuotaTotalBytes() const { return _lastQuotaTotalBytes; }
|
||||
QString lastEtag() const { return _lastEtag; }
|
||||
|
||||
QList<QNetworkCookie> getLastAuthCookies();
|
||||
|
||||
signals:
|
||||
// result signal with url- and version string.
|
||||
void ownCloudInfoFound( const QString&, const QString&, const QString&, const QString& );
|
||||
void noOwncloudFound( QNetworkReply* );
|
||||
void ownCloudDirExists( const QString&, QNetworkReply* );
|
||||
|
||||
void webdavColCreated( QNetworkReply::NetworkError );
|
||||
void sslFailed( QNetworkReply *reply, QList<QSslError> errors );
|
||||
void guiLog( const QString& title, const QString& content );
|
||||
void quotaUpdated( qint64 total, qint64 quotaUsedBytes );
|
||||
void directoryListingUpdated(const QStringList &directories);
|
||||
|
||||
protected slots:
|
||||
void slotReplyFinished( );
|
||||
void slotError( QNetworkReply::NetworkError );
|
||||
|
||||
void slotMkdirFinished();
|
||||
void slotGetQuotaFinished();
|
||||
void slotGetDirectoryListingFinished();
|
||||
|
||||
private:
|
||||
explicit ownCloudInfo();
|
||||
|
||||
/**
|
||||
* a general GET request to the ownCloud WebDAV.
|
||||
*/
|
||||
QNetworkReply* getRequest( const QUrl &url);
|
||||
QNetworkReply* davRequest(const QByteArray&, QNetworkRequest&, QIODevice* );
|
||||
|
||||
~ownCloudInfo();
|
||||
|
||||
void setupHeaders(QNetworkRequest &req, quint64 size );
|
||||
|
||||
static ownCloudInfo *_instance;
|
||||
|
||||
QNetworkAccessManager *_manager;
|
||||
QString _connection;
|
||||
QString _configHandle;
|
||||
QUrl _urlRedirectedTo;
|
||||
QHash<QNetworkReply*, QString> _directories;
|
||||
QHash<QNetworkReply*, QString> _configHandleMap;
|
||||
QList<QSslCertificate> _certificateChain;
|
||||
bool _certsUntrusted;
|
||||
int _authAttempts;
|
||||
QMutex _certChainMutex;
|
||||
int _redirectCount;
|
||||
qint64 _lastQuotaUsedBytes;
|
||||
qint64 _lastQuotaTotalBytes;
|
||||
QString _lastEtag;
|
||||
|
||||
friend class RequestEtagJob;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class RequestEtagJob : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
QNetworkReply *_reply;
|
||||
|
||||
public:
|
||||
explicit RequestEtagJob(const QString &dir , QObject* parent = 0);
|
||||
|
||||
private slots:
|
||||
void slotFinished();
|
||||
void slotError();
|
||||
|
||||
signals:
|
||||
void etagRetreived(const QString &etag);
|
||||
void networkError();
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif // OWNCLOUDINFO_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Olivier Goffart <ogoffart@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef OWNCLOUDPROPAGATOR_H
|
||||
#define OWNCLOUDPROPAGATOR_H
|
||||
|
||||
#include <neon/ne_request.h>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <qelapsedtimer.h>
|
||||
|
||||
#include "syncfileitem.h"
|
||||
#include "progressdispatcher.h"
|
||||
|
||||
struct hbf_transfer_s;
|
||||
struct ne_session_s;
|
||||
struct ne_decompress_s;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class SyncJournalDb;
|
||||
class OwncloudPropagator;
|
||||
|
||||
class PropagatorJob : public QObject {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
OwncloudPropagator *_propagator;
|
||||
public:
|
||||
explicit PropagatorJob(OwncloudPropagator* propagator) : _propagator(propagator) {}
|
||||
public slots:
|
||||
virtual void start() = 0;
|
||||
signals:
|
||||
void finished(SyncFileItem::Status);
|
||||
void completed(const SyncFileItem &);
|
||||
void progress(Progress::Kind, const SyncFileItem& item, quint64 bytes, quint64 total);
|
||||
};
|
||||
|
||||
/*
|
||||
* Propagate a directory, and all its sub entries.
|
||||
*/
|
||||
class PropagateDirectory : public PropagatorJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// e.g: create the directory
|
||||
QScopedPointer<PropagatorJob>_firstJob;
|
||||
|
||||
// all the sub files or sub directories.
|
||||
//TODO: in the future, all sub job can be run in parallel
|
||||
QVector<PropagatorJob *> _subJobs;
|
||||
|
||||
SyncFileItem _item;
|
||||
|
||||
int _current; // index of the current running job
|
||||
SyncFileItem::Status _hasError; // NoStatus, or NormalError / SoftError if there was an error
|
||||
|
||||
|
||||
explicit PropagateDirectory(OwncloudPropagator *propagator, const SyncFileItem &item = SyncFileItem())
|
||||
: PropagatorJob(propagator)
|
||||
, _firstJob(0), _item(item), _current(-1), _hasError(SyncFileItem::NoStatus) { }
|
||||
|
||||
virtual ~PropagateDirectory() {
|
||||
qDeleteAll(_subJobs);
|
||||
}
|
||||
|
||||
void append(PropagatorJob *subJob) {
|
||||
_subJobs.append(subJob);
|
||||
}
|
||||
|
||||
virtual void start();
|
||||
|
||||
|
||||
private slots:
|
||||
void startJob(PropagatorJob *next) {
|
||||
connect(next, SIGNAL(finished(SyncFileItem::Status)), this, SLOT(proceedNext(SyncFileItem::Status)), Qt::QueuedConnection);
|
||||
connect(next, SIGNAL(completed(SyncFileItem)), this, SIGNAL(completed(SyncFileItem)));
|
||||
connect(next, SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)), this, SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)));
|
||||
next->start();
|
||||
}
|
||||
|
||||
void proceedNext(SyncFileItem::Status status);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Abstract class to propagate a single item
|
||||
*/
|
||||
class PropagateItemJob : public PropagatorJob {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
void done(SyncFileItem::Status status, const QString &errorString = QString());
|
||||
|
||||
void updateMTimeAndETag(const char *uri, time_t);
|
||||
|
||||
/* fetch the error code and string from the session
|
||||
in case of error, calls done with the error and returns true.
|
||||
|
||||
If the HTTP error code is ignoreHTTPError, the error is ignored
|
||||
*/
|
||||
bool updateErrorFromSession(int neon_code = 0, ne_request *req = 0, int ignoreHTTPError = 0);
|
||||
|
||||
/*
|
||||
* to be called by the progress callback and will wait the amount of time needed.
|
||||
*/
|
||||
void limitBandwidth(qint64 progress, qint64 limit);
|
||||
|
||||
QElapsedTimer _lastTime;
|
||||
qint64 _lastProgress;
|
||||
int _httpStatusCode;
|
||||
SyncFileItem _item;
|
||||
|
||||
public:
|
||||
PropagateItemJob(OwncloudPropagator* propagator, const SyncFileItem &item)
|
||||
: PropagatorJob(propagator), _lastProgress(0), _httpStatusCode(0), _item(item) {}
|
||||
};
|
||||
|
||||
// Dummy job that just mark it as completed and ignored.
|
||||
class PropagateIgnoreJob : public PropagateItemJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PropagateIgnoreJob(OwncloudPropagator* propagator,const SyncFileItem& item)
|
||||
: PropagateItemJob(propagator, item) {}
|
||||
void start() {
|
||||
done(SyncFileItem::FileIgnored);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class OwncloudPropagator : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
PropagateItemJob *createJob(const SyncFileItem& item);
|
||||
QScopedPointer<PropagateDirectory> _rootJob;
|
||||
|
||||
public:
|
||||
ne_session_s *_session;
|
||||
QString _localDir; // absolute path to the local directory. ends with '/'
|
||||
QString _remoteDir; // path to the root of the remote. ends with '/'
|
||||
SyncJournalDb *_journal;
|
||||
|
||||
public:
|
||||
OwncloudPropagator(ne_session_s *session, const QString &localDir, const QString &remoteDir,
|
||||
SyncJournalDb *progressDb, QAtomicInt *abortRequested)
|
||||
: _session(session)
|
||||
, _localDir(localDir)
|
||||
, _remoteDir(remoteDir)
|
||||
, _journal(progressDb)
|
||||
, _abortRequested(abortRequested)
|
||||
{
|
||||
if (!localDir.endsWith(QChar('/'))) _localDir+='/';
|
||||
if (!remoteDir.endsWith(QChar('/'))) _remoteDir+='/';
|
||||
}
|
||||
|
||||
void start(const SyncFileItemVector &_syncedItems);
|
||||
|
||||
int _downloadLimit;
|
||||
int _uploadLimit;
|
||||
|
||||
QAtomicInt *_abortRequested; // boolean set by the main thread to abort.
|
||||
|
||||
signals:
|
||||
void completed(const SyncFileItem &);
|
||||
void progress(Progress::Kind kind, const SyncFileItem&, quint64 bytes, quint64 total);
|
||||
void finished();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
478
src/mirall/owncloudsetuppage_ng.ui
Normal file
478
src/mirall/owncloudsetuppage_ng.ui
Normal file
@@ -0,0 +1,478 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OwncloudSetupPage</class>
|
||||
<widget class="QWidget" name="OwncloudSetupPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>583</width>
|
||||
<height>448</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="topLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>68</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="resultLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="3">
|
||||
<widget class="QWidget" name="advancedBox" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Local Folder</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>pbSelectLocalFolder</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="pbSelectLocalFolder">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>pbSelectLocalFolder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QWidget" name="resolutionWidget" native="true">
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QRadioButton" name="radioButton">
|
||||
<property name="text">
|
||||
<string>&Keep local data</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><small>Syncs your existing data to new location.</small></string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QRadioButton" name="cbSyncFromScratch">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If this box is checked, existing content in the local directory will be erased to start a clean sync from the server.</p><p>Do not check this if the local content should be uploaded to the servers directory.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Start a clean sync</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><small>Erases the contents of the local folder before syncing using the new settings.</small></string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Server &Address</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>leUrl</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Utils::FancyLineEdit" name="leUrl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter the url of the ownCloud you want to connect to (without http or https).</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>https://...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Username</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>leUsername</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Utils::FancyLineEdit" name="leUsername">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter the ownCloud username.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>&Password</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>lePassword</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Utils::FancyLineEdit" name="lePassword">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter the ownCloud password.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Error Label</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbAdvanced">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Advanced &Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="3">
|
||||
<widget class="QLabel" name="syncModeLabel">
|
||||
<property name="text">
|
||||
<string>Status message</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>58</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="bottomLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Utils::FancyLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header location="global">fancylineedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>lePassword</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -23,14 +23,10 @@
|
||||
#include "wizard/owncloudwizard.h"
|
||||
#include "mirall/owncloudsetupwizard.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
#include "mirall/sslerrordialog.h"
|
||||
|
||||
#include "creds/credentialsfactory.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "creds/dummycredentials.h"
|
||||
|
||||
@@ -38,8 +34,11 @@ namespace Mirall {
|
||||
|
||||
OwncloudSetupWizard::OwncloudSetupWizard(QObject* parent) :
|
||||
QObject( parent ),
|
||||
_account(0),
|
||||
_ocWizard(new OwncloudWizard),
|
||||
_mkdirRequestReply(),
|
||||
_checkInstallationRequest(),
|
||||
_checkRemoteFolderRequest(),
|
||||
_configHandle(),
|
||||
_remoteFolder()
|
||||
{
|
||||
connect( _ocWizard, SIGNAL(determineAuthType(const QString&)),
|
||||
@@ -53,7 +52,8 @@ OwncloudSetupWizard::OwncloudSetupWizard(QObject* parent) :
|
||||
Therefore Qt::QueuedConnection is required */
|
||||
connect( _ocWizard, SIGNAL(basicSetupFinished(int)),
|
||||
this, SLOT(slotAssistantFinished(int)), Qt::QueuedConnection);
|
||||
connect( _ocWizard, SIGNAL(finished(int)), SLOT(deleteLater()));
|
||||
connect( _ocWizard, SIGNAL(clearPendingRequests()),
|
||||
this, SLOT(slotClearPendingRequests()));
|
||||
}
|
||||
|
||||
OwncloudSetupWizard::~OwncloudSetupWizard()
|
||||
@@ -71,46 +71,34 @@ void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *
|
||||
|
||||
wiz = new OwncloudSetupWizard(parent);
|
||||
connect( wiz, SIGNAL(ownCloudWizardDone(int)), obj, amember);
|
||||
connect( wiz, SIGNAL(ownCloudWizardDone(int)), wiz, SLOT(deleteLater()));
|
||||
FolderMan::instance()->setSyncEnabled(false);
|
||||
wiz->startWizard();
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::startWizard()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
bool multiFolderSetup = folderMan->map().count() > 1;
|
||||
// ###
|
||||
Account *account = Account::restore();
|
||||
if (!account) {
|
||||
_ocWizard->setConfigExists(false);
|
||||
account = new Account;
|
||||
account->setCredentials(CredentialsFactory::create("dummy"));
|
||||
} else {
|
||||
account->credentials()->fetch(account);
|
||||
_ocWizard->setConfigExists(true);
|
||||
// Set useful default values.
|
||||
MirallConfigFile cfgFile;
|
||||
// Fill the entry fields with existing values.
|
||||
QString url = cfgFile.ownCloudUrl();
|
||||
//QString user = cfgFile.ownCloudUser();
|
||||
bool configExists = !( url.isEmpty()/* || user.isEmpty()*/ );
|
||||
_ocWizard->setConfigExists( configExists );
|
||||
|
||||
if( !url.isEmpty() ) {
|
||||
_ocWizard->setOCUrl( url );
|
||||
}
|
||||
account->setSslErrorHandler(new SslDialogErrorHandler);
|
||||
_ocWizard->setAccount(account);
|
||||
_ocWizard->setOCUrl(account->url().toString());
|
||||
|
||||
_remoteFolder = Theme::instance()->defaultServerFolder();
|
||||
// remoteFolder may be empty, which means /
|
||||
|
||||
QString localFolder = Theme::instance()->defaultClientFolder();
|
||||
|
||||
// if its a relative path, prepend with users home dir, otherwise use as absolute path
|
||||
|
||||
if( !QDir(localFolder).isAbsolute() ) {
|
||||
localFolder = QDir::homePath() + QDir::separator() + localFolder;
|
||||
}
|
||||
|
||||
if (!multiFolderSetup) {
|
||||
QList<Folder*> folders = folderMan->map().values();
|
||||
if (!folders.isEmpty()) {
|
||||
Folder* folder = folders.first();
|
||||
localFolder = QDir(folder->path()).absolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
_ocWizard->setProperty("localFolder", localFolder);
|
||||
_ocWizard->setRemoteFolder(_remoteFolder);
|
||||
|
||||
@@ -119,92 +107,190 @@ void OwncloudSetupWizard::startWizard()
|
||||
_ocWizard->restart();
|
||||
|
||||
// settings re-initialized in initPage must be set here after restart
|
||||
_ocWizard->setMultipleFoldersExist( multiFolderSetup );
|
||||
_ocWizard->setMultipleFoldersExist(FolderMan::instance()->map().count() > 1);
|
||||
|
||||
_ocWizard->open();
|
||||
_ocWizard->raise();
|
||||
}
|
||||
|
||||
// also checks if an installation is valid and determines auth type in a second step
|
||||
void OwncloudSetupWizard::slotDetermineAuthType(const QString &urlString)
|
||||
void OwncloudSetupWizard::slotDetermineAuthType(const QString& serverUrl)
|
||||
{
|
||||
QString fixedUrl = urlString;
|
||||
QUrl url = QUrl::fromUserInput(fixedUrl);
|
||||
// fromUserInput defaults to http, not http if no scheme is specified
|
||||
if (!fixedUrl.startsWith("http://") && !fixedUrl.startsWith("https://")) {
|
||||
url.setScheme("https");
|
||||
QString url(serverUrl);
|
||||
qDebug() << "Connect to url: " << url;
|
||||
_ocWizard->setField(QLatin1String("OCUrl"), url );
|
||||
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2 to determine authentication type...")
|
||||
.arg( Theme::instance()->appNameGUI() ).arg(url) );
|
||||
// write a temporary config.
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
// remove a possibly existing custom config.
|
||||
if( ! _configHandle.isEmpty() ) {
|
||||
// remove the old config file.
|
||||
MirallConfigFile oldConfig( _configHandle );
|
||||
oldConfig.cleanupCustomConfig();
|
||||
}
|
||||
|
||||
_configHandle = now.toString(QLatin1String("MMddyyhhmmss"));
|
||||
|
||||
MirallConfigFile cfgFile( _configHandle, true );
|
||||
if( url.isEmpty() ) return;
|
||||
if( !( url.startsWith(QLatin1String("https://")) || url.startsWith(QLatin1String("http://"))) ) {
|
||||
qDebug() << "url does not start with a valid protocol, assuming https.";
|
||||
url.prepend(QLatin1String("https://"));
|
||||
// FIXME: give a hint about the auto completion
|
||||
_ocWizard->setOCUrl(url);
|
||||
}
|
||||
cfgFile.writeOwncloudConfig( Theme::instance()->appName(),
|
||||
url,
|
||||
new DummyCredentials);
|
||||
|
||||
ownCloudInfo* info = ownCloudInfo::instance();
|
||||
info->setCustomConfigHandle( _configHandle );
|
||||
if( info->isConfigured() ) {
|
||||
// reset the SSL Untrust flag to let the SSL dialog appear again.
|
||||
info->resetSSLUntrust();
|
||||
connect(info, SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
SLOT(slotOwnCloudFoundAuth(QString,QString,QString,QString)));
|
||||
connect(info, SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
SLOT(slotNoOwnCloudFoundAuth(QNetworkReply*)));
|
||||
_checkInstallationRequest = info->checkInstallation();
|
||||
} else {
|
||||
qDebug() << " ownCloud seems not to be configured, can not start test connect.";
|
||||
}
|
||||
Account *account = _ocWizard->account();
|
||||
account->setUrl(url);
|
||||
CheckServerJob *job = new CheckServerJob(_ocWizard->account(), false, this);
|
||||
job->setIgnoreCredentialFailure(true);
|
||||
connect(job, SIGNAL(instanceFound(QUrl,QVariantMap)), SLOT(slotOwnCloudFoundAuth(QUrl,QVariantMap)));
|
||||
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotNoOwnCloudFoundAuth(QNetworkReply*)));
|
||||
connect(job, SIGNAL(timeout(const QUrl&)), SLOT(slotNoOwnCloudFoundAuthTimeout(const QUrl&)));
|
||||
job->setTimeout(10*1000);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotOwnCloudFoundAuth(const QUrl& url, const QVariantMap &info)
|
||||
void OwncloudSetupWizard::slotOwnCloudFoundAuth( const QString& url, const QString& infoString, const QString& version, const QString& )
|
||||
{
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
this, SLOT(slotOwnCloudFoundAuth(QString,QString,QString,QString)));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotNoOwnCloudFoundAuth(QNetworkReply*)));
|
||||
|
||||
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\">Successfully connected to %1: %2 version %3 (%4)</font><br/><br/>")
|
||||
.arg(url.toString())
|
||||
.arg(Theme::instance()->appNameGUI())
|
||||
.arg(CheckServerJob::versionString(info))
|
||||
.arg(CheckServerJob::version(info)));
|
||||
.arg( url ).arg(Theme::instance()->appNameGUI()).arg(infoString).arg(version));
|
||||
|
||||
if (url.path().endsWith("/status.php")) {
|
||||
// We might be redirected, update the account
|
||||
QUrl redirectedUrl = url;
|
||||
redirectedUrl.setPath(url.path().left(url.path().length() - 11));
|
||||
_ocWizard->account()->setUrl(redirectedUrl);
|
||||
qDebug() << Q_FUNC_INFO << " was redirected to" << redirectedUrl.toString();
|
||||
MirallAccessManager* nm = new MirallAccessManager(this);
|
||||
// TODO: We should get this path from owncloud info.
|
||||
QNetworkReply* reply = nm->get (QNetworkRequest (url + "/remote.php/webdav/"));
|
||||
|
||||
connect (reply, SIGNAL(finished()),
|
||||
this, SLOT(slotAuthCheckReplyFinished()));
|
||||
|
||||
nm->setProperty ("mirallRedirs", QVariant (0));
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotAuthCheckReplyFinished()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast< QNetworkReply* > (sender ());
|
||||
QUrl redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
QNetworkAccessManager* nm = reply->manager ();
|
||||
const int redirCount = nm->property ("mirallRedirs").toInt();
|
||||
|
||||
if (redirCount > 10) {
|
||||
redirection.clear ();
|
||||
}
|
||||
|
||||
DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_ocWizard->account(), this);
|
||||
job->setIgnoreCredentialFailure(true);
|
||||
connect(job, SIGNAL(authType(WizardCommon::AuthType)),
|
||||
_ocWizard, SLOT(setAuthType(WizardCommon::AuthType)));
|
||||
job->start();
|
||||
disconnect (reply, SIGNAL(finished()),
|
||||
this, SLOT(slotAuthCheckReplyFinished()));
|
||||
if ((reply->error () == QNetworkReply::AuthenticationRequiredError) || redirection.isEmpty()) {
|
||||
reply->deleteLater();
|
||||
nm->deleteLater();
|
||||
_ocWizard->setAuthType (WizardCommon::HttpCreds);
|
||||
} else if (redirection.toString().endsWith ("/remote.php/webdav/")) {
|
||||
QNetworkReply* newReply = nm->get (QNetworkRequest(redirection));
|
||||
|
||||
connect (newReply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(slotAuthCheckReplyError(QNetworkReply::NetworkError)));
|
||||
connect (newReply, SIGNAL(finished()),
|
||||
this, SLOT(slotAuthCheckReplyFinished(QNetworkReply::NetworkError)));
|
||||
reply->deleteLater();
|
||||
|
||||
nm->setProperty ("mirallRedirs", QVariant(redirCount + 1));
|
||||
} else {
|
||||
QRegExp shibbolethyWords ("SAML|wayf");
|
||||
|
||||
shibbolethyWords.setCaseSensitivity (Qt::CaseInsensitive);
|
||||
if (redirection.toString ().contains (shibbolethyWords)) {
|
||||
_ocWizard->setAuthType(WizardCommon::Shibboleth);
|
||||
} else {
|
||||
// TODO: Send an error.
|
||||
// eh?
|
||||
_ocWizard->setAuthType (WizardCommon::HttpCreds);
|
||||
}
|
||||
reply->deleteLater();
|
||||
nm->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotNoOwnCloudFoundAuth(QNetworkReply *reply)
|
||||
void OwncloudSetupWizard::slotNoOwnCloudFoundAuth( QNetworkReply *err )
|
||||
{
|
||||
_ocWizard->displayError(tr("Failed to connect to %1 at %2:<br/>%3")
|
||||
.arg(Theme::instance()->appNameGUI())
|
||||
.arg(reply->url().toString())
|
||||
.arg(reply->errorString()));
|
||||
}
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
this, SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotNoOwnCloudFound(QNetworkReply*)));
|
||||
|
||||
void OwncloudSetupWizard::slotNoOwnCloudFoundAuthTimeout(const QUrl&url)
|
||||
{
|
||||
_ocWizard->displayError(tr("Failed to connect to %1 at %2:<br/>%3")
|
||||
.arg(Theme::instance()->appNameGUI())
|
||||
.arg(url.toString())
|
||||
.arg("Timeout"));
|
||||
_ocWizard->displayError(tr("Failed to connect to %1:<br/>%2").
|
||||
arg(Theme::instance()->appNameGUI()).arg(err->errorString()));
|
||||
|
||||
// remove the config file again
|
||||
MirallConfigFile cfgFile( _configHandle );
|
||||
cfgFile.cleanupCustomConfig();
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotConnectToOCUrl( const QString& url )
|
||||
{
|
||||
qDebug() << "Connect to url: " << url;
|
||||
_ocWizard->account()->setCredentials(_ocWizard->getCredentials());
|
||||
_ocWizard->setField(QLatin1String("OCUrl"), url );
|
||||
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2...")
|
||||
.arg( Theme::instance()->appNameGUI() ).arg(url) );
|
||||
|
||||
testOwnCloudConnect();
|
||||
qDebug() << "Connect to url: " << url;
|
||||
_ocWizard->setField(QLatin1String("OCUrl"), url );
|
||||
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2...")
|
||||
.arg( Theme::instance()->appNameGUI() ).arg(url) );
|
||||
testOwnCloudConnect();
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::testOwnCloudConnect()
|
||||
{
|
||||
ValidateDavAuthJob *job = new ValidateDavAuthJob(_ocWizard->account(), this);
|
||||
job->setIgnoreCredentialFailure(true);
|
||||
connect(job, SIGNAL(authResult(QNetworkReply*)), SLOT(slotConnectionCheck(QNetworkReply*)));
|
||||
job->start();
|
||||
// write a temporary config.
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
if( _configHandle.isEmpty() ) {
|
||||
_configHandle = now.toString(QLatin1String("MMddyyhhmmss"));
|
||||
}
|
||||
|
||||
MirallConfigFile cfgFile( _configHandle, true );
|
||||
QString url = _ocWizard->field(QLatin1String("OCUrl")).toString();
|
||||
if( url.isEmpty() ) return;
|
||||
if( !( url.startsWith(QLatin1String("https://")) || url.startsWith(QLatin1String("http://"))) ) {
|
||||
qDebug() << "url does not start with a valid protocol, assuming https.";
|
||||
url.prepend(QLatin1String("https://"));
|
||||
// FIXME: give a hint about the auto completion
|
||||
_ocWizard->setOCUrl(url);
|
||||
}
|
||||
cfgFile.writeOwncloudConfig( Theme::instance()->appName(),
|
||||
url,
|
||||
_ocWizard->getCredentials());
|
||||
|
||||
ownCloudInfo* info(ownCloudInfo::instance());
|
||||
info->setCustomConfigHandle( _configHandle );
|
||||
// If there is already a config, take its proxy config.
|
||||
if( info->isConfigured() ) {
|
||||
MirallConfigFile prevCfg;
|
||||
cfgFile.setProxyType( prevCfg.proxyType(), prevCfg.proxyHostName(), prevCfg.proxyPort(),
|
||||
prevCfg.proxyNeedsAuth(), prevCfg.proxyUser(), prevCfg.proxyPassword() );
|
||||
}
|
||||
|
||||
connect( info,SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this,SLOT(slotConnectionCheck(QString,QNetworkReply*)));
|
||||
|
||||
qDebug() << "# checking for authentication settings.";
|
||||
_checkRemoteFolderRequest = info->getWebDAVPath(_remoteFolder ); // this call needs to be authenticated.
|
||||
// continue in slotConnectionCheck
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotConnectionCheck(QNetworkReply* reply)
|
||||
void OwncloudSetupWizard::slotConnectionCheck(const QString&, QNetworkReply* reply)
|
||||
{
|
||||
// disconnect from ownCloud Info signals
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this, SLOT(slotConnectionCheck(QString,QNetworkReply*)));
|
||||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
case QNetworkReply::ContentNotFoundError:
|
||||
@@ -221,8 +307,9 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFo
|
||||
{
|
||||
qDebug() << "Setup local sync folder for new oC connection " << localFolder;
|
||||
const QDir fi( localFolder );
|
||||
// FIXME: Show problems with local folder properly.
|
||||
bool localFolderOk = true;
|
||||
|
||||
bool nextStep = true;
|
||||
if( fi.exists() ) {
|
||||
// there is an existing local folder. If its non empty, it can only be synced if the
|
||||
// ownCloud is newly created.
|
||||
@@ -236,23 +323,35 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFo
|
||||
} else {
|
||||
res += tr("failed.");
|
||||
qDebug() << "Failed to create " << fi.path();
|
||||
localFolderOk = false;
|
||||
_ocWizard->displayError(tr("Could not create local folder %1").arg(localFolder));
|
||||
nextStep = false;
|
||||
}
|
||||
_ocWizard->appendToConfigurationLog( res );
|
||||
}
|
||||
if (nextStep) {
|
||||
EntityExistsJob *job = new EntityExistsJob(_ocWizard->account(), remoteFolder, this);
|
||||
connect(job, SIGNAL(exists(QNetworkReply*)), SLOT(slotAuthCheckReply(QNetworkReply*)));
|
||||
job->start();
|
||||
} else {
|
||||
finalizeSetup( false );
|
||||
|
||||
if( localFolderOk ) {
|
||||
checkRemoteFolder(remoteFolder);
|
||||
}
|
||||
}
|
||||
|
||||
// ### TODO move into EntityExistsJob once we decide if/how to return gui strings from jobs
|
||||
void OwncloudSetupWizard::slotAuthCheckReply(QNetworkReply *reply)
|
||||
void OwncloudSetupWizard::checkRemoteFolder(const QString& remoteFolder)
|
||||
{
|
||||
ownCloudInfo* info(ownCloudInfo::instance());
|
||||
connect( info,SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this,SLOT(slotAuthCheckReply(QString,QNetworkReply*)));
|
||||
|
||||
qDebug() << "# checking for existence of remote folder.";
|
||||
info->setCustomConfigHandle(_configHandle);
|
||||
_checkRemoteFolderRequest = info->getWebDAVPath(remoteFolder); // this call needs to be authenticated.
|
||||
// continue in slotAuthCheckReply
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotAuthCheckReply( const QString&, QNetworkReply *reply )
|
||||
{
|
||||
// disconnect from ownCloud Info signals
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this,SLOT(slotAuthCheckReply(QString,QNetworkReply*)));
|
||||
|
||||
bool ok = true;
|
||||
QString error;
|
||||
QNetworkReply::NetworkError errId = reply->error();
|
||||
@@ -260,11 +359,11 @@ void OwncloudSetupWizard::slotAuthCheckReply(QNetworkReply *reply)
|
||||
if( errId == QNetworkReply::NoError ) {
|
||||
qDebug() << "******** Remote folder found, all cool!";
|
||||
} else if( errId == QNetworkReply::ContentNotFoundError ) {
|
||||
if( _remoteFolder.isEmpty() ) {
|
||||
error = tr("No remote folder specified!");
|
||||
ok = false;
|
||||
if( createRemoteFolder() ) {
|
||||
return; // Finish here, the mkdir request will go on.
|
||||
} else {
|
||||
createRemoteFolder();
|
||||
error = tr("The remote folder could not be accessed!");
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
error = tr("Error: %1").arg(reply->errorString());
|
||||
@@ -278,20 +377,25 @@ void OwncloudSetupWizard::slotAuthCheckReply(QNetworkReply *reply)
|
||||
finalizeSetup( ok );
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::createRemoteFolder()
|
||||
bool OwncloudSetupWizard::createRemoteFolder()
|
||||
{
|
||||
_ocWizard->appendToConfigurationLog( tr("creating folder on ownCloud: %1" ).arg( _remoteFolder ));
|
||||
if( _remoteFolder.isEmpty() ) return false;
|
||||
|
||||
MkColJob *job = new MkColJob(_ocWizard->account(), _remoteFolder, this);
|
||||
connect(job, SIGNAL(finished(QNetworkReply::NetworkError)), SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
job->start();
|
||||
_ocWizard->appendToConfigurationLog( tr("creating folder on ownCloud: %1" ).arg( _remoteFolder ));
|
||||
ownCloudInfo* info(ownCloudInfo::instance());
|
||||
connect(info, SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
this, SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
|
||||
_mkdirRequestReply = info->mkdirRequest( _remoteFolder );
|
||||
|
||||
return (_mkdirRequestReply != NULL);
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotCreateRemoteFolderFinished( QNetworkReply::NetworkError error )
|
||||
{
|
||||
qDebug() << "** webdav mkdir request finished " << error;
|
||||
// disconnect(ownCloudInfo::instance(), SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
// this, SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
this, SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
|
||||
bool success = true;
|
||||
|
||||
@@ -308,7 +412,7 @@ void OwncloudSetupWizard::slotCreateRemoteFolderFinished( QNetworkReply::Network
|
||||
"are wrong!"
|
||||
"<br/>Please go back and check your credentials.</p>"));
|
||||
_ocWizard->appendToConfigurationLog( tr("<p><font color=\"red\">Remote folder creation failed probably because the provided credentials are wrong.</font>"
|
||||
"<br/>Please go back and check your credentials.</p>"));
|
||||
"<br/>Please go back and check your credentials.</p>"));
|
||||
_remoteFolder.clear();
|
||||
success = false;
|
||||
} else {
|
||||
@@ -330,167 +434,125 @@ void OwncloudSetupWizard::finalizeSetup( bool success )
|
||||
if( success ) {
|
||||
if( !(localFolder.isEmpty() || _remoteFolder.isEmpty() )) {
|
||||
_ocWizard->appendToConfigurationLog( tr("A sync connection from %1 to remote directory %2 was set up.")
|
||||
.arg(localFolder).arg(_remoteFolder));
|
||||
.arg(localFolder).arg(_remoteFolder));
|
||||
}
|
||||
_ocWizard->appendToConfigurationLog( QLatin1String(" "));
|
||||
_ocWizard->appendToConfigurationLog( QLatin1String("<p><font color=\"green\"><b>")
|
||||
+ tr("Successfully connected to %1!")
|
||||
.arg(Theme::instance()->appNameGUI())
|
||||
+ QLatin1String("</b></font></p>"));
|
||||
+ tr("Successfully connected to %1!")
|
||||
.arg(Theme::instance()->appNameGUI())
|
||||
+ QLatin1String("</b></font></p>"));
|
||||
_ocWizard->successfulStep();
|
||||
} else {
|
||||
// ### this is not quite true, pass in the real problem as optional parameter
|
||||
_ocWizard->appendToConfigurationLog(QLatin1String("<p><font color=\"red\">")
|
||||
+ tr("Connection to %1 could not be established. Please check again.")
|
||||
.arg(Theme::instance()->appNameGUI())
|
||||
+ QLatin1String("</font></p>"));
|
||||
+ tr("Connection to %1 could not be established. Please check again.")
|
||||
.arg(Theme::instance()->appNameGUI())
|
||||
+ QLatin1String("</font></p>"));
|
||||
}
|
||||
}
|
||||
|
||||
bool OwncloudSetupWizard::ensureStartFromScratch(const QString &localFolder) {
|
||||
// first try to rename (backup) the current local dir.
|
||||
bool renameOk = false;
|
||||
while( !renameOk ) {
|
||||
renameOk = FolderMan::instance()->startFromScratch(localFolder);
|
||||
if( ! renameOk ) {
|
||||
QMessageBox::StandardButton but;
|
||||
but = QMessageBox::question( 0, tr("Folder rename failed"),
|
||||
tr("Can't remove and back up the folder because the folder or a file in it is open in another program."
|
||||
"Please close the folder or file and hit retry or cancel the setup."), QMessageBox::Retry | QMessageBox::Abort, QMessageBox::Retry);
|
||||
if( but == QMessageBox::Abort ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return renameOk;
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::replaceDefaultAccountWith(Account *newAccount)
|
||||
{
|
||||
// new Account
|
||||
AccountManager *mgr = AccountManager::instance();
|
||||
if (mgr->account()) {
|
||||
mgr->account()->deleteLater();
|
||||
}
|
||||
mgr->setAccount(newAccount);
|
||||
newAccount->save();
|
||||
}
|
||||
|
||||
// Method executed when the user ends has finished the basic setup.
|
||||
// Method executed when the user ends the wizard, either with 'accept' or 'reject'.
|
||||
// accept the custom config to be the main one if Accepted.
|
||||
void OwncloudSetupWizard::slotAssistantFinished( int result )
|
||||
{
|
||||
MirallConfigFile cfg( _configHandle );
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
if( result == QDialog::Rejected ) {
|
||||
// the old config remains valid. Remove the temporary one.
|
||||
_ocWizard->account()->deleteLater();
|
||||
cfg.cleanupCustomConfig();
|
||||
qDebug() << "Rejected the new config, use the old!";
|
||||
} else if( result == QDialog::Accepted ) {
|
||||
AbstractCredentials* credentials(_ocWizard->getCredentials());
|
||||
|
||||
Account *newAccount = _ocWizard->account();
|
||||
Account *origAccount = AccountManager::instance()->account();
|
||||
const QString localFolder = _ocWizard->localFolder();
|
||||
qDebug() << "Config Changes were accepted!";
|
||||
|
||||
bool isInitialSetup = (origAccount == 0);
|
||||
bool reinitRequired = newAccount->changed(origAccount, true /* ignoreProtocol, allows http->https */);
|
||||
bool startFromScratch = _ocWizard->field("OCSyncFromScratch").toBool();
|
||||
// go through all folders and remove the journals if the server changed.
|
||||
MirallConfigFile prevCfg;
|
||||
QUrl prevUrl( prevCfg.ownCloudUrl() );
|
||||
QUrl newUrl( cfg.ownCloudUrl() );
|
||||
AbstractCredentials* oldCredentials(prevCfg.getCredentials());
|
||||
|
||||
// This distinguishes three possibilities:
|
||||
// 1. Initial setup, no prior account exists
|
||||
if (isInitialSetup) {
|
||||
folderMan->addFolderDefinition(Theme::instance()->appName(),
|
||||
localFolder, _remoteFolder );
|
||||
replaceDefaultAccountWith(newAccount);
|
||||
bool urlHasChanged = (prevUrl.host() != newUrl.host() ||
|
||||
prevUrl.port() != newUrl.port() ||
|
||||
prevUrl.path() != newUrl.path());
|
||||
|
||||
// if the user changed, its also a changed url.
|
||||
if(credentials->changed(oldCredentials)) {
|
||||
urlHasChanged = true;
|
||||
qDebug() << "The User has changed, same as url change.";
|
||||
}
|
||||
// 2. Server URL or user changed, requires reinit of folders
|
||||
else if (reinitRequired) {
|
||||
// 2.1: startFromScratch: (Re)move local data, clean slate sync
|
||||
if (startFromScratch) {
|
||||
if (ensureStartFromScratch(localFolder)) {
|
||||
folderMan->addFolderDefinition(Theme::instance()->appName(),
|
||||
localFolder, _remoteFolder );
|
||||
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\"><b>Local sync folder %1 successfully created!</b></font>").arg(localFolder));
|
||||
replaceDefaultAccountWith(newAccount);
|
||||
|
||||
const QString localFolder = _ocWizard->localFolder();
|
||||
bool acceptCfg = true;
|
||||
|
||||
if( urlHasChanged ) {
|
||||
// first terminate sync jobs.
|
||||
folderMan->terminateSyncProcess();
|
||||
|
||||
bool startFromScratch = _ocWizard->field( "OCSyncFromScratch" ).toBool();
|
||||
if( startFromScratch ) {
|
||||
// first try to rename (backup) the current local dir.
|
||||
bool renameOk = false;
|
||||
while( !renameOk ) {
|
||||
renameOk = folderMan->startFromScratch(localFolder);
|
||||
if( ! renameOk ) {
|
||||
QMessageBox::StandardButton but;
|
||||
but = QMessageBox::question( 0, tr("Folder rename failed"),
|
||||
tr("Can't remove and back up the folder because the folder or a file in it is open in another program."
|
||||
"Please close the folder or file and hit retry or cancel the setup."), QMessageBox::Retry | QMessageBox::Abort, QMessageBox::Retry);
|
||||
if( but == QMessageBox::Abort ) {
|
||||
renameOk = true;
|
||||
acceptCfg = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2.2: Reinit: Remove journal and start a sync
|
||||
else {
|
||||
folderMan->removeAllFolderDefinitions();
|
||||
folderMan->addFolderDefinition(Theme::instance()->appName(),
|
||||
localFolder, _remoteFolder );
|
||||
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\"><b>Local sync folder %1 successfully created!</b></font>").arg(localFolder));
|
||||
replaceDefaultAccountWith(newAccount);
|
||||
}
|
||||
|
||||
// Now write the resulting folder definition if folder names are set.
|
||||
if( acceptCfg && urlHasChanged ) {
|
||||
folderMan->removeAllFolderDefinitions();
|
||||
folderMan->addFolderDefinition(Theme::instance()->appName(),
|
||||
localFolder, _remoteFolder );
|
||||
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\"><b>Local sync folder %1 successfully created!</b></font>").arg(localFolder));
|
||||
} else {
|
||||
// url is unchanged. Only the password was changed.
|
||||
if( acceptCfg ) {
|
||||
qDebug() << "Only password was changed, no changes to folder configuration.";
|
||||
} else {
|
||||
qDebug() << "User interrupted change of configuration.";
|
||||
}
|
||||
}
|
||||
// 3. Existing setup, http -> https or password changed
|
||||
else {
|
||||
replaceDefaultAccountWith(newAccount);
|
||||
qDebug() << "Only password was changed, no changes to folder configuration.";
|
||||
|
||||
// save the user credentials and afterwards clear the cred store.
|
||||
if( acceptCfg ) {
|
||||
cfg.acceptCustomConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// clear the custom config handle
|
||||
_configHandle.clear();
|
||||
ownCloudInfo::instance()->setCustomConfigHandle( QString::null );
|
||||
|
||||
// notify others.
|
||||
emit ownCloudWizardDone( result );
|
||||
}
|
||||
|
||||
DetermineAuthTypeJob::DetermineAuthTypeJob(Account *account, QObject *parent)
|
||||
: AbstractNetworkJob(account, QString(), parent)
|
||||
, _redirects(0)
|
||||
void OwncloudSetupWizard::slotClearPendingRequests()
|
||||
{
|
||||
}
|
||||
|
||||
void DetermineAuthTypeJob::start()
|
||||
{
|
||||
QNetworkReply *reply = getRequest(Account::davPath());
|
||||
setReply(reply);
|
||||
setupConnections(reply);
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void DetermineAuthTypeJob::finished()
|
||||
{
|
||||
QUrl redirection = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
qDebug() << redirection.toString();
|
||||
if (_redirects >= maxRedirects()) {
|
||||
redirection.clear();
|
||||
qDebug() << "Pending request: " << _mkdirRequestReply;
|
||||
if( _mkdirRequestReply && _mkdirRequestReply->isRunning() ) {
|
||||
qDebug() << "ABORTing pending mkdir request.";
|
||||
_mkdirRequestReply->abort();
|
||||
}
|
||||
if ((reply()->error() == QNetworkReply::AuthenticationRequiredError) || redirection.isEmpty()) {
|
||||
emit authType(WizardCommon::HttpCreds);
|
||||
} else if (redirection.toString().endsWith(Account::davPath())) {
|
||||
// do a new run
|
||||
_redirects++;
|
||||
setReply(getRequest(redirection));
|
||||
setupConnections(reply());
|
||||
} else {
|
||||
QRegExp shibbolethyWords("SAML|wayf");
|
||||
|
||||
shibbolethyWords.setCaseSensitivity(Qt::CaseInsensitive);
|
||||
if (redirection.toString().contains(shibbolethyWords)) {
|
||||
emit authType(WizardCommon::Shibboleth);
|
||||
} else {
|
||||
// TODO: Send an error.
|
||||
// eh?
|
||||
emit authType(WizardCommon::HttpCreds);
|
||||
}
|
||||
if( _checkInstallationRequest && _checkInstallationRequest->isRunning() ) {
|
||||
qDebug() << "ABORTing pending check installation request.";
|
||||
_checkInstallationRequest->abort();
|
||||
}
|
||||
if( _checkRemoteFolderRequest && _checkRemoteFolderRequest->isRunning() ) {
|
||||
qDebug() << "ABORTing pending remote folder check request.";
|
||||
_checkRemoteFolderRequest->abort();
|
||||
}
|
||||
}
|
||||
|
||||
ValidateDavAuthJob::ValidateDavAuthJob(Account *account, QObject *parent)
|
||||
: AbstractNetworkJob(account, QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ValidateDavAuthJob::start()
|
||||
{
|
||||
QNetworkReply *reply = getRequest(Account::davPath());
|
||||
setReply(reply);
|
||||
setupConnections(reply);
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void ValidateDavAuthJob::finished()
|
||||
{
|
||||
emit authResult(reply());
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -22,39 +22,10 @@
|
||||
#include <QPointer>
|
||||
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
|
||||
#include "wizard/owncloudwizardcommon.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class OwncloudWizard;
|
||||
class Account;
|
||||
|
||||
class ValidateDavAuthJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ValidateDavAuthJob(Account* account, QObject *parent = 0);
|
||||
void start();
|
||||
signals:
|
||||
void authResult(QNetworkReply*);
|
||||
private slots:
|
||||
void finished();
|
||||
};
|
||||
|
||||
class DetermineAuthTypeJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DetermineAuthTypeJob(Account *account, QObject *parent = 0);
|
||||
void start();
|
||||
signals:
|
||||
void authType(WizardCommon::AuthType);
|
||||
private slots:
|
||||
void finished();
|
||||
private:
|
||||
int _redirects;
|
||||
};
|
||||
|
||||
|
||||
class OwncloudSetupWizard : public QObject
|
||||
{
|
||||
@@ -69,17 +40,18 @@ signals:
|
||||
|
||||
private slots:
|
||||
void slotDetermineAuthType(const QString&);
|
||||
void slotOwnCloudFoundAuth(const QUrl&, const QVariantMap&);
|
||||
void slotNoOwnCloudFoundAuth(QNetworkReply *reply);
|
||||
void slotNoOwnCloudFoundAuthTimeout(const QUrl&url);
|
||||
void slotOwnCloudFoundAuth(const QString&, const QString&, const QString&, const QString&);
|
||||
void slotAuthCheckReplyFinished();
|
||||
void slotNoOwnCloudFoundAuth(QNetworkReply*);
|
||||
|
||||
void slotConnectToOCUrl(const QString&);
|
||||
void slotConnectionCheck(QNetworkReply*);
|
||||
void slotConnectionCheck(const QString&, QNetworkReply*);
|
||||
|
||||
void slotCreateLocalAndRemoteFolders(const QString&, const QString&);
|
||||
void slotAuthCheckReply(QNetworkReply*);
|
||||
void slotAuthCheckReply(const QString&, QNetworkReply*);
|
||||
void slotCreateRemoteFolderFinished(QNetworkReply::NetworkError);
|
||||
void slotAssistantFinished( int );
|
||||
void slotClearPendingRequests();
|
||||
|
||||
private:
|
||||
explicit OwncloudSetupWizard(QObject *parent = 0 );
|
||||
@@ -87,15 +59,16 @@ private:
|
||||
|
||||
void startWizard();
|
||||
void testOwnCloudConnect();
|
||||
void createRemoteFolder();
|
||||
void checkRemoteFolder(const QString& remoteFolder);
|
||||
bool createRemoteFolder();
|
||||
void finalizeSetup( bool );
|
||||
bool ensureStartFromScratch(const QString &localFolder);
|
||||
void replaceDefaultAccountWith(Account *newAccount);
|
||||
|
||||
Account* _account;
|
||||
OwncloudWizard* _ocWizard;
|
||||
QPointer<QNetworkReply> _mkdirRequestReply;
|
||||
QPointer<QNetworkReply> _checkInstallationRequest;
|
||||
QPointer<QNetworkReply> _checkRemoteFolderRequest;
|
||||
QString _configHandle;
|
||||
QString _remoteFolder;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ QString ownCloudTheme::about() const
|
||||
"Based on Mirall by Duncan Mac-Vicar P.</small></p>"
|
||||
"%7"
|
||||
)
|
||||
.arg(MIRALL_VERSION_STRING)
|
||||
.arg(MIRALL_STRINGIFY(MIRALL_VERSION))
|
||||
.arg("http://" MIRALL_STRINGIFY(APPLICATION_DOMAIN))
|
||||
.arg(MIRALL_STRINGIFY(APPLICATION_DOMAIN))
|
||||
.arg(devString);
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
namespace Mirall {
|
||||
|
||||
ProgressDispatcher* ProgressDispatcher::_instance = 0;
|
||||
QString Progress::asResultString( const Progress::Info& progress)
|
||||
QString Progress::asResultString( Kind kind )
|
||||
{
|
||||
QString re;
|
||||
|
||||
switch(progress.kind) {
|
||||
switch(kind) {
|
||||
case Download:
|
||||
case EndDownload:
|
||||
re = QCoreApplication::translate( "progress", "Download");
|
||||
@@ -58,12 +58,6 @@ QString Progress::asResultString( const Progress::Info& progress)
|
||||
case EndDelete:
|
||||
re = QCoreApplication::translate( "progress", "deleted");
|
||||
break;
|
||||
case StartRename:
|
||||
re = QCoreApplication::translate( "progress", "Moved to %1").arg(progress.rename_target);
|
||||
break;
|
||||
case EndRename:
|
||||
re = QCoreApplication::translate( "progress", "Moved to %1").arg(progress.rename_target);
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
@@ -112,27 +106,12 @@ QString Progress::asActionString( Kind kind )
|
||||
case EndDelete:
|
||||
re = QCoreApplication::translate( "progress", "deleted");
|
||||
break;
|
||||
case StartRename:
|
||||
re = QCoreApplication::translate( "progress", "move");
|
||||
break;
|
||||
case EndRename:
|
||||
re = QCoreApplication::translate( "progress", "moved");
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
bool Progress::isErrorKind( Kind kind )
|
||||
{
|
||||
bool re = false;
|
||||
if( kind == SoftError || kind == NormalError || kind == FatalError ) {
|
||||
re = true;
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
ProgressDispatcher* ProgressDispatcher::instance() {
|
||||
if (!_instance) {
|
||||
_instance = new ProgressDispatcher();
|
||||
@@ -168,57 +147,58 @@ QList<Progress::SyncProblem> ProgressDispatcher::recentProblems(int count)
|
||||
return _recentProblems;
|
||||
}
|
||||
|
||||
void ProgressDispatcher::setProgressProblem(const QString& folder, const Progress::SyncProblem &problem)
|
||||
{
|
||||
Q_ASSERT( Progress::isErrorKind(problem.kind));
|
||||
|
||||
_recentProblems.prepend( problem );
|
||||
if( _recentProblems.size() > _QueueSize ) {
|
||||
_recentProblems.removeLast();
|
||||
}
|
||||
emit progressSyncProblem( folder, problem );
|
||||
}
|
||||
|
||||
void ProgressDispatcher::setProgressInfo(const QString& folder, const Progress::Info& progress)
|
||||
{
|
||||
if( folder.isEmpty() ) {
|
||||
return;
|
||||
}
|
||||
Progress::Info newProgress(progress);
|
||||
Progress::Info newProgress = progress;
|
||||
|
||||
Q_ASSERT( !Progress::isErrorKind(progress.kind));
|
||||
if( newProgress.kind == Progress::Error ) {
|
||||
Progress::SyncProblem err;
|
||||
err.folder = folder;
|
||||
err.current_file = newProgress.current_file;
|
||||
// its really
|
||||
err.error_message = QString::fromLocal8Bit( (const char*)newProgress.file_size );
|
||||
err.error_code = newProgress.current_file_bytes;
|
||||
err.timestamp = QDateTime::currentDateTime();
|
||||
|
||||
if( newProgress.kind == Progress::StartSync ) {
|
||||
_recentProblems.clear();
|
||||
_timer.start();
|
||||
}
|
||||
if( newProgress.kind == Progress::EndSync ) {
|
||||
newProgress.overall_current_bytes = newProgress.overall_transmission_size;
|
||||
newProgress.current_file_no = newProgress.overall_file_count;
|
||||
_currentAction.remove(newProgress.folder);
|
||||
qint64 msecs = _timer.elapsed();
|
||||
|
||||
qDebug()<< "[PROGRESS] progressed " << newProgress.overall_transmission_size
|
||||
<< " bytes in " << newProgress.overall_file_count << " files"
|
||||
<< " in msec " << msecs;
|
||||
}
|
||||
if( newProgress.kind == Progress::EndDownload ||
|
||||
newProgress.kind == Progress::EndUpload ||
|
||||
newProgress.kind == Progress::EndDelete ||
|
||||
newProgress.kind == Progress::EndRename ) {
|
||||
_recentChanges.prepend(newProgress);
|
||||
if( _recentChanges.size() > _QueueSize ) {
|
||||
_recentChanges.removeLast();
|
||||
_recentProblems.prepend( err );
|
||||
if( _recentProblems.size() > _QueueSize ) {
|
||||
_recentProblems.removeLast();
|
||||
}
|
||||
}
|
||||
// store the last real action to help clients that start during
|
||||
// the Context-phase of an upload or download.
|
||||
if( newProgress.kind != Progress::Context ) {
|
||||
_currentAction[folder] = newProgress.kind;
|
||||
}
|
||||
emit progressSyncProblem( folder, err );
|
||||
} else {
|
||||
if( newProgress.kind == Progress::StartSync ) {
|
||||
_recentProblems.clear();
|
||||
_timer.start();
|
||||
}
|
||||
if( newProgress.kind == Progress::EndSync ) {
|
||||
newProgress.overall_current_bytes = newProgress.overall_transmission_size;
|
||||
newProgress.current_file_no = newProgress.overall_file_count;
|
||||
_currentAction.remove(newProgress.folder);
|
||||
qint64 msecs = _timer.elapsed();
|
||||
|
||||
emit progressInfo( folder, newProgress );
|
||||
qDebug()<< "[PROGRESS] progressed " << newProgress.overall_transmission_size
|
||||
<< " bytes in " << newProgress.overall_file_count << " files"
|
||||
<< " in msec " << msecs;
|
||||
}
|
||||
if( newProgress.kind == Progress::EndDownload ||
|
||||
newProgress.kind == Progress::EndUpload ||
|
||||
newProgress.kind == Progress::EndDelete ) {
|
||||
_recentChanges.prepend(newProgress);
|
||||
if( _recentChanges.size() > _QueueSize ) {
|
||||
_recentChanges.removeLast();
|
||||
}
|
||||
}
|
||||
// store the last real action to help clients that start during
|
||||
// the Context-phase of an upload or download.
|
||||
if( newProgress.kind != Progress::Context ) {
|
||||
_currentAction[folder] = newProgress.kind;
|
||||
}
|
||||
|
||||
emit progressInfo( folder, newProgress );
|
||||
}
|
||||
}
|
||||
|
||||
Progress::Kind ProgressDispatcher::currentFolderContext( const QString& folder )
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Mirall {
|
||||
*/
|
||||
namespace Progress
|
||||
{
|
||||
enum Kind {
|
||||
typedef enum {
|
||||
Invalid,
|
||||
StartSync,
|
||||
Download,
|
||||
@@ -42,19 +42,13 @@ namespace Progress
|
||||
EndSync,
|
||||
StartDelete,
|
||||
EndDelete,
|
||||
StartRename,
|
||||
EndRename,
|
||||
SoftError,
|
||||
NormalError,
|
||||
FatalError
|
||||
};
|
||||
Error
|
||||
} Kind;
|
||||
|
||||
struct Info {
|
||||
typedef struct {
|
||||
Kind kind;
|
||||
QString folder;
|
||||
QString current_file;
|
||||
QString rename_target;
|
||||
|
||||
qint64 file_size;
|
||||
qint64 current_file_bytes;
|
||||
|
||||
@@ -65,26 +59,18 @@ namespace Progress
|
||||
|
||||
QDateTime timestamp;
|
||||
|
||||
Info() : kind(Invalid), file_size(0), current_file_bytes(0),
|
||||
overall_file_count(0), current_file_no(0),
|
||||
overall_transmission_size(0), overall_current_bytes(0) { }
|
||||
};
|
||||
} Info;
|
||||
|
||||
struct SyncProblem {
|
||||
Kind kind;
|
||||
typedef struct {
|
||||
QString folder;
|
||||
QString current_file;
|
||||
QString error_message;
|
||||
int error_code;
|
||||
QDateTime timestamp;
|
||||
|
||||
SyncProblem() : kind(Invalid), error_code(0) {}
|
||||
};
|
||||
} SyncProblem;
|
||||
|
||||
QString asActionString( Kind );
|
||||
QString asResultString( const Progress::Info& progress );
|
||||
|
||||
bool isErrorKind( Kind );
|
||||
QString asResultString( Kind );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +95,6 @@ public:
|
||||
QList<Progress::SyncProblem> recentProblems(int count);
|
||||
|
||||
Progress::Kind currentFolderContext( const QString& folder );
|
||||
|
||||
signals:
|
||||
/**
|
||||
@brief Signals the progress of data transmission.
|
||||
@@ -123,8 +108,7 @@ signals:
|
||||
void progressSyncProblem( const QString& folder, const Progress::SyncProblem& problem );
|
||||
|
||||
protected:
|
||||
void setProgressInfo(const QString& folder, const Progress::Info& progress);
|
||||
void setProgressProblem( const QString& folder, const Progress::SyncProblem& problem);
|
||||
void setProgressInfo(const QString &folder, const Progress::Info& progress);
|
||||
|
||||
private:
|
||||
ProgressDispatcher(QObject* parent = 0);
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
/*
|
||||
* 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 <QtGui>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <QtWidgets>
|
||||
#endif
|
||||
|
||||
#include "mirall/protocolwidget.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/syncfileitem.h"
|
||||
#include "mirall/folder.h"
|
||||
|
||||
#include "ui_protocolwidget.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
ProtocolWidget::ProtocolWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ErrorIndicatorRole( Qt::UserRole +1 ),
|
||||
_ui(new Ui::ProtocolWidget)
|
||||
{
|
||||
_ui->setupUi(this);
|
||||
|
||||
connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString,Progress::Info)),
|
||||
this, SLOT(slotProgressInfo(QString,Progress::Info)));
|
||||
connect(ProgressDispatcher::instance(), SIGNAL(progressSyncProblem(const QString&,const Progress::SyncProblem&)),
|
||||
this, SLOT(slotProgressProblem(const QString&, const Progress::SyncProblem&)));
|
||||
|
||||
connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SLOT(slotOpenFile(QTreeWidgetItem*,int)));
|
||||
|
||||
// Adjust copyToClipboard() when making changes here!
|
||||
QStringList header;
|
||||
header << tr("Time");
|
||||
header << tr("File");
|
||||
header << tr("Folder");
|
||||
header << tr("Action");
|
||||
header << tr("Size");
|
||||
|
||||
_ui->_treeWidget->setHeaderLabels( header );
|
||||
_ui->_treeWidget->setColumnWidth(1, 180);
|
||||
_ui->_treeWidget->setColumnCount(5);
|
||||
_ui->_treeWidget->setRootIsDecorated(false);
|
||||
_ui->_treeWidget->setTextElideMode(Qt::ElideMiddle);
|
||||
|
||||
connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
|
||||
|
||||
_clearBlacklistBtn = _ui->_dialogButtonBox->addButton(tr("Retry Sync"), QDialogButtonBox::ActionRole);
|
||||
_clearBlacklistBtn->setEnabled(false);
|
||||
connect(_clearBlacklistBtn, SIGNAL(clicked()), SLOT(slotClearBlacklist()));
|
||||
|
||||
QPushButton *copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
|
||||
copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
|
||||
connect(copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
|
||||
|
||||
}
|
||||
|
||||
void ProtocolWidget::initializeList()
|
||||
{
|
||||
QList<Progress::Info> progressList = ProgressDispatcher::instance()->recentChangedItems(0); // All.
|
||||
QList<QTreeWidgetItem*> items;
|
||||
QTreeWidgetItem *item;
|
||||
|
||||
_ui->_treeWidget->clear();
|
||||
|
||||
QList<Progress::SyncProblem> problemList = ProgressDispatcher::instance()->recentProblems(0);
|
||||
items.clear();
|
||||
foreach( Progress::SyncProblem prob, problemList ) {
|
||||
item = createProblemTreewidgetItem(prob);
|
||||
if (item) {
|
||||
items.append(item);
|
||||
}
|
||||
}
|
||||
_ui->_treeWidget->addTopLevelItems(items);
|
||||
|
||||
foreach( Progress::Info info, progressList ) {
|
||||
item = createProgressTreewidgetItem(info);
|
||||
if(item) {
|
||||
items.append(item);
|
||||
}
|
||||
}
|
||||
_ui->_treeWidget->addTopLevelItems(items);
|
||||
|
||||
computeResyncButtonEnabled();
|
||||
|
||||
}
|
||||
|
||||
ProtocolWidget::~ProtocolWidget()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void ProtocolWidget::copyToClipboard()
|
||||
{
|
||||
QString text;
|
||||
QTextStream ts(&text);
|
||||
|
||||
int topLevelItems = _ui->_treeWidget->topLevelItemCount();
|
||||
for (int i = 0; i < topLevelItems; i++) {
|
||||
QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i);
|
||||
ts << left
|
||||
// time stamp
|
||||
<< qSetFieldWidth(10)
|
||||
<< child->data(0,Qt::DisplayRole).toString()
|
||||
// file name
|
||||
<< qSetFieldWidth(64)
|
||||
<< child->data(1,Qt::DisplayRole).toString()
|
||||
// folder
|
||||
<< qSetFieldWidth(15)
|
||||
<< child->data(2, Qt::DisplayRole).toString()
|
||||
// action
|
||||
<< qSetFieldWidth(15)
|
||||
<< child->data(3, Qt::DisplayRole).toString()
|
||||
// size
|
||||
<< qSetFieldWidth(10)
|
||||
<< child->data(4, Qt::DisplayRole).toString()
|
||||
<< qSetFieldWidth(0)
|
||||
<< endl;
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setText(text);
|
||||
emit guiLog(tr("Copied to clipboard"), tr("The sync status has been copied to the clipboard."));
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotClearBlacklist()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
Folder::Map folders = folderMan->map();
|
||||
|
||||
foreach( Folder *f, folders ) {
|
||||
int num = f->slotWipeBlacklist();
|
||||
qDebug() << num << "entries were removed from"<< f->alias() << "blacklist";
|
||||
}
|
||||
|
||||
folderMan->slotScheduleAllFolders();
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> ProtocolWidget::errorItems( const QString& folder )
|
||||
{
|
||||
QList<QTreeWidgetItem*> list;
|
||||
|
||||
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
||||
|
||||
for( int cnt = 0; cnt < itemCnt; cnt++ ) {
|
||||
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt);
|
||||
bool isErrorItem = item->data(0, ErrorIndicatorRole).toBool();
|
||||
QString itemFolder = item->data(2, Qt::DisplayRole).toString();
|
||||
if( isErrorItem && itemFolder == folder ) {
|
||||
list.append(item);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void ProtocolWidget::cleanErrorItems( const QString& folder ) // FIXME: Use the folder to detect which errors can be deleted.
|
||||
{
|
||||
QList<QTreeWidgetItem*> wipeList = errorItems(folder);
|
||||
if( wipeList.count() > 0 ) {
|
||||
qDeleteAll(wipeList.begin(), wipeList.end());
|
||||
}
|
||||
}
|
||||
|
||||
QString ProtocolWidget::timeString(QDateTime dt, QLocale::FormatType format) const
|
||||
{
|
||||
QLocale loc = QLocale::system();
|
||||
QString timeStr;
|
||||
QDate today = QDate::currentDate();
|
||||
|
||||
if( format == QLocale::NarrowFormat ) {
|
||||
if( dt.date().day() == today.day() ) {
|
||||
timeStr = loc.toString(dt.time(), QLocale::NarrowFormat);
|
||||
} else {
|
||||
timeStr = loc.toString(dt, QLocale::NarrowFormat);
|
||||
}
|
||||
} else {
|
||||
timeStr = loc.toString(dt, format);
|
||||
}
|
||||
return timeStr;
|
||||
}
|
||||
|
||||
QTreeWidgetItem *ProtocolWidget::createProblemTreewidgetItem( const Progress::SyncProblem& problem)
|
||||
{
|
||||
QStringList columns;
|
||||
QString timeStr = timeString(problem.timestamp);
|
||||
QString longTimeStr = timeString(problem.timestamp, QLocale::LongFormat);
|
||||
|
||||
columns << timeStr;
|
||||
columns << problem.current_file;
|
||||
columns << problem.folder;
|
||||
QString errMsg = problem.error_message;
|
||||
#if 0
|
||||
if( problem.error_code == 507 ) {
|
||||
errMsg = tr("No more storage space available on server.");
|
||||
}
|
||||
#endif
|
||||
columns << errMsg;
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(columns);
|
||||
item->setData(0, ErrorIndicatorRole, QVariant(true) );
|
||||
// Maybe we should not set the error icon for all problems but distinguish
|
||||
// by error_code. A quota problem is considered an error, others might not??
|
||||
if( problem.kind == Progress::SoftError ) {
|
||||
item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Problem, true));
|
||||
} else {
|
||||
item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Error, true));
|
||||
}
|
||||
item->setToolTip(0, longTimeStr);
|
||||
item->setToolTip(1, problem.current_file);
|
||||
item->setToolTip(3, errMsg );
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotProgressProblem( const QString& folder, const Progress::SyncProblem& problem)
|
||||
{
|
||||
Q_UNUSED(folder);
|
||||
QTreeWidgetItem *item = createProblemTreewidgetItem(problem);
|
||||
_ui->_treeWidget->insertTopLevelItem(0, item);
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotOpenFile( QTreeWidgetItem *item, int )
|
||||
{
|
||||
QString folderName = item->text(2);
|
||||
QString fileName = item->text(1);
|
||||
|
||||
Folder *folder = FolderMan::instance()->folder(folderName);
|
||||
if (folder) {
|
||||
QString fullPath = folder->path() + '/' + fileName;
|
||||
if (QFile(fullPath).exists()) {
|
||||
Utility::showInFileManager(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QTreeWidgetItem* ProtocolWidget::createProgressTreewidgetItem( const Progress::Info& progress )
|
||||
{
|
||||
QStringList columns;
|
||||
const QString timeStr = timeString(progress.timestamp);
|
||||
const QString longTimeStr = timeString(progress.timestamp, QLocale::LongFormat);
|
||||
const QString actionStr = Progress::asResultString(progress);
|
||||
|
||||
columns << timeStr;
|
||||
columns << progress.current_file;
|
||||
columns << progress.folder;
|
||||
columns << actionStr;
|
||||
columns << Utility::octetsToString( progress.file_size );
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(columns);
|
||||
item->setToolTip(0, longTimeStr);
|
||||
item->setToolTip(3, actionStr);
|
||||
return item;
|
||||
}
|
||||
|
||||
void ProtocolWidget::computeResyncButtonEnabled()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder::Map folders = folderMan->map();
|
||||
|
||||
int cnt = 0;
|
||||
foreach( Folder *f, folders ) {
|
||||
cnt += f->blackListEntryCount();
|
||||
}
|
||||
|
||||
QString t = tr("Currently no files are ignored because of previous errors.");
|
||||
if(cnt > 0) {
|
||||
t = tr("%1 files are ignored because of previous errors.\n Try to sync these again.").arg(cnt);
|
||||
}
|
||||
|
||||
_clearBlacklistBtn->setEnabled(cnt > 0);
|
||||
_clearBlacklistBtn->setToolTip(t);
|
||||
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotProgressInfo( const QString& folder, const Progress::Info& progress )
|
||||
{
|
||||
if( progress.kind == Progress::StartSync ) {
|
||||
cleanErrorItems( folder );
|
||||
_clearBlacklistBtn->setEnabled(false);
|
||||
}
|
||||
|
||||
if( progress.kind == Progress::EndSync ) {
|
||||
computeResyncButtonEnabled();
|
||||
}
|
||||
|
||||
// Ingore other events than finishing an individual up- or download.
|
||||
if( !(progress.kind == Progress::EndDownload || progress.kind == Progress::EndUpload
|
||||
|| progress.kind == Progress::EndDelete || progress.kind == Progress::EndRename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTreeWidgetItem *item = createProgressTreewidgetItem(progress);
|
||||
if(item) {
|
||||
_ui->_treeWidget->insertTopLevelItem(0, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Mirall::ProtocolWidget</class>
|
||||
<widget class="QWidget" name="Mirall::ProtocolWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>612</width>
|
||||
<height>515</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Sync Activity</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTreeWidget" name="_treeWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QDialogButtonBox" name="_dialogButtonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::NoButton</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Daniel Molkentin <danimo@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 "mirall/quotainfo.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
namespace {
|
||||
static const int defaultIntervalT = 30*1000;
|
||||
static const int failIntervalT = 5*1000;
|
||||
static const int initialTimeT = 1*1000;
|
||||
}
|
||||
|
||||
QuotaInfo::QuotaInfo(QObject *parent)
|
||||
: QObject(parent)
|
||||
, _account(AccountManager::instance()->account())
|
||||
, _lastQuotaTotalBytes(0)
|
||||
, _lastQuotaUsedBytes(0)
|
||||
, _refreshTimer(new QTimer(this))
|
||||
{
|
||||
connect(AccountManager::instance(), SIGNAL(accountChanged(Account*,Account*)),
|
||||
SLOT(slotAccountChanged(Account*,Account*)));
|
||||
connect(_refreshTimer, SIGNAL(timeout()), SLOT(slotCheckQuota()));
|
||||
_refreshTimer->setSingleShot(true);
|
||||
_refreshTimer->start(initialTimeT);
|
||||
}
|
||||
|
||||
void QuotaInfo::slotAccountChanged(Account *newAccount, Account *oldAccount)
|
||||
{
|
||||
_account = newAccount;
|
||||
disconnect(oldAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int)));
|
||||
connect(newAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int)));
|
||||
}
|
||||
|
||||
void QuotaInfo::slotAccountStateChanged(int state)
|
||||
{
|
||||
if (state == Account::Connected) {
|
||||
slotCheckQuota();
|
||||
} else {
|
||||
_refreshTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void QuotaInfo::slotCheckQuota()
|
||||
{
|
||||
if (!_account.isNull() && _account->credentials() && _account->credentials()->ready()) {
|
||||
CheckQuotaJob *job = new CheckQuotaJob(_account, "/", this);
|
||||
connect(job, SIGNAL(quotaRetrieved(qint64,qint64)), SLOT(slotUpdateLastQuota(qint64,qint64)));
|
||||
job->start();
|
||||
_refreshTimer->start(defaultIntervalT);
|
||||
} else {
|
||||
_lastQuotaTotalBytes = 0;
|
||||
_lastQuotaUsedBytes = 0;
|
||||
_refreshTimer->start(failIntervalT);
|
||||
}
|
||||
}
|
||||
|
||||
void QuotaInfo::slotUpdateLastQuota(qint64 total, qint64 used)
|
||||
{
|
||||
_lastQuotaTotalBytes = total;
|
||||
_lastQuotaUsedBytes = used;
|
||||
emit quotaUpdated(total, used);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Daniel Molkentin <danimo@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 <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
class QTimer;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class Account;
|
||||
|
||||
class QuotaInfo : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QuotaInfo(QObject *parent);
|
||||
|
||||
qint64 lastQuotaTotalBytes() const { return _lastQuotaTotalBytes; }
|
||||
qint64 lastQuotaUsedBytes() const { return _lastQuotaUsedBytes; }
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotUpdateLastQuota(qint64 total, qint64 used);
|
||||
void slotCheckQuota();
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotAccountChanged(Account *newAccount, Account *oldAccount);
|
||||
void slotAccountStateChanged(int state);
|
||||
|
||||
Q_SIGNALS:
|
||||
void quotaUpdated(qint64 total, qint64 used);
|
||||
|
||||
private:
|
||||
QPointer<Account> _account;
|
||||
qint64 _lastQuotaTotalBytes;
|
||||
qint64 _lastQuotaUsedBytes;
|
||||
QTimer *_refreshTimer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace Mirall
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user