Comparar commits

..

11 Commits

Autor SHA1 Mensagem Data
Daniel Molkentin 06ce09651e Merge remote-tracking branch 'origin/master' into libsplit
Conflicts:
	src/gui/folder.cpp
2015-10-20 22:21:37 +02:00
Sebastian Kügler c9eef1aca2 Include remaining necessary classes in libclient 2015-09-25 16:45:45 +02:00
Sebastian Kügler f8329bf8b5 Add libowncloudclient
This is an option right now, and not used by the desktop client (this
switch can come later).
2015-09-25 04:26:29 +02:00
Sebastian Kügler c3ab89c965 Fix include 2015-09-25 04:24:31 +02:00
Sebastian Kügler 2b4f0e6ea4 Fix includes 2015-09-25 04:24:01 +02:00
Sebastian Kügler 63a67da3b8 Install cross-referenced headers 2015-09-25 04:22:10 +02:00
Sebastian Kügler 9d62c519f8 Include the private header, but only in the .cpp files 2015-09-25 04:21:53 +02:00
Sebastian Kügler 3b1a78e3b4 Do not install private header
This is actually not needed in any of the headers, so we can forego
installing it. We do need the config_ header, and the std/c_private.h
one, though.
2015-09-25 04:20:43 +02:00
Sebastian Kügler 695223527f Install owncloudlib.h 2015-09-25 02:54:10 +02:00
Sebastian Kügler 79d8b0d261 include csync_private.h from csync.h
This allows to remove the private includes, and will make it possible to
merge these headers later on without breaking includes. It's a bit of a
hack, but makes the separation a bit cleaner.
2015-09-25 01:19:09 +02:00
Sebastian Kügler f37a66bd5c Install csync headers
- installs headers for non-osx bundles
- also installs csync_private.h, since it's needed in the owncloud
  client (this is a sign that it warrants rethinking the public API, and
  what's supposed to be private, and why.
2015-09-25 01:12:33 +02:00
221 arquivos alterados com 31600 adições e 68122 exclusões
+7
Ver Arquivo
@@ -133,9 +133,13 @@ endif()
#endif()
set(USE_NEON TRUE)
if(HAVE_QT5)
message(STATUS "Using Qt ${Qt5Core_VERSION_MAJOR}.${Qt5Core_VERSION_MINOR}.x")
if (${Qt5Core_VERSION_MAJOR} EQUAL "5")
if (${Qt5Core_VERSION_MINOR} EQUAL "4" OR ${Qt5Core_VERSION_MINOR} GREATER 4)
message(STATUS "We do not require Neon in this setup, compile without!")
set(USE_NEON FALSE)
else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
@@ -144,6 +148,9 @@ else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
if (USE_NEON)
find_package(Neon REQUIRED)
endif(USE_NEON)
find_package(OpenSSL 1.0.0 REQUIRED)
if(NOT TOKEN_AUTH_ONLY)
+32 -38
Ver Arquivo
@@ -1,55 +1,49 @@
ChangeLog
=========
version 2.1 (release 2015-yy-zz)
* We removed the old libneon-based propagator.
It was already disabled on OS X and Windows and only used on
Linux when building with Qt < 5.4 and having bandwidth limiting enabled.
So now you need Qt 5.4 on Linux if you want bandwidth limiting.
version 2.0.2 (release 2015-10-21)
version 2.0.2 (release 2015-10-xx)
* csync_file_stat_s: Save a bit of memory
* Shibboleth: Add our base user agent to WebKit
* SelectiveSync: Increase folder list timeout to 60
* Propagation: Try another sync on 423 Locked (#3387)
* Propagation: Make 423 Locked a soft error (#3387)
* Propagation: Reset upload blacklist if a chunk succeeds
* Application: Fix crash on early shutdown (#3898)
* Linux: Don't show settings dialog always when launched twice (#3273, #3771, #3485)
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. (#3813)
* Propagation: Try another sync on 423 Locked #3387
* Propagation: Make 423 Locked a soft error #3387
* Propagation: Reset upload blacklist if a chunk suceeds
* Application: Fix crash on early shutdown #3898
* Linux: Don't show settings dialog always when launched twice #3273 #3771 #3485
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. #3813
* AccountSettings: only expand root elements on single click.
* AccountSettings: Do not allow to expand the folder list when disconnected.
* Use application SHORT name for the name of the MacOSX pkg file (ownBrander).
* FolderMan: Fix for removing a syncing folder (#3843)
* ConnectionMethodDialog: Don't be insecure on close (#3863)
* Updater: Ensure folders are not removed (#3747)
* Folder settings: Ensure path is cleaned (#3811)
* Propagator: Simplify sub job finished counting (#3844)
* Share dialog: Hide settings dialog before showing (#3783)
* UI: Only expand 1 level in folder list (#3585)
* UI: Allow folder expanding from button click (#3585)
* UI: Expand folder treeview on single click (#3585)
* GUI: Change tray menu order (#3657)
* FolderMan: Fix for removing a syncing folder #3843
* ConnectionMethodDialog: Don't be insecure on close #3863
* Updater: Ensure folders are not removed #3747
* Folder settings: Ensure path is cleaned #3811
* Propagator: Simplify sub job finished counting #3844
* Share dialog: Hide settings dialog before showing #3783
* UI: Only expand 1 level in folder list #3585
* UI: Allow folder expanding from button click #3585
* UI: Expand folder treeview on single click #3585
* GUI: Change tray menu order #3657
* GUI: Replace term "sign in" with "Log in" and friends.
* SetupPage: Fix crash caused by uninitialized Account object.
* Use a themable WebDAV path all over.
* Units: Back to the "usual" mix units (JEDEC standard).
* csync io: Full UNC path support on Win (#3748)
* Tray: Don't use the tray workaround with the KDE theme (#3706, #3765)
* ShareDialog: Fix folder display (#3659)
* AccountSettings: Restore from legacy only once (#3565)
* SSL Certificate Error Dialog: show account name (#3729)
* Tray notification: Don't show a message about modified folder (#3613)
* csync io: Full UNC path support on Win #3748
* Tray: Don't use the tray workaround with the KDE theme #3706, #3765
* ShareDialog: Fix folder display #3659
* AccountSettings: Restore from legacy only once #3565
* SSL Certificate Error Dialog: show account name #3729
* Tray notification: Don't show a message about modified folder #3613
* PropagateLocalRemove: remove entries from the DB even if there was an error.
* Settings UI improvements (eg. #3713, #3721, #3619 and others)
* Folder: Do not create the sync folder if it does not exist (#3692)
* Shell integration: don't show share menu item for top level folders
* Tray: Hide while modifying menus (#3656, #3672)
* AddFolder: Improve remote path selection error handling (#3573)
* csync_update: Use excluded_traversal() to improve performance (#3638)
* csync_excluded: Add fast _traversal() function (#3638)
* csync_exclude: Speed up significantly (#3638)
* AccountSettings: Adjust quota info design (#3644, #3651)
* Adjust buttons on remove folder/account questions (#3654)
* Folder: Do not create the sync folder if it does not exist #3692
* Shell integratioon: don't show share menu item for top level folders
* Tray: Hide while modifying menus #3656 #3672
* AddFolder: Improve remote path selection error handling #3573
* csync_update: Use excluded_traversal() to improve performance #3638
* csync_excluded: Add fast _traversal() function #3638
* csync_exclude: Speed up siginificantly #3638
* AccountSettings: Adjust quota info design #3644 #3651
* Adjust buttons on remove folder/account questions #3654
version 2.0.1 (release 2015-09-01)
* AccountWizard: fix when the theme specify a override URL (#3699)
+1 -1
Ver Arquivo
@@ -4,7 +4,7 @@
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| client-build-matrix | [![Build Status](https://ci.owncloud.org/job/client-build-matrix-linux/badge/icon)](https://ci.owncloud.org/job/client-build-matrix-linux/) |
| client-test-matrix-linux-no-build | [![Build Status](https://ci.owncloud.org/buildStatus/icon?job=client-test-matrix-linux-no-build)](https://ci.owncloud.org/job/client-test-matrix-linux-no-build/) |
| coverity_scan | [![Build Status](https://img.shields.io/coverity/scan/2482.svg)](https://scan.coverity.com/projects/owncloud-mirall)
## Introduction
+1 -1
Ver Arquivo
@@ -4,7 +4,7 @@ set( MIRALL_VERSION_PATCH 0 )
set( MIRALL_SOVERSION 0 )
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
set( MIRALL_VERSION_SUFFIX "beta1") #e.g. beta1, beta2, rc1
set( MIRALL_VERSION_SUFFIX "git") #e.g. beta1, beta2, rc1
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
if( NOT DEFINED MIRALL_VERSION_BUILD )
-6
Ver Arquivo
@@ -38,8 +38,6 @@ QT_PLUGINS = [
'imageformats/libqgif.dylib',
'imageformats/libqico.dylib',
'imageformats/libqjpeg.dylib',
'bearer/libqcorewlanbearer.dylib',
'bearer/libqgenericbearer.dylib',
'imageformats/libqsvg.dylib',
'imageformats/libqmng.dylib',
]
@@ -146,10 +144,6 @@ def FindFramework(path):
search_pathes = FRAMEWORK_SEARCH_PATH
search_pathes.insert(0, QueryQMake('QT_INSTALL_LIBS'))
for search_path in search_pathes:
# The following two lines are needed for a custom built Qt from version 5.5 on, possibly not for the one from the Qt SDK.
# Looks like the upstream macdeployqt also had an issue there https://bugreports.qt.io/browse/QTBUG-47868
if path.find( "\@rpath/"):
path = path.replace("@rpath/", "")
abs_path = os.path.join(search_path, path)
if os.path.exists(abs_path):
return abs_path
+4
Ver Arquivo
@@ -32,3 +32,7 @@ SET(QT_MOC_EXECUTABLE ${MINGW_PREFIX}-moc)
SET(QT_RCC_EXECUTABLE ${MINGW_PREFIX}-rcc)
SET(QT_UIC_EXECUTABLE ${MINGW_PREFIX}-uic)
SET(QT_LRELEASE_EXECUTABLE ${MINGW_PREFIX}-lrelease)
# neon config
SET(NEON_CONFIG_EXECUTABLE ${CMAKE_FIND_ROOT_PATH}/bin/neon-config)
# /usr/i686-w64-mingw32/sys-root/mingw/bin/neon-config
-2
Ver Arquivo
@@ -20,7 +20,5 @@
<file>resources/lock-https.png</file>
<file>resources/lock-https@2x.png</file>
<file>resources/account.png</file>
<file>resources/more.png</file>
<file>resources/delete.png</file>
</qresource>
</RCC>
+73
Ver Arquivo
@@ -0,0 +1,73 @@
# - 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-2013 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 neon-27
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)
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 )
-5
Ver Arquivo
@@ -40,7 +40,6 @@
!define QT_DLL_PATH "${MING_BIN}"
!define ACCESSIBLE_DLL_PATH "${MING_LIB}/qt5/plugins/accessible"
!define SQLITE_DLL_PATH "${MING_LIB}/qt5/plugins/sqldrivers"
!define BEARER_DLL_PATH "${MING_LIB}/qt5/plugins/bearer"
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt5/plugins/imageformats"
!define PLATFORMS_DLL_PATH "${MING_LIB}/qt5/plugins/platforms"
@@ -407,10 +406,6 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
SetOutPath "$INSTDIR\sqldrivers"
File "${SQLITE_DLL_PATH}\qsqlite.dll"
SetOutPath "$INSTDIR\bearer"
File "${BEARER_DLL_PATH}\qgenericbearer.dll"
File "${BEARER_DLL_PATH}\qnativewifibearer.dll"
SetOutPath "$INSTDIR"
;License & release notes.
File "@CPACK_RESOURCE_FILE_LICENSE@"
+1 -5
Ver Arquivo
@@ -22,9 +22,7 @@ if( Qt5Core_FOUND )
find_package(Qt5WebKitWidgets REQUIRED)
find_package(Qt5WebKit REQUIRED)
find_package(Qt5PrintSupport REQUIRED)
if(NOT APPLE)
find_package(Qt5Quick REQUIRED) # only needed on Windows because of OBS dependencies(?)
endif()
find_package(Qt5Quick REQUIRED)
find_package(Qt5Widgets REQUIRED)
if(APPLE)
find_package(Qt5MacExtras REQUIRED)
@@ -33,9 +31,7 @@ if( Qt5Core_FOUND )
else( Qt5Core_FOUND )
if(WIN32 OR APPLE)
if (NOT BUILD_WITH_QT4)
message(FATAL_ERROR "Qt 5 not found, but application depends on Qt5 on Windows and Mac OS X")
endif ()
endif(WIN32 OR APPLE)
endif( Qt5Core_FOUND )
+4
Ver Arquivo
@@ -23,4 +23,8 @@
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
#cmakedefine SHAREDIR "@SHAREDIR@"
#ifndef NEON_WITH_LFS
#cmakedefine NEON_WITH_LFS "@NEON_WITH_LFS@"
#endif
#endif
+4
Ver Arquivo
@@ -24,6 +24,10 @@
#cmakedefine HAVE_ICONV 1
#cmakedefine HAVE_ICONV_CONST 1
#ifndef NEON_WITH_LFS
#cmakedefine NEON_WITH_LFS 1
#endif
#cmakedefine HAVE___MINGW_ASPRINTF 1
#cmakedefine HAVE_ASPRINTF 1
+29 -9
Ver Arquivo
@@ -1,6 +1,9 @@
project(libcsync)
add_subdirectory(std)
if(USE_NEON)
add_subdirectory(httpbf)
endif()
# Statically include sqlite
@@ -68,14 +71,25 @@ else()
endif()
if(USE_NEON)
list(APPEND csync_SRCS
csync_owncloud.c
csync_owncloud_util.c
)
list(APPEND CSYNC_LINK_LIBRARIES
${NEON_LIBRARIES}
)
include_directories(${NEON_INCLUDE_DIRS})
add_definitions(-DUSE_NEON)
endif(USE_NEON)
configure_file(csync_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/csync_version.h)
set(csync_HDRS
${CMAKE_CURRENT_BINARY_DIR}/csync_version.h
${CMAKE_CURRENT_BINARY_DIR}/../config_csync.h
csync.h
vio/csync_vio.h
vio/csync_vio_method.h
vio/csync_vio_module.h
)
# Statically include sqlite
@@ -126,12 +140,18 @@ else()
RUNTIME DESTINATION
${BIN_INSTALL_DIR}/${APPLICATION_EXECUTABLE}
)
INSTALL(
FILES
${csync_HDRS}
DESTINATION
${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME}
)
INSTALL(
FILES
std/c_private.h
DESTINATION
${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME}/std
)
endif()
# INSTALL(
# FILES
# ${csync_HDRS}
# DESTINATION
# ${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME}
# )
+47
Ver Arquivo
@@ -58,6 +58,12 @@
#include "csync_rename.h"
#include "c_jhash.h"
#ifdef USE_NEON
// Breaking the abstraction for fun and profit.
#include "csync_owncloud.h"
#endif
static int _key_cmp(const void *key, const void *data) {
uint64_t a;
csync_file_stat_t *b;
@@ -148,6 +154,9 @@ int csync_init(CSYNC *ctx) {
ctx->local.type = LOCAL_REPLICA;
#ifdef USE_NEON
owncloud_init(ctx);
#endif
ctx->remote.type = REMOTE_REPLICA;
if (c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp) < 0) {
@@ -207,6 +216,14 @@ int csync_update(CSYNC *ctx) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "No exclude file loaded or defined!");
}
#ifdef USE_NEON
/* This is not actually connecting, just setting the info for neon. The legacy propagator can use it.. */
if (dav_connect( ctx, ctx->remote.uri ) < 0) {
ctx->status_code = CSYNC_STATUS_CONNECT_ERROR;
return -1;
}
#endif
/* update detection for local replica */
csync_gettime(&start);
ctx->current = LOCAL_REPLICA;
@@ -629,6 +646,10 @@ int csync_destroy(CSYNC *ctx) {
SAFE_FREE(ctx->remote.uri);
SAFE_FREE(ctx->error_string);
#ifdef USE_NEON
owncloud_destroy(ctx);
#endif
#ifdef WITH_ICONV
c_close_iconv();
#endif
@@ -651,6 +672,21 @@ void csync_clear_exclude_list(CSYNC *ctx)
csync_exclude_clear(ctx);
}
int csync_set_auth_callback(CSYNC *ctx, csync_auth_callback cb) {
if (ctx == NULL || cb == NULL) {
return -1;
}
if (ctx->status & CSYNC_STATUS_INIT) {
ctx->status_code = CSYNC_STATUS_CSYNC_STATUS_ERROR;
fprintf(stderr, "This function must be called before initialization.");
return -1;
}
ctx->callbacks.auth_function = cb;
return 0;
}
void *csync_get_userdata(CSYNC *ctx) {
if (ctx == NULL) {
return NULL;
@@ -745,3 +781,14 @@ void csync_file_stat_free(csync_file_stat_t *st)
SAFE_FREE(st);
}
}
int csync_set_module_property(CSYNC* ctx, const char* key, void* value)
{
#ifdef USE_NEON
return owncloud_set_property(ctx, key, value);
#else
(void)ctx, (void)key, (void)value;
return 0;
#endif
}
+15 -1
Ver Arquivo
@@ -173,7 +173,7 @@ enum csync_vio_file_stat_fields_e {
CSYNC_VIO_FILE_STAT_FIELDS_TYPE = 1 << 0,
CSYNC_VIO_FILE_STAT_FIELDS_MODE = 1 << 1, // local POSIX mode
CSYNC_VIO_FILE_STAT_FIELDS_FLAGS = 1 << 2,
// CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3,
CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3,
CSYNC_VIO_FILE_STAT_FIELDS_INODE = 1 << 4,
// CSYNC_VIO_FILE_STAT_FIELDS_LINK_COUNT = 1 << 5,
CSYNC_VIO_FILE_STAT_FIELDS_SIZE = 1 << 6,
@@ -212,6 +212,7 @@ struct csync_vio_file_stat_s {
mode_t mode;
dev_t device;
uint64_t inode;
int fields; // actually enum csync_vio_file_stat_fields_e fields;
@@ -518,6 +519,19 @@ const char *csync_get_status_string(CSYNC *ctx);
int csync_set_iconv_codec(const char *from);
#endif
/**
* @brief Set a property to module
*
* @param ctx The csync context.
*
* @param key The property key
*
* @param value An opaque pointer to the data.
*
* @return 0 on success, less than 0 if an error occured.
*/
int csync_set_module_property(CSYNC *ctx, const char *key, void *value);
/**
* @brief Aborts the current sync run as soon as possible. Can be called from another thread.
*
+47 -61
Ver Arquivo
@@ -171,6 +171,7 @@ bool csync_is_windows_reserved_word(const char* filename) {
static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const char *path, int filetype, bool check_leading_dirs) {
size_t i = 0;
const char *p = NULL;
const char *bname = NULL;
size_t blen = 0;
char *conflict = NULL;
@@ -178,6 +179,22 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
CSYNC_EXCLUDE_TYPE type = CSYNC_NOT_EXCLUDED;
for (p = path; *p; p++) {
switch (*p) {
case '\\':
case ':':
case '?':
case '*':
case '"':
case '>':
case '<':
case '|':
return CSYNC_FILE_EXCLUDE_INVALID_CHAR;
default:
break;
}
}
/* split up the path */
bname = strrchr(path, '/');
if (bname) {
@@ -200,7 +217,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
goto out;
}
#ifdef _WIN32
#ifdef _WIN32
// Windows cannot sync files ending in spaces (#2176). It also cannot
// distinguish files ending in '.' from files without an ending,
// as '.' is a separator that is not stored internally, so let's
@@ -214,26 +231,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
match = CSYNC_FILE_EXCLUDE_INVALID_CHAR;
goto out;
}
// Filter out characters not allowed in a filename on windows
const char *p = NULL;
for (p = path; *p; p++) {
switch (*p) {
case '\\':
case ':':
case '?':
case '*':
case '"':
case '>':
case '<':
case '|':
match = CSYNC_FILE_EXCLUDE_INVALID_CHAR;
goto out;
default:
break;
}
}
#endif
#endif
rc = csync_fnmatch(".owncloudsync.log*", bname, 0);
if (rc == 0) {
@@ -266,41 +264,40 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
goto out;
}
c_strlist_t *path_components = NULL;
if (check_leading_dirs) {
/* Build a list of path components to check. */
path_components = c_strlist_new(32);
char *path_split = strdup(path);
size_t len = strlen(path_split);
for (i = len; ; --i) {
// read backwards until a path separator is found
if (i != 0 && path_split[i-1] != '/') {
continue;
}
// check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo'
if (path_split[i] != 0) {
c_strlist_add_grow(&path_components, path_split + i);
}
if (i == 0) {
break;
}
// check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo'
path_split[i-1] = '\0';
c_strlist_add_grow(&path_components, path_split);
/* Build a list of path components to check. */
c_strlist_t *path_components = c_strlist_new(32);
char *path_split = strdup(path);
size_t len = strlen(path_split);
for (i = len; ; --i) {
// read backwards until a path separator is found
if (i != 0 && path_split[i-1] != '/') {
continue;
}
SAFE_FREE(path_split);
// check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo'
if (path_split[i] != 0) {
c_strlist_add_grow(&path_components, path_split + i);
}
if (i == 0 || !check_leading_dirs) {
break;
}
// check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo'
path_split[i-1] = '\0';
c_strlist_add_grow(&path_components, path_split);
}
SAFE_FREE(path_split);
/* Loop over all exclude patterns and evaluate the given path */
for (i = 0; match == CSYNC_NOT_EXCLUDED && i < excludes->count; i++) {
bool match_dirs_only = false;
char *pattern = excludes->vector[i];
char *pattern_stored = c_strdup(excludes->vector[i]);
char* pattern = pattern_stored;
type = CSYNC_FILE_EXCLUDE_LIST;
if (!pattern[0]) { /* empty pattern */
if (strlen(pattern) < 1) {
SAFE_FREE(pattern_stored);
continue;
}
/* Excludes starting with ']' means it can be cleanup */
@@ -312,9 +309,6 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
}
/* Check if the pattern applies to pathes only. */
if (pattern[strlen(pattern)-1] == '/') {
if (!check_leading_dirs && filetype == CSYNC_FTW_TYPE_FILE) {
continue;
}
match_dirs_only = true;
pattern[strlen(pattern)-1] = '\0'; /* Cut off the slash */
}
@@ -332,7 +326,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
}
/* if still not excluded, check each component and leading directory of the path */
if (match == CSYNC_NOT_EXCLUDED && check_leading_dirs) {
if (match == CSYNC_NOT_EXCLUDED) {
size_t j = 0;
if (match_dirs_only && filetype == CSYNC_FTW_TYPE_FILE) {
j = 1; // skip the first entry, which is bname
@@ -344,16 +338,8 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
break;
}
}
} else if (match == CSYNC_NOT_EXCLUDED && !check_leading_dirs) {
rc = csync_fnmatch(pattern, bname, 0);
if (rc == 0) {
match = type;
}
}
if (match_dirs_only) {
/* restore the '/' */
pattern[strlen(pattern)] = '/';
}
SAFE_FREE(pattern_stored);
}
c_strlist_destroy(path_components);
+10 -3
Ver Arquivo
@@ -57,13 +57,20 @@ int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) {
#else /* HAVE_FNMATCH */
#include <shlwapi.h>
int csync_fnmatch(const char *pattern, const char *name, int flags) {
int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) {
wchar_t *pat = NULL;
wchar_t *name = NULL;
BOOL match;
(void) flags;
(void) __flags;
match = PathMatchSpecA(name, pattern);
name = c_utf8_string_to_locale(__name);
pat = c_utf8_string_to_locale(__pattern);
match = PathMatchSpecW(name, pat);
c_free_locale_string(pat);
c_free_locale_string(name);
if(match)
return 0;
else
+617
Ver Arquivo
@@ -0,0 +1,617 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "csync_owncloud.h"
#include "csync_owncloud_private.h"
#include <inttypes.h>
#include "csync_private.h"
#include "csync_version.h"
/*
* helper method to build up a user text for SSL problems, called from the
* verify_sslcert callback.
*/
static void addSSLWarning( char *ptr, const char *warn, int len )
{
char *concatHere = ptr;
int remainingLen = 0;
if( ! (warn && ptr )) return;
remainingLen = len - strlen(ptr);
if( remainingLen <= 0 ) return;
concatHere = ptr + strlen(ptr); /* put the write pointer to the end. */
strncpy( concatHere, warn, remainingLen );
}
/*
* Callback to verify the SSL certificate, called from libneon.
* It analyzes the SSL problem, creates a user information text and passes
* it to the csync callback to ask the user.
*/
#define LEN 4096
static int ssl_callback_by_neon(void *userdata, int failures,
const ne_ssl_certificate *certificate)
{
char problem[LEN];
char buf[MAX(NE_SSL_DIGESTLEN, NE_ABUFSIZ)];
int ret = -1;
const ne_ssl_certificate *cert = certificate;
csync_auth_callback authcb = NULL;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
memset( problem, 0, LEN );
while( cert ) {
addSSLWarning( problem, "There are problems with the SSL certificate:\n", LEN );
if( failures & NE_SSL_NOTYETVALID ) {
addSSLWarning( problem, " * The certificate is not yet valid.\n", LEN );
}
if( failures & NE_SSL_EXPIRED ) {
addSSLWarning( problem, " * The certificate has expired.\n", LEN );
}
if( failures & NE_SSL_UNTRUSTED ) {
addSSLWarning( problem, " * The certificate is not trusted!\n", LEN );
}
if( failures & NE_SSL_IDMISMATCH ) {
addSSLWarning( problem, " * The hostname for which the certificate was "
"issued does not match the hostname of the server\n", LEN );
}
if( failures & NE_SSL_BADCHAIN ) {
addSSLWarning( problem, " * The certificate chain contained a certificate other than the server cert\n", LEN );
}
if( failures & NE_SSL_REVOKED ) {
addSSLWarning( problem, " * The server certificate has been revoked by the issuing authority.\n", LEN );
}
if (ne_ssl_cert_digest(cert, buf) == 0) {
addSSLWarning( problem, "Certificate fingerprint: ", LEN );
addSSLWarning( problem, buf, LEN );
addSSLWarning( problem, "\n", LEN );
}
cert = ne_ssl_cert_signedby( cert );
}
addSSLWarning( problem, "Do you want to accept the certificate chain anyway?\nAnswer yes to do so and take the risk: ", LEN );
if( ctx->csync_ctx ) {
authcb = csync_get_auth_callback( ctx->csync_ctx );
}
if( authcb ){
/* call the csync callback */
DEBUG_WEBDAV("Call the csync callback for SSL problems");
memset( buf, 0, NE_ABUFSIZ );
(*authcb) ( problem, buf, NE_ABUFSIZ-1, 1, 0, csync_get_userdata(ctx->csync_ctx) );
if( buf[0] == 'y' || buf[0] == 'Y') {
ret = 0;
} else {
DEBUG_WEBDAV("Authentication callback replied %s", buf );
}
}
DEBUG_WEBDAV("## VERIFY_SSL CERT: %d", ret );
return ret;
}
/*
* Authentication callback. Is set by ne_set_server_auth to be called
* from the neon lib to authenticate a request.
*/
static int authentication_callback_by_neon( void *userdata, const char *realm, int attempt,
char *username, char *password)
{
char buf[NE_ABUFSIZ];
csync_auth_callback authcb = NULL;
int re = attempt;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
(void) realm;
/* DEBUG_WEBDAV( "Authentication required %s", realm ); */
if( username && password ) {
DEBUG_WEBDAV( "Authentication required %s", username );
if( ctx->dav_session.user ) {
/* allow user without password */
if( strlen( ctx->dav_session.user ) < NE_ABUFSIZ ) {
strcpy( username, ctx->dav_session.user );
}
if( ctx->dav_session.pwd && strlen( ctx->dav_session.pwd ) < NE_ABUFSIZ ) {
strcpy( password, ctx->dav_session.pwd );
}
} else {
authcb = csync_get_auth_callback( ctx->csync_ctx );
if( authcb != NULL ){
/* call the csync callback */
DEBUG_WEBDAV("Call the csync callback for %s", realm );
memset( buf, 0, NE_ABUFSIZ );
(*authcb) ("Enter your username: ", buf, NE_ABUFSIZ-1, 1, 0, csync_get_userdata(ctx->csync_ctx) );
if( strlen(buf) < NE_ABUFSIZ ) {
strcpy( username, buf );
}
memset( buf, 0, NE_ABUFSIZ );
(*authcb) ("Enter your password: ", buf, NE_ABUFSIZ-1, 0, 0, csync_get_userdata(ctx->csync_ctx) );
if( strlen(buf) < NE_ABUFSIZ) {
strcpy( password, buf );
}
} else {
re = 1;
}
}
}
return re;
}
/*
* Authentication callback. Is set by ne_set_proxy_auth to be called
* from the neon lib to authenticate against a proxy. The data to authenticate
* against comes from mirall throught vio_module_init function.
*/
static int proxy_authentication_callback_by_neon( void *userdata, const char *realm, int attempt,
char *username, char *password)
{
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
(void) realm;
if( ctx->dav_session.proxy_user && strlen( ctx->dav_session.proxy_user ) < NE_ABUFSIZ) {
strcpy( username, ctx->dav_session.proxy_user );
if( ctx->dav_session.proxy_pwd && strlen( ctx->dav_session.proxy_pwd ) < NE_ABUFSIZ) {
strcpy( password, ctx->dav_session.proxy_pwd );
}
}
/* NTLM needs several attempts */
return (attempt < 3) ? 0 : -1;
}
/* Configure the proxy depending on the variables */
static int configureProxy( csync_owncloud_ctx_t *ctx, ne_session *session )
{
int port = 8080;
int re = -1;
if( ! session ) return -1;
if( ! ctx->dav_session.proxy_type ) return 0; /* Go by NoProxy per default */
if( ctx->dav_session.proxy_port > 0 ) {
port = ctx->dav_session.proxy_port;
}
if( c_streq(ctx->dav_session.proxy_type, "NoProxy" )) {
DEBUG_WEBDAV("No proxy configured.");
re = 0;
} else if( c_streq(ctx->dav_session.proxy_type, "DefaultProxy") ||
c_streq(ctx->dav_session.proxy_type, "HttpProxy") ||
c_streq(ctx->dav_session.proxy_type, "HttpCachingProxy") ||
c_streq(ctx->dav_session.proxy_type, "Socks5Proxy")) {
if( ctx->dav_session.proxy_host ) {
DEBUG_WEBDAV("%s at %s:%d", ctx->dav_session.proxy_type, ctx->dav_session.proxy_host, port );
if (c_streq(ctx->dav_session.proxy_type, "Socks5Proxy")) {
ne_session_socks_proxy(session, NE_SOCK_SOCKSV5, ctx->dav_session.proxy_host, port,
ctx->dav_session.proxy_user, ctx->dav_session.proxy_pwd);
} else {
ne_session_proxy(session, ctx->dav_session.proxy_host, port );
}
re = 2;
} else {
DEBUG_WEBDAV("%s requested but no proxy host defined.", ctx->dav_session.proxy_type );
/* we used to try ne_system_session_proxy here, but we should rather err out
to behave exactly like the caller. */
}
} else {
DEBUG_WEBDAV( "Unsupported Proxy: %s", ctx->dav_session.proxy_type );
}
return re;
}
/*
* This hook is called for with the response of a request. Here its checked
* if a Set-Cookie header is there for the PHPSESSID. The key is stored into
* the webdav session to be added to subsequent requests.
*/
static void post_request_hook(ne_request *req, void *userdata, const ne_status *status)
{
const char *set_cookie_header = NULL;
const char *sc = NULL;
char *key = NULL;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
if (ctx->dav_session.session_key)
return; /* We already have a session cookie, and we should ignore other ones */
if(!(status && req)) return;
if( status->klass == 2 || status->code == 401 ) {
/* successful request */
set_cookie_header = ne_get_response_header( req, "Set-Cookie" );
if( set_cookie_header ) {
DEBUG_WEBDAV(" Set-Cookie found: %s", set_cookie_header);
/* try to find a ', ' sequence which is the separator of neon if multiple Set-Cookie
* headers are there.
* The following code parses a string like this:
* Set-Cookie: 50ace6bd8a669=p537brtt048jh8srlp2tuep7em95nh9u98mj992fbqc47d1aecp1;
*/
sc = set_cookie_header;
while(sc) {
const char *sc_val = sc;
const char *sc_end = sc_val;
int cnt = 0;
int len = strlen(sc_val); /* The length of the rest of the header string. */
while( cnt < len && *sc_end != ';' && *sc_end != ',') {
cnt++;
sc_end++;
}
if( cnt == len ) {
/* exit: We are at the end. */
sc = NULL;
} else if( *sc_end == ';' ) {
/* We are at the end of the session key. */
int keylen = sc_end-sc_val;
if( key ) {
int oldlen = strlen(key);
key = c_realloc(key, oldlen + 2 + keylen+1);
strcpy(key + oldlen, "; ");
strncpy(key + oldlen + 2, sc_val, keylen);
key[oldlen + 2 + keylen] = '\0';
} else {
key = c_malloc(keylen+1);
strncpy( key, sc_val, keylen );
key[keylen] = '\0';
}
/* now search for a ',' to find a potential other header entry */
while(cnt < len && *sc_end != ',') {
cnt++;
sc_end++;
}
if( cnt < len )
sc = sc_end+2; /* mind the space after the comma */
else
sc = NULL;
} else if( *sc_end == ',' ) {
/* A new entry is to check. */
if( *(sc_end + 1) == ' ') {
sc = sc_end+2;
} else {
/* error condition */
sc = NULL;
}
}
}
}
} else {
DEBUG_WEBDAV("Request failed, don't take session header.");
}
if( key ) {
DEBUG_WEBDAV("----> Session-key: %s", key);
SAFE_FREE(ctx->dav_session.session_key);
ctx->dav_session.session_key = key;
}
}
/*
* this hook is called just after a request has been created, before its sent.
* Here it is used to set the proxy connection header if available.
*/
static void request_created_hook(ne_request *req, void *userdata,
const char *method, const char *requri)
{
// FIXME Can possibly be merged with pre_send_hook
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
(void) method;
(void) requri;
if( !req ) return;
if(ctx->dav_session.proxy_type) {
/* required for NTLM */
ne_add_request_header(req, "Proxy-Connection", "Keep-Alive");
}
}
/*
* this hook is called just before a request has been sent.
* Here it is used to set the session cookie if available.
*/
static void pre_send_hook(ne_request *req, void *userdata,
ne_buffer *header)
{
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
if( !req ) return;
if(ctx->dav_session.session_key) {
ne_buffer_concat(header, "Cookie: ", ctx->dav_session.session_key, "\r\n", NULL);
} else {
DEBUG_WEBDAV("csync pre_send_hook We don't have a Auth Cookie (session_key), this is wrong!");
}
}
static int post_send_hook(ne_request *req, void *userdata,
const ne_status *status)
{
const char *location;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
(void) status;
location = ne_get_response_header(req, "Location");
if( !location ) return NE_OK;
if( ctx->dav_session.redir_callback ) {
if( ctx->dav_session.redir_callback( ctx->csync_ctx, location ) ) {
return NE_REDIRECT;
} else {
return NE_RETRY;
}
}
return NE_REDIRECT;
}
/*
* Connect to a DAV server
* This function sets the flag _connected if the connection is established
* and returns if the flag is set, so calling it frequently is save.
*/
int dav_connect(CSYNC *csyncCtx, const char *base_url) {
int useSSL = 0;
int rc;
char protocol[6] = {'\0'};
char uaBuf[256];
char *path = NULL;
char *scheme = NULL;
char *host = NULL;
unsigned int port = 0;
int proxystate = -1;
csync_owncloud_ctx_t *ctx = csyncCtx->owncloud_context;
struct csync_client_certs_s* clientCerts = csyncCtx->clientCerts;
if (ctx->_connected) {
return 0;
}
rc = c_parse_uri( base_url, &scheme,
&ctx->dav_session.user,
&ctx->dav_session.pwd,
&host, &port, &path );
if( rc < 0 ) {
DEBUG_WEBDAV("Failed to parse uri %s", base_url );
goto out;
}
DEBUG_WEBDAV("* scheme %s", scheme );
DEBUG_WEBDAV("* host %s", host );
DEBUG_WEBDAV("* port %u", port );
DEBUG_WEBDAV("* path %s", path );
if( strcmp( scheme, "owncloud" ) == 0 || strcmp( scheme, "http" ) == 0 ) {
strcpy( protocol, "http");
} else if( strcmp( scheme, "ownclouds" ) == 0 || strcmp( scheme, "https") == 0 ) {
strcpy( protocol, "https");
useSSL = 1;
} else {
DEBUG_WEBDAV("Invalid scheme %s, go out here!", scheme );
rc = -1;
goto out;
}
DEBUG_WEBDAV("* user %s", ctx->dav_session.user ? ctx->dav_session.user : "");
if (port == 0) {
port = ne_uri_defaultport(protocol);
}
ctx->dav_session.ctx = ne_session_create( protocol, host, port);
if (ctx->dav_session.ctx == NULL) {
DEBUG_WEBDAV("Session create with protocol %s failed", protocol );
rc = -1;
goto out;
}
if (ctx->dav_session.read_timeout != 0) {
ne_set_read_timeout(ctx->dav_session.ctx, ctx->dav_session.read_timeout);
DEBUG_WEBDAV("Timeout set to %u seconds", ctx->dav_session.read_timeout );
}
// Should never take more than some seconds, 30 is really a max.
ne_set_connect_timeout(ctx->dav_session.ctx, 30);
snprintf( uaBuf, sizeof(uaBuf), "Mozilla/5.0 (%s) mirall/%s (csyncoC)",
CSYNC_STRINGIFY( MIRALL_VERSION ), csync_owncloud_get_platform() );
ne_set_useragent( ctx->dav_session.ctx, uaBuf);
ne_set_server_auth(ctx->dav_session.ctx, authentication_callback_by_neon, ctx);
if( useSSL ) {
if (!ne_has_support(NE_FEATURE_SSL)) {
DEBUG_WEBDAV("Error: SSL is not enabled.");
rc = -1;
goto out;
}
if(clientCerts != NULL) {
ne_ssl_client_cert *clicert;
DEBUG_WEBDAV("dav_connect: certificatePath and certificatePasswd are set, so we use it" );
DEBUG_WEBDAV(" with certificatePath: %s", clientCerts->certificatePath );
DEBUG_WEBDAV(" with certificatePasswd: %s", clientCerts->certificatePasswd );
clicert = ne_ssl_clicert_read ( clientCerts->certificatePath );
if ( clicert == NULL ) {
DEBUG_WEBDAV ( "Error read certificate : %s", ne_get_error ( ctx->dav_session.ctx ) );
} else {
if ( ne_ssl_clicert_encrypted ( clicert ) ) {
int rtn = ne_ssl_clicert_decrypt ( clicert, clientCerts->certificatePasswd );
if ( !rtn ) {
DEBUG_WEBDAV ( "Certificate was deciphered successfully." );
ne_ssl_set_clicert ( ctx->dav_session.ctx, clicert );
} else {
DEBUG_WEBDAV ( "Errors while deciphering certificate: %s", ne_get_error ( ctx->dav_session.ctx ) );
}
}
}
} else {
DEBUG_WEBDAV("dav_connect: error with csync_client_certs_s* clientCerts");
}
ne_ssl_trust_default_ca( ctx->dav_session.ctx );
ne_ssl_set_verify( ctx->dav_session.ctx, ssl_callback_by_neon, ctx);
}
/* Hook called when a request is created. It sets the proxy connection header. */
ne_hook_create_request( ctx->dav_session.ctx, request_created_hook, ctx );
/* Hook called after response headers are read. It gets the Session ID. */
ne_hook_post_headers( ctx->dav_session.ctx, post_request_hook, ctx );
/* Hook called before a request is sent. It sets the cookies. */
ne_hook_pre_send( ctx->dav_session.ctx, pre_send_hook, ctx );
/* Hook called after request is dispatched. Used for handling possible redirections. */
ne_hook_post_send( ctx->dav_session.ctx, post_send_hook, ctx );
/* Proxy support */
proxystate = configureProxy( ctx, ctx->dav_session.ctx );
if( proxystate < 0 ) {
DEBUG_WEBDAV("Error: Proxy-Configuration failed.");
} else if( proxystate > 0 ) {
ne_set_proxy_auth( ctx->dav_session.ctx, proxy_authentication_callback_by_neon, ctx );
}
ctx->_connected = 1;
rc = 0;
out:
SAFE_FREE(path);
SAFE_FREE(host);
SAFE_FREE(scheme);
return rc;
}
char *owncloud_error_string(CSYNC* ctx)
{
return ctx->owncloud_context->dav_session.error_string;
}
int owncloud_commit(CSYNC* ctx) {
if (!ctx->owncloud_context) {
return 0;
}
if( ctx->owncloud_context->dav_session.ctx ) {
ne_forget_auth(ctx->owncloud_context->dav_session.ctx);
ne_session_destroy(ctx->owncloud_context->dav_session.ctx );
ctx->owncloud_context->dav_session.ctx = 0;
}
/* DEBUG_WEBDAV( "********** vio_module_shutdown" ); */
ctx->owncloud_context->dav_session.ctx = 0;
// ne_sock_exit();
ctx->owncloud_context->_connected = 0; /* triggers dav_connect to go through the whole neon setup */
SAFE_FREE( ctx->owncloud_context->dav_session.user );
SAFE_FREE( ctx->owncloud_context->dav_session.pwd );
SAFE_FREE( ctx->owncloud_context->dav_session.session_key);
SAFE_FREE( ctx->owncloud_context->dav_session.error_string );
return 0;
}
void owncloud_destroy(CSYNC* ctx)
{
owncloud_commit(ctx);
SAFE_FREE(ctx->owncloud_context);
if (ctx->clientCerts) {
SAFE_FREE(ctx->clientCerts->certificatePasswd);
SAFE_FREE(ctx->clientCerts->certificatePath);
SAFE_FREE(ctx->clientCerts);
}
ne_sock_exit();
}
int owncloud_set_property(CSYNC* ctx, const char *key, void *data) {
#define READ_STRING_PROPERTY(P) \
if (c_streq(key, #P)) { \
SAFE_FREE(ctx->owncloud_context->dav_session.P); \
ctx->owncloud_context->dav_session.P = c_strdup((const char*)data); \
return 0; \
}
READ_STRING_PROPERTY(session_key)
READ_STRING_PROPERTY(proxy_type)
READ_STRING_PROPERTY(proxy_host)
READ_STRING_PROPERTY(proxy_user)
READ_STRING_PROPERTY(proxy_pwd)
#undef READ_STRING_PROPERTY
if (c_streq(key, "proxy_port")) {
ctx->owncloud_context->dav_session.proxy_port = *(int*)(data);
return 0;
}
if (c_streq(key, "read_timeout") || c_streq(key, "timeout")) {
ctx->owncloud_context->dav_session.read_timeout = *(int*)(data);
return 0;
}
if( c_streq(key, "get_dav_session")) {
/* Give the ne_session to the caller */
*(ne_session**)data = ctx->owncloud_context->dav_session.ctx;
return 0;
}
if( c_streq(key, "redirect_callback")) {
if (data) {
csync_owncloud_redirect_callback_t* cb_wrapper = data;
ctx->owncloud_context->dav_session.redir_callback = *cb_wrapper;
} else {
ctx->owncloud_context->dav_session.redir_callback = NULL;
}
}
if( c_streq(key, "SSLClientCerts")) {
if(ctx->clientCerts != NULL) {
SAFE_FREE(ctx->clientCerts->certificatePasswd);
SAFE_FREE(ctx->clientCerts->certificatePath);
SAFE_FREE(ctx->clientCerts);
ctx->clientCerts = NULL;
}
if (data) {
struct csync_client_certs_s* clientCerts = (struct csync_client_certs_s*) data;
struct csync_client_certs_s* newCerts = c_malloc(sizeof(struct csync_client_certs_s));
newCerts->certificatePath = c_strdup(clientCerts->certificatePath);
newCerts->certificatePasswd = c_strdup(clientCerts->certificatePasswd);
ctx->clientCerts = newCerts;
} else {
DEBUG_WEBDAV("error: in owncloud_set_property for 'SSLClientCerts'" );
}
}
return -1;
}
void owncloud_init(CSYNC* ctx) {
ne_sock_init();
ctx->owncloud_context = c_malloc( sizeof( struct csync_owncloud_ctx_s ));
ctx->owncloud_context->csync_ctx = ctx; // back reference
}
/* vim: set ts=4 sw=4 et cindent: */
+36
Ver Arquivo
@@ -0,0 +1,36 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CSYNC_OWNCLOUD_H
#define CSYNC_OWNCLOUD_H
#include "csync.h"
#include "vio/csync_vio.h"
// Public API used by csync
int owncloud_commit(CSYNC* ctx);
void owncloud_destroy(CSYNC* ctx);
char *owncloud_error_string(CSYNC* ctx);
int owncloud_set_property(CSYNC* ctx, const char *key, void *data);
void owncloud_init(CSYNC* ctx);
int dav_connect(CSYNC* ctx, const char *base_url);
#endif /* CSYNC_OWNCLOUD_H */
+115
Ver Arquivo
@@ -0,0 +1,115 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CSYNC_OWNCLOUD_PRIVATE_H
#define CSYNC_OWNCLOUD_PRIVATE_H
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config_csync.h"
#ifdef NEON_WITH_LFS /* Switch on LFS in libneon. Never remove the NE_LFS! */
#define NE_LFS
#endif
#include <neon/ne_basic.h>
#include <neon/ne_socket.h>
#include <neon/ne_session.h>
#include <neon/ne_request.h>
#include <neon/ne_props.h>
#include <neon/ne_auth.h>
#include <neon/ne_dates.h>
#include <neon/ne_compress.h>
#include <neon/ne_redirect.h>
#include "c_rbtree.h"
#include "c_lib.h"
#include "csync.h"
#include "csync_misc.h"
#include "csync_macros.h"
#include "c_private.h"
#include "vio/csync_vio.h"
#include "csync_log.h"
#include "csync_owncloud.h"
#define DEBUG_WEBDAV(...) csync_log( 9, "oc_module", __VA_ARGS__);
typedef int (*csync_owncloud_redirect_callback_t)(CSYNC* ctx, const char* uri);
/* Struct with the WebDAV session */
struct dav_session_s {
ne_session *ctx;
char *user;
char *pwd;
char *proxy_type;
char *proxy_host;
int proxy_port;
char *proxy_user;
char *proxy_pwd;
char *session_key;
char *error_string;
int read_timeout;
csync_owncloud_redirect_callback_t redir_callback;
};
struct csync_owncloud_ctx_s {
CSYNC *csync_ctx;
// For the WebDAV connection
struct dav_session_s dav_session; /* The DAV Session, initialised in dav_connect */
int _connected; /* flag to indicate if a connection exists, ie.
the dav_session is valid */
};
typedef struct csync_owncloud_ctx_s csync_owncloud_ctx_t;
//typedef csync_owncloud_ctx_t* csync_owncloud_ctx_p;
void set_errno_from_http_errcode( int err );
void set_error_message( csync_owncloud_ctx_t *ctx, const char *msg );
void set_errno_from_neon_errcode(csync_owncloud_ctx_t *ctx, int neon_code );
int http_result_code_from_session(csync_owncloud_ctx_t *ctx);
void set_errno_from_session(csync_owncloud_ctx_t *ctx);
time_t oc_httpdate_parse( const char *date );
const char* csync_owncloud_get_platform(void);
char *_cleanPath( const char* uri );
#endif // CSYNC_OWNCLOUD_PRIVATE_H
+125
Ver Arquivo
@@ -0,0 +1,125 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "csync_owncloud.h"
#include "csync_owncloud_private.h"
#include "csync_misc.h"
void set_errno_from_http_errcode( int err ) {
int new_errno = 0;
switch(err) {
case 200: /* OK */
case 201: /* Created */
case 202: /* Accepted */
case 203: /* Non-Authoritative Information */
case 204: /* No Content */
case 205: /* Reset Content */
case 207: /* Multi-Status */
case 304: /* Not Modified */
new_errno = 0;
break;
case 401: /* Unauthorized */
case 402: /* Payment Required */
case 407: /* Proxy Authentication Required */
case 405:
new_errno = EPERM;
break;
case 301: /* Moved Permanently */
case 303: /* See Other */
case 404: /* Not Found */
case 410: /* Gone */
new_errno = ENOENT;
break;
case 408: /* Request Timeout */
case 504: /* Gateway Timeout */
new_errno = EAGAIN;
break;
case 423: /* Locked */
new_errno = EACCES;
break;
case 400: /* Bad Request */
case 403: /* Forbidden */
case 409: /* Conflict */
case 411: /* Length Required */
case 412: /* Precondition Failed */
case 414: /* Request-URI Too Long */
case 415: /* Unsupported Media Type */
case 424: /* Failed Dependency */
case 501: /* Not Implemented */
new_errno = EINVAL;
break;
case 507: /* Insufficient Storage */
new_errno = ENOSPC;
break;
case 206: /* Partial Content */
case 300: /* Multiple Choices */
case 302: /* Found */
case 305: /* Use Proxy */
case 306: /* (Unused) */
case 307: /* Temporary Redirect */
case 406: /* Not Acceptable */
case 416: /* Requested Range Not Satisfiable */
case 417: /* Expectation Failed */
case 422: /* Unprocessable Entity */
case 500: /* Internal Server Error */
case 502: /* Bad Gateway */
case 505: /* HTTP Version Not Supported */
new_errno = EIO;
break;
case 503: /* Service Unavailable */
new_errno = ERRNO_SERVICE_UNAVAILABLE;
break;
case 413: /* Request Entity too Large */
new_errno = EFBIG;
break;
default:
new_errno = EIO;
}
errno = new_errno;
}
// as per http://sourceforge.net/p/predef/wiki/OperatingSystems/
// extend as required
const char* csync_owncloud_get_platform() {
#if defined (_WIN32)
return "Windows";
#elif defined(__APPLE__)
return "Macintosh";
#elif defined(__gnu_linux__)
return "Linux";
#elif defined(__DragonFly__)
/* might also define __FreeBSD__ */
return "DragonFlyBSD";
#elif defined(__FreeBSD__)
return "FreeBSD";
#elif defined(__NetBSD__)
return "NetBSD";
#elif defined(__OpenBSD__)
return "OpenBSD";
#elif defined(sun) || defined(__sun)
return "Solaris";
#else
return "Unknown OS";
#endif
}
+6
Ver Arquivo
@@ -77,6 +77,9 @@ enum csync_replica_e {
typedef struct csync_file_stat_s csync_file_stat_t;
struct csync_owncloud_ctx_s; // csync_owncloud.c
/**
* @brief csync public structure
*/
@@ -166,6 +169,9 @@ struct csync_s {
bool db_is_empty;
bool ignore_hidden_files;
struct csync_owncloud_ctx_s *owncloud_context;
};
+38 -8
Ver Arquivo
@@ -416,10 +416,37 @@ csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx,
return st;
}
/* Get the etag. */
char *csync_statedb_get_etag( CSYNC *ctx, uint64_t jHash ) {
char *ret = NULL;
csync_file_stat_t *fs = NULL;
if( !ctx ) {
return NULL;
}
if( ! csync_get_statedb_exists(ctx)) return ret;
fs = csync_statedb_get_stat_by_hash(ctx, jHash );
if( fs ) {
if( fs->etag ) {
ret = c_strdup(fs->etag);
}
csync_file_stat_free(fs);
}
return ret;
}
#define BELOW_PATH_QUERY "SELECT phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM metadata WHERE pathlen>? AND path LIKE(?)"
int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
int rc;
sqlite3_stmt *stmt = NULL;
int64_t cnt = 0;
char *likepath;
int asp;
int min_path_len;
if( !path ) {
return -1;
@@ -429,12 +456,7 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
return -1;
}
/* Select the entries for anything that starts with (path+'/')
* In other words, anything that is between path+'/' and path+'0',
* (because '0' follows '/' in ascii)
*/
const char *below_path_query = "SELECT phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM metadata WHERE path > (?||'/') AND path < (?||'0')";
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL));
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, BELOW_PATH_QUERY, -1, &stmt, NULL));
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_OK ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query.");
@@ -445,8 +467,15 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
return -1;
}
sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, path, -1, SQLITE_STATIC);
asp = asprintf( &likepath, "%s/%%%%", path);
if (asp < 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "asprintf failed!");
return -1;
}
min_path_len = strlen(path);
sqlite3_bind_int(stmt, 1, min_path_len);
sqlite3_bind_text(stmt, 2, likepath, -1, SQLITE_STATIC);
cnt = 0;
@@ -489,6 +518,7 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "%" PRId64 " entries read below path %s from db.", cnt, path);
}
sqlite3_finalize(stmt);
SAFE_FREE(likepath);
return 0;
}
+42 -13
Ver Arquivo
@@ -270,6 +270,10 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
((int64_t) fs->mtime), ((int64_t) tmp->modtime),
fs->etag, tmp->etag, (uint64_t) fs->inode, (uint64_t) tmp->inode,
(uint64_t) fs->size, (uint64_t) tmp->size, fs->remotePerm, tmp->remotePerm, tmp->has_ignored_files );
if( !fs->etag) {
st->instruction = CSYNC_INSTRUCTION_EVAL;
goto out;
}
if((ctx->current == REMOTE_REPLICA && !c_streq(fs->etag, tmp->etag ))
|| (ctx->current == LOCAL_REPLICA && (!_csync_mtime_equal(fs->mtime, tmp->modtime)
// zero size in statedb can happen during migration
@@ -580,6 +584,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
char *d_name = NULL;
csync_vio_handle_t *dh = NULL;
csync_vio_file_stat_t *dirent = NULL;
csync_vio_file_stat_t *fs = NULL;
csync_file_stat_t *previous_fs = NULL;
int read_from_db = 0;
int rc = 0;
@@ -683,8 +688,8 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
}
/* skip "." and ".." */
if ( (d_name[0] == '.' && d_name[1] == '\0')
|| (d_name[0] == '.' && d_name[1] == '.' && d_name[2] == '\0')) {
if (d_name[0] == '.' && (d_name[1] == '\0'
|| (d_name[1] == '.' && d_name[2] == '\0'))) {
csync_vio_file_stat_destroy(dirent);
dirent = NULL;
continue;
@@ -736,21 +741,15 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
/* Only for the local replica we have to stat(), for the remote one we have all data already */
if (ctx->replica == LOCAL_REPLICA) {
res = csync_vio_stat(ctx, filename, dirent);
fs = csync_vio_file_stat_new();
res = csync_vio_stat(ctx, filename, fs);
} else {
fs = dirent;
res = 0;
}
/* if the filename starts with a . we consider it a hidden file
* For windows, the hidden state is also discovered within the vio
* local stat function.
*/
if( d_name[0] == '.' ) {
dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
if( res == 0) {
switch (dirent->type) {
switch (fs->type) {
case CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK:
flag = CSYNC_FTW_FLAG_SLINK;
break;
@@ -773,12 +772,42 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
flag = CSYNC_FTW_FLAG_NSTAT;
}
if( ctx->current == LOCAL_REPLICA ) {
char *etag = NULL;
int len = strlen( path );
uint64_t h = c_jhash64((uint8_t *) path, len, 0);
etag = csync_statedb_get_etag( ctx, h );
if(_last_db_return_error(ctx)) {
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
SAFE_FREE(etag);
goto error;
}
if( etag ) {
SAFE_FREE(fs->etag);
fs->etag = etag;
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ETAG;
if( c_streq(etag, "")) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Uniq ID from Database is EMPTY: %s", path);
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Uniq ID from Database: %s -> %s", path, fs->etag ? fs->etag : "<NULL>" );
}
}
}
previous_fs = ctx->current_fs;
/* Call walker function for each file */
rc = fn(ctx, filename, dirent, flag);
rc = fn(ctx, filename, fs, flag);
/* this function may update ctx->current and ctx->read_from_db */
/* Only for the local replica we have to destroy stat(), for the remote one it is a pointer to dirent */
if (ctx->replica == LOCAL_REPLICA) {
csync_vio_file_stat_destroy(fs);
}
if (rc < 0) {
if (CSYNC_STATUS_IS_OK(ctx->status_code)) {
ctx->status_code = CSYNC_STATUS_UPDATE_ERROR;
+57
Ver Arquivo
@@ -0,0 +1,57 @@
project(httpbflib C)
find_package(Neon)
set(HTTPBF_PUBLIC_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/src
${NEON_INCLUDE_DIRS}
CACHE INTERNAL "httpbflib public include directories"
)
set(HTTPBF_LIBRARY
httpbf
CACHE INTERNAL "httpbf library"
)
set(HTTPBF_LINK_LIBRARIES
${HTTPBF_LIBRARY}
)
set(httpbf_SRCS
src/httpbf.c
)
set(httpbf_HEADERS
src/httpbf.h
)
include_directories(
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
add_library(${HTTPBF_LIBRARY} STATIC ${httpbf_SRCS})
target_link_libraries(${HTTPBF_LIBRARY} ${NEON_LIBRARIES})
if(NOT WIN32)
add_definitions( -fPIC )
endif()
INSTALL(
TARGETS
${HTTPBF_LIBRARY}
LIBRARY DESTINATION
${LIB_INSTALL_DIR}
ARCHIVE DESTINATION
${LIB_INSTALL_DIR}
RUNTIME DESTINATION
${BIN_INSTALL_DIR}
)
if (NOT APPLE)
INSTALL(
FILES
${httpbf_HEADERS}
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}
)
endif (NOT APPLE)
+29
Ver Arquivo
@@ -0,0 +1,29 @@
This is a little code that does ownCloud file chunking.
Basically to put a local file to an url:
(Also see the client example code in client dir.)
/* Initialize the transfer, get a transfer struct. */
hbf_transfer_t *trans = hbf_init_transfer( url );
Hbf_State state;
if( trans ) {
int fd = open_local_file( file );
/* create a neon session to use for the transfer */
ne_session *session = create_neon_session(uri);
if( session && fd > -1 ) {
/* Prepare the list of chunks, ie. calculate chunks and write back to trans. */
state = hbf_splitlist(trans, fd);
if( state == HBF_SUCCESS ) {
/* Transfer all the chunks through the HTTP session using PUT. */
state = hbf_transfer( session, trans, "PUT" );
}
}
}
GET a large file:
Do GET Range requests.
+36
Ver Arquivo
@@ -0,0 +1,36 @@
project(client C)
set(CLIENT_EXECUTABLE httpbfclient CACHE INTERNAL "httpbf client")
set(CLIENT_LINK_LIBRARIES ${NEON_LIBRARIES} ${HBF_LIBRARY})
set(HTTPBF_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src})
if(NOT LINUX)
list(APPEND CLIENT_LINK_LIBRARIES ${ARGP_LIBRARIES})
endif()
set(client_SRCS
httpbf_client.c
)
include_directories(
${HTTPBF_INCLUDE_DIR}
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
add_executable(${CLIENT_EXECUTABLE} ${client_SRCS})
target_link_libraries(${CLIENT_EXECUTABLE} ${CLIENT_LINK_LIBRARIES})
set_target_properties(
${CLIENT_EXECUTABLE}
PROPERTIES
OUTPUT_NAME
httpbf
)
# install( TARGETS ${CLIENT_EXECUTABLE} DESTINATION ${BIN_INSTALL_DIR} )
install(TARGETS ${CLIENT_EXECUTABLE} DESTINATION bin)
+225
Ver Arquivo
@@ -0,0 +1,225 @@
/*
* httpbf - send big files via http
*
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <neon/ne_auth.h>
#include "httpbf.h"
/* Program documentation. */
static char doc[] = "Usage: httpbf [OPTION...] LOCAL REMOTEDIR\n\
httpbf - command line client to upload big files via http.\n\
\n\
Transfer a big file to a remote directory on ownCloud.\n\
\n\
-?, --help Give this help list\n\
--usage Give a short usage message\n\
-V, --version Print program version\n\
";
static char _user[NE_ABUFSIZ];
static char _pwd[NE_ABUFSIZ];
/* The options we understand. */
static const struct option long_options[] =
{
{"version", no_argument, 0, 'V' },
{"usage", no_argument, 0, 'h' },
{0, 0, 0, 0}
};
static const char* httpbf_version = "0.1";
static void print_version()
{
printf( "%s\n", httpbf_version );
exit(0);
}
static void print_help()
{
printf( "%s\n", doc );
exit(0);
}
static int ne_auth( void *userdata, const char *realm, int attempt,
char *username, char *password)
{
(void) userdata;
(void) realm;
if( username && password ) {
if( _user ) {
/* allow user without password */
if( strlen( _user ) < NE_ABUFSIZ ) {
strcpy( username, _user );
}
if( _pwd && strlen( _pwd ) < NE_ABUFSIZ ) {
strcpy( password, _pwd );
}
}
}
return attempt;
}
static int parse_args(int argc, char **argv)
{
while(optind < argc) {
int c = -1;
struct option *opt = NULL;
int result = getopt_long( argc, argv, "Vh", long_options, &c );
if( result == -1 ) {
break;
}
switch(result) {
case 'V':
print_version();
break;
case 'h':
print_help();
break;
case 0:
opt = (struct option*)&(long_options[c]);
if(strcmp(opt->name, "no-name-yet")) {
} else {
fprintf(stderr, "Argument: No idea what!\n");
}
break;
default:
break;
}
}
return optind;
}
static ne_session* create_neon_session( const char *url )
{
ne_uri uri;
ne_session *sess = NULL;
memset( _user, 0, NE_ABUFSIZ );
memset( _pwd, 0, NE_ABUFSIZ );
if( ne_uri_parse( url, &uri ) == 0 ) {
unsigned int port = ne_uri_defaultport(uri.scheme);
if( uri.userinfo ) {
char *slash = NULL;
strcpy( _user, uri.userinfo );
slash = strchr( _user, ':');
if( slash ) {
strcpy( _pwd, slash+1);
*slash = 0;
}
}
sess = ne_session_create(uri.scheme, uri.host, port);
ne_set_server_auth(sess, ne_auth, 0 );
ne_uri_free(&uri);
}
return sess;
}
static int open_local_file( const char *file )
{
int fd = -1;
if( !file ) return -1;
fd = open(file, O_RDONLY);
return fd;
}
static void transfer( const char* local, const char* uri )
{
if( !(local && uri )) return;
char *whole_url;
int len;
char *filename = basename(local);
if( ! filename ) {
return;
}
len = strlen(filename)+strlen(uri)+2;
whole_url = malloc( len );
strcpy(whole_url, uri);
strcat(whole_url, "/");
strcat(whole_url, filename);
hbf_transfer_t *trans = hbf_init_transfer( whole_url );
Hbf_State state;
if( trans ) {
ne_session *session = create_neon_session(uri);
if( session ) {
int fd = open_local_file( local );
if( fd > -1 ) {
state = hbf_splitlist(trans, fd );
if( state == HBF_SUCCESS ) {
state = hbf_transfer( session, trans, "PUT" );
}
}
ne_session_destroy(session);
}
}
if( state != HBF_SUCCESS ) {
printf("Upload failed: %s\n", hbf_error_string(state));
printf(" HTTP result %d, Server Error: %s\n",
trans->status_code, trans->error_string ? trans->error_string : "<empty>" );
}
/* Print the result of the recent transfer */
hbf_free_transfer( trans );
free(whole_url);
}
int main(int argc, char **argv) {
int rc = 0;
char errbuf[256] = {0};
parse_args(argc, argv);
/* two options must remain as source and target */
/* printf("ARGC: %d -> optind: %d\n", argc, optind ); */
if( argc - optind < 2 ) {
print_help();
}
transfer( argv[optind], argv[optind+1]);
}
/* vim: set ts=8 sw=2 et cindent: */
+38
Ver Arquivo
@@ -0,0 +1,38 @@
project(httpbf C)
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
set(HTTPBF_PUBLIC_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
CACHE INTERNAL "httpbf public include directories"
)
set(HTTPBFLIB_PRIVATE_INCLUDE_DIRS
)
set(HBF_LIBRARY
httpbf
CACHE INTERNAL "httpbflib library"
)
set(HTTPBFLIB_LINK_LIBRARIES
${HBF_LIBRARY}
)
set(httpbflib_SRCS
httpbf.c
)
include_directories(
${NEON_INCLUDE_DIRS}
${GLIB2_INCLUDE_DIRS}
)
if(NOT WIN32)
add_definitions( -fPIC )
endif()
add_library(${HBF_LIBRARY} SHARED ${httpbflib_SRCS} )
target_link_libraries(${HBF_LIBRARY} ${NEON_LIBRARIES})
+688
Ver Arquivo
@@ -0,0 +1,688 @@
/*
* httpbf - send big files via http
*
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/timeb.h>
#include <sys/time.h>
#include <inttypes.h>
#include "httpbf.h"
#include <neon/ne_session.h>
#include <neon/ne_request.h>
#include <neon/ne_basic.h>
// #ifdef NDEBUG
// #define DEBUG_HBF(...)
// #else
#define DEBUG_HBF(...) { if(transfer->log_cb) { \
char buf[1024]; \
snprintf(buf, 1024, __VA_ARGS__); \
transfer->log_cb(__func__, buf, transfer->user_data); \
} }
// #endif
#define DEFAULT_BLOCK_SIZE (10*1024*1024)
/* Platform specific defines go here. */
#ifdef _WIN32
#define _hbf_fstat _fstat64
typedef struct stat64 hbf_stat_t;
#else
#define _hbf_fstat fstat
typedef struct stat hbf_stat_t;
#endif
static int transfer_id( hbf_stat_t *sb ) {
struct timeval tp;
int res;
int r;
if( gettimeofday(&tp, 0) < 0 ) {
return 0;
}
/* build a Unique ID:
* take the current epoch and shift 8 bits up to keep the least bits.
* than add the milliseconds, again shift by 8
* and finally add the least 8 bit of the inode of the file.
*/
res = tp.tv_sec; /* epoche value in seconds */
res = res << 8;
r = (sb->st_ino & 0xFF);
res += r; /* least six bit of inode */
res = res << sizeof(tp.tv_usec);
res += tp.tv_usec; /* milliseconds */
return res;
}
hbf_transfer_t *hbf_init_transfer( const char *dest_uri ) {
hbf_transfer_t * transfer = NULL;
transfer = malloc( sizeof(hbf_transfer_t) );
memset(transfer, 0, sizeof(hbf_transfer_t));
/* store the target uri */
transfer->url = strdup(dest_uri);
transfer->status_code = 200;
transfer->error_string = NULL;
transfer->start_id = 0;
transfer->block_size = DEFAULT_BLOCK_SIZE;
transfer->threshold = transfer->block_size;
transfer->modtime_accepted = 0;
transfer->oc_header_modtime = 0;
return transfer;
}
/* Create the splitlist of a given file descriptor */
Hbf_State hbf_splitlist(hbf_transfer_t *transfer, int fd ) {
hbf_stat_t sb;
int64_t num_blocks;
int64_t blk_size;
int64_t remainder = 0;
if( ! transfer ) {
return HBF_PARAM_FAIL;
}
if( fd <= 0 ) {
DEBUG_HBF("File descriptor is invalid.");
return HBF_PARAM_FAIL;
}
if( _hbf_fstat(fd, &sb) < 0 ) {
DEBUG_HBF("Failed to stat the file descriptor: errno = %d", errno);
return HBF_FILESTAT_FAIL;
}
/* Store the file characteristics. */
transfer->fd = fd;
transfer->stat_size = sb.st_size;
transfer->modtime = sb.st_mtime;
transfer->previous_etag = NULL;
#ifndef NDEBUG
transfer->calc_size = 0;
#endif
DEBUG_HBF("block_size: %" PRId64 " threshold: %" PRId64 " st_size: %" PRId64, transfer->block_size, transfer->threshold, sb.st_size );
/* calc the number of blocks to split in */
blk_size = transfer->block_size;
if (sb.st_size < transfer->threshold) {
blk_size = transfer->threshold;
}
num_blocks = sb.st_size / blk_size;
/* there migth be a remainder. */
remainder = sb.st_size - num_blocks * blk_size;
/* if there is a remainder, add one block */
if( remainder > 0 ) {
num_blocks++;
}
/* The file has size 0. There still needs to be at least one block. */
if( sb.st_size == 0 ) {
num_blocks = 1;
blk_size = 0;
}
DEBUG_HBF("num_blocks: %" PRId64 " rmainder: %" PRId64 " blk_size: %" PRId64, num_blocks, remainder, blk_size );
if( num_blocks ) {
int cnt;
int64_t overall = 0;
/* create a datastructure for the transfer data */
transfer->block_arr = calloc(num_blocks, sizeof(hbf_block_t*));
transfer->block_cnt = num_blocks;
transfer->transfer_id = transfer_id(&sb);
transfer->start_id = 0;
for( cnt=0; cnt < num_blocks; cnt++ ) {
/* allocate a block struct and fill */
hbf_block_t *block = malloc( sizeof(hbf_block_t) );
memset(block, 0, sizeof(hbf_block_t));
block->seq_number = cnt;
if( cnt > 0 ) {
block->start = cnt * blk_size;
}
block->size = blk_size;
block->state = HBF_NOT_TRANSFERED;
/* consider the remainder if we're already at the end */
if( cnt == num_blocks-1 && remainder > 0 ) {
block->size = remainder;
}
overall += block->size;
/* store the block data into the result array in the transfer */
*((transfer->block_arr)+cnt) = block;
DEBUG_HBF("created block %d (start: %" PRId64 " size: %" PRId64 ")", cnt, block->start, block->size );
}
#ifndef NDEBUG
transfer->calc_size = overall;
#endif
}
return HBF_SUCCESS;
}
void hbf_free_transfer( hbf_transfer_t *transfer ) {
int cnt;
if( !transfer ) return;
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
hbf_block_t *block = transfer->block_arr[cnt];
if( !block ) continue;
if( block->http_error_msg ) free( block->http_error_msg );
if( block->etag ) free( block->etag );
}
free( transfer->block_arr );
free( transfer->url );
free( transfer->file_id );
if( transfer->error_string) free( (void*) transfer->error_string );
free( transfer );
}
static char* get_transfer_url( hbf_transfer_t *transfer, int indx ) {
char *res = NULL;
hbf_block_t *block = NULL;
if( ! transfer ) return NULL;
if( indx >= transfer->block_cnt ) return NULL;
block = transfer->block_arr[indx];
if( ! block ) return NULL;
if( transfer->block_cnt == 1 ) {
/* Just one chunk. We send as an ordinary request without chunking. */
res = strdup( transfer->url );
} else {
char trans_id_str[32];
char trans_block_str[32];
char indx_str[32];
int len = 1; /* trailing zero. */
int tlen = 0;
tlen = sprintf( trans_id_str, "%u", transfer->transfer_id );
if( tlen < 0 ) {
return NULL;
}
len += tlen;
tlen = sprintf( trans_block_str, "%u", transfer->block_cnt );
if( tlen < 0 ) {
return NULL;
}
len += tlen;
tlen = sprintf( indx_str, "%u", indx );
if( tlen < 0 ) {
return NULL;
}
len += tlen;
len += strlen(transfer->url);
len += strlen("-chunking---");
res = malloc(len);
/* Note: must be %u for unsigned because one does not want '--' */
if( sprintf(res, "%s-chunking-%u-%u-%u", transfer->url, transfer->transfer_id,
transfer->block_cnt, indx ) < 0 ) {
return NULL;
}
}
return res;
}
/*
* perform one transfer of one block.
* returns HBF_TRANSFER_SUCCESS if the transfer of this block was a success
* returns HBF_SUCCESS if the server aknoweldge that he received all the blocks
*/
static int _hbf_dav_request(hbf_transfer_t *transfer, ne_request *req, int fd, hbf_block_t *blk ) {
Hbf_State state = HBF_TRANSFER_SUCCESS;
int res;
const ne_status *req_status = NULL;
const char *etag = NULL;
(void) transfer;
if( ! (blk && req) ) return HBF_PARAM_FAIL;
ne_set_request_body_fd(req, fd, blk->start, blk->size);
DEBUG_HBF("Block: %d , Start: %" PRId64 " and Size: %" PRId64 "", blk->seq_number, blk->start, blk->size );
res = ne_request_dispatch(req);
req_status = ne_get_status( req );
switch(res) {
case NE_OK:
blk->state = HBF_TRANSFER_FAILED;
state = HBF_FAIL;
etag = 0;
if( req_status->klass == 2 ) {
state = HBF_TRANSFER_SUCCESS;
blk->state = HBF_TRANSFER_SUCCESS;
etag = ne_get_response_header(req, "ETag");
if (etag && etag[0]) {
/* When there is an etag, it means the transfer was complete */
state = HBF_SUCCESS;
if( etag[0] == '"' && etag[ strlen(etag)-1] == '"') {
int len = strlen( etag )-2;
blk->etag = malloc( len+1 );
strncpy( blk->etag, etag+1, len );
blk->etag[len] = '\0';
} else {
blk->etag = strdup( etag );
}
} else {
/* DEBUG_HBF("OOOOOOOO No etag returned!"); */
}
/* check if the server was able to set the mtime already. */
etag = ne_get_response_header(req, "X-OC-MTime");
if( etag && strcmp(etag, "accepted") == 0 ) {
/* the server acknowledged that the mtime was set. */
transfer->modtime_accepted = 1;
}
etag = ne_get_response_header(req, "OC-FileID");
if( etag ) {
transfer->file_id = strdup( etag );
}
}
break;
case NE_AUTH:
state = HBF_AUTH_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_PROXYAUTH:
state = HBF_PROXY_AUTH_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_CONNECT:
state = HBF_CONNECT_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_TIMEOUT:
state = HBF_TIMEOUT_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_ERROR:
state = HBF_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
}
blk->http_result_code = req_status->code;
if( req_status->reason_phrase ) {
blk->http_error_msg = strdup(req_status->reason_phrase);
}
return state;
}
Hbf_State hbf_validate_source_file( hbf_transfer_t *transfer ) {
Hbf_State state = HBF_SUCCESS;
hbf_stat_t sb;
if( transfer == NULL ) {
state = HBF_PARAM_FAIL;
}
if( state == HBF_SUCCESS ) {
if( transfer->fd <= 0 ) {
state = HBF_PARAM_FAIL;
}
}
if( state == HBF_SUCCESS ) {
int rc = _hbf_fstat( transfer->fd, &sb );
if( rc != 0 ) {
state = HBF_STAT_FAIL;
}
}
if( state == HBF_SUCCESS ) {
if( sb.st_mtime != transfer->modtime || sb.st_size != transfer->stat_size ) {
state = HBF_SOURCE_FILE_CHANGE;
}
}
return state;
}
/* Get the HTTP error code for the last request */
static int _hbf_http_error_code(ne_session *session) {
const char *msg = ne_get_error( session );
char *msg2;
int err;
err = strtol(msg, &msg2, 10);
if (msg == msg2) {
err = 0;
}
return err;
}
static Hbf_State _hbf_transfer_no_chunk(ne_session *session, hbf_transfer_t *transfer, const char *verb) {
int res;
const ne_status* req_status;
ne_request *req = ne_request_create(session, verb ? verb : "PUT", transfer->url);
if (!req)
return HBF_MEMORY_FAIL;
ne_add_request_header( req, "Content-Type", "application/octet-stream");
ne_set_request_body_fd(req, transfer->fd, 0, transfer->stat_size);
DEBUG_HBF("HBF: chunking not supported for %s", transfer->url);
res = ne_request_dispatch(req);
req_status = ne_get_status( req );
if (res == NE_OK && req_status->klass == 2) {
ne_request_destroy(req);
return HBF_SUCCESS;
}
if( transfer->error_string ) free( transfer->error_string );
transfer->error_string = strdup( ne_get_error(session) );
transfer->status_code = req_status->code;
ne_request_destroy(req);
return HBF_FAIL;
}
Hbf_State hbf_transfer( ne_session *session, hbf_transfer_t *transfer, const char *verb ) {
Hbf_State state = HBF_TRANSFER_SUCCESS;
int cnt;
if( ! session ) {
state = HBF_SESSION_FAIL;
}
if( ! transfer ) {
state = HBF_SPLITLIST_FAIL;
}
if( ! verb ) {
state = HBF_PARAM_FAIL;
}
if(state == HBF_TRANSFER_SUCCESS) {
DEBUG_HBF("%s request to %s", verb, transfer->url);
}
for( cnt=0; state == HBF_TRANSFER_SUCCESS && cnt < transfer->block_cnt; cnt++ ) {
/* cnt goes from O to block_cnt, but block_id starts at start_id and wrap around
* That way if we have not finished uploaded when we reach block_cnt, we re-upload
* the beginning of the file that the server did not have in cache anymore.
*/
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
char *transfer_url = NULL;
if( ! block ) state = HBF_PARAM_FAIL;
if( transfer->abort_cb ) {
int do_abort = (transfer->abort_cb)(transfer->user_data);
if( do_abort ) {
state = HBF_USER_ABORTED;
transfer->start_id = block_id % transfer->block_cnt;
}
}
if( state == HBF_TRANSFER_SUCCESS ) {
transfer_url = get_transfer_url( transfer, block_id );
if( ! transfer_url ) {
state = HBF_PARAM_FAIL;
}
}
if( state == HBF_TRANSFER_SUCCESS ) {
if( transfer->block_cnt > 1 && cnt > 0 ) {
/* The block count is > 1, check size and mtime before transmitting. */
state = hbf_validate_source_file(transfer);
if( state == HBF_SOURCE_FILE_CHANGE ) {
/* The source file has changed meanwhile */
}
}
}
if( state == HBF_TRANSFER_SUCCESS || state == HBF_SUCCESS ) {
ne_request *req = ne_request_create(session, verb, transfer_url);
if( req ) {
char buf[21];
snprintf(buf, sizeof(buf), "%"PRId64, transfer->stat_size);
ne_add_request_header(req, "OC-Total-Length", buf);
if( transfer->oc_header_modtime > 0 ) {
snprintf(buf, sizeof(buf), "%"PRId64, transfer->oc_header_modtime);
ne_add_request_header(req, "X-OC-Mtime", buf);
}
if( transfer->previous_etag ) {
ne_add_request_header(req, "If-Match", transfer->previous_etag);
}
if( transfer->block_cnt > 1 ) {
ne_add_request_header(req, "OC-Chunked", "1");
snprintf(buf, sizeof(buf), "%"PRId64, transfer->threshold);
ne_add_request_header(req, "OC-Chunk-Size", buf);
}
ne_add_request_header( req, "Content-Type", "application/octet-stream");
state = _hbf_dav_request(transfer, req, transfer->fd, block );
if( state != HBF_TRANSFER_SUCCESS && state != HBF_SUCCESS) {
if( transfer->error_string ) free( transfer->error_string );
transfer->error_string = strdup( ne_get_error(session) );
transfer->start_id = block_id % transfer->block_cnt;
/* Set the code of the last transmission. */
state = HBF_FAIL;
transfer->status_code = transfer->block_arr[block_id]->http_result_code;
}
ne_request_destroy(req);
if (transfer->block_cnt > 1 && state == HBF_SUCCESS && cnt == 0) {
/* Success on the first chunk is suspicious.
It could happen that the server did not support chunking */
int rc = ne_delete(session, transfer_url);
if (rc == NE_OK && _hbf_http_error_code(session) == 204) {
/* If delete suceeded, it means some proxy strips the OC_CHUNKING header
start again without chunking: */
free( transfer_url );
return _hbf_transfer_no_chunk(session, transfer, verb);
}
}
if (state == HBF_TRANSFER_SUCCESS && transfer->chunk_finished_cb) {
transfer->chunk_finished_cb(transfer, block_id, transfer->user_data);
}
} else {
state = HBF_MEMORY_FAIL;
}
}
free( transfer_url );
}
/* do the source file validation finally (again). */
if( state == HBF_TRANSFER_SUCCESS ) {
/* This means that no etag was returned on one of the chunks to indicate
* that the upload was finished. */
state = HBF_TRANSFER_NOT_ACKED;
}
return state;
}
int hbf_fail_http_code( hbf_transfer_t *transfer )
{
int cnt;
if( ! transfer ) return 0;
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
if( block->state != HBF_NOT_TRANSFERED && block->state != HBF_TRANSFER_SUCCESS ) {
return block->http_result_code;
}
}
return 200;
}
const char *hbf_transfer_etag( hbf_transfer_t *transfer )
{
int cnt;
const char *etag = NULL;
if( ! transfer ) return 0;
/* Loop over all parts and do a assertion that there is only one etag. */
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
if( block->etag ) {
if( etag && strcmp(etag, block->etag) != 0 ) {
/* multiple etags in the transfer, not equal. */
DEBUG_HBF( "WARN: etags are not equal in blocks of one single transfer." );
}
etag = block->etag;
}
}
return etag;
}
const char *hbf_transfer_file_id( hbf_transfer_t *transfer )
{
const char *re = NULL;
if(transfer) {
re = transfer->file_id;
}
return re;
}
const char *hbf_error_string(hbf_transfer_t *transfer, Hbf_State state)
{
const char *re;
int cnt;
switch( state ) {
case HBF_SUCCESS:
re = "Ok.";
break;
case HBF_NOT_TRANSFERED: /* never tried to transfer */
re = "Block was not yet tried to transfer.";
break;
case HBF_TRANSFER: /* transfer currently running */
re = "Block is currently transferred.";
break;
case HBF_TRANSFER_FAILED: /* transfer tried but failed */
re = "Block transfer failed.";
break;
case HBF_TRANSFER_SUCCESS: /* transfer succeeded. */
re = "Block transfer successful.";
break;
case HBF_SPLITLIST_FAIL: /* the file could not be split */
re = "Splitlist could not be computed.";
break;
case HBF_SESSION_FAIL:
re = "No valid session in transfer.";
break;
case HBF_FILESTAT_FAIL:
re = "Source file could not be stat'ed.";
break;
case HBF_PARAM_FAIL:
re = "Parameter fail.";
break;
case HBF_AUTH_FAIL:
re = "Authentication fail.";
break;
case HBF_PROXY_AUTH_FAIL:
re = "Proxy Authentication fail.";
break;
case HBF_CONNECT_FAIL:
re = "Connection could not be established.";
break;
case HBF_TIMEOUT_FAIL:
re = "Network timeout.";
break;
case HBF_MEMORY_FAIL:
re = "Out of memory.";
break;
case HBF_STAT_FAIL:
re = "Filesystem stat on file failed.";
break;
case HBF_SOURCE_FILE_CHANGE:
re = "Source file changed too often during upload.";
break;
case HBF_USER_ABORTED:
re = "Transmission aborted by user.";
break;
case HBF_TRANSFER_NOT_ACKED:
re = "The server did not provide an Etag.";
break;
case HBF_FAIL:
default:
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
if( block->state != HBF_NOT_TRANSFERED && block->state != HBF_TRANSFER_SUCCESS
&& block->http_error_msg != NULL) {
return block->http_error_msg;
}
}
re = "Unknown error.";
}
return re;
}
void hbf_set_abort_callback( hbf_transfer_t *transfer, hbf_abort_callback cb)
{
if( transfer ) {
transfer->abort_cb = cb;
}
}
void hbf_set_log_callback(hbf_transfer_t* transfer, hbf_log_callback cb)
{
if( transfer ) {
transfer->log_cb = cb;
}
}
+142
Ver Arquivo
@@ -0,0 +1,142 @@
/**
* http big file functions
*
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HBF_SEND_H
#define _HBF_SEND_H
#include "config_csync.h"
#ifdef NEON_WITH_LFS /* Switch on LFS in libneon. Never remove the NE_LFS! */
#define NE_LFS
#endif
#include <neon/ne_session.h>
#ifdef __cplusplus
extern "C" {
#endif
enum hbf_state_e {
HBF_SUCCESS,
HBF_NOT_TRANSFERED, /* never tried to transfer */
HBF_TRANSFER, /* transfer currently running */
HBF_TRANSFER_FAILED, /* transfer tried but failed */
HBF_TRANSFER_SUCCESS, /* block transfer succeeded. */
HBF_SPLITLIST_FAIL, /* the file could not be split */
HBF_SESSION_FAIL,
HBF_FILESTAT_FAIL,
HBF_PARAM_FAIL,
HBF_AUTH_FAIL,
HBF_PROXY_AUTH_FAIL,
HBF_CONNECT_FAIL,
HBF_TIMEOUT_FAIL,
HBF_MEMORY_FAIL,
HBF_STAT_FAIL,
HBF_SOURCE_FILE_CHANGE,
HBF_USER_ABORTED,
HBF_TRANSFER_NOT_ACKED,
HBF_FAIL
};
typedef enum hbf_state_e Hbf_State;
typedef struct hbf_block_s hbf_block_t;
struct hbf_block_s {
int seq_number;
int64_t start;
int64_t size;
Hbf_State state;
int http_result_code;
char *http_error_msg;
char *etag;
int tries;
};
typedef struct hbf_transfer_s hbf_transfer_t;
/* Callback for to check on abort */
typedef int (*hbf_abort_callback) (void *);
typedef void (*hbf_log_callback) (const char *, const char *, void*);
typedef void (*hbf_chunk_finished_callback) (hbf_transfer_t*,int, void*);
struct hbf_transfer_s {
hbf_block_t **block_arr;
int block_cnt;
int fd;
int transfer_id;
char *url;
int start_id;
int status_code;
char *error_string;
int64_t stat_size;
time_t modtime;
time_t oc_header_modtime;
int64_t block_size;
int64_t threshold;
void *user_data;
hbf_abort_callback abort_cb;
hbf_log_callback log_cb;
hbf_chunk_finished_callback chunk_finished_cb;
int modtime_accepted;
const char *previous_etag; /* etag send as the If-Match http header */
char *file_id;
#ifndef NDEBUG
int64_t calc_size;
#endif
};
hbf_transfer_t *hbf_init_transfer( const char *dest_uri );
Hbf_State hbf_transfer( ne_session *session, hbf_transfer_t *transfer, const char *verb );
Hbf_State hbf_splitlist( hbf_transfer_t *transfer, int fd );
void hbf_free_transfer( hbf_transfer_t *transfer );
const char *hbf_error_string(hbf_transfer_t* transfer, Hbf_State state);
const char *hbf_transfer_etag( hbf_transfer_t *transfer );
const char *hbf_transfer_file_id( hbf_transfer_t *transfer );
void hbf_set_abort_callback( hbf_transfer_t *transfer, hbf_abort_callback cb);
void hbf_set_log_callback( hbf_transfer_t *transfer, hbf_log_callback cb);
/* returns an http (error) code of the transmission. If the transmission
* succeeded, the code is 200. If it failed, its the error code of the
* first part transmission that failed.
*/
int hbf_fail_http_code( hbf_transfer_t *transfer );
Hbf_State hbf_validate_source_file( hbf_transfer_t *transfer );
#ifdef __cplusplus
}
#endif
#endif
+18
Ver Arquivo
@@ -0,0 +1,18 @@
project(hbf_test C )
add_definitions(-DUNIT_TESTING=1)
find_package(CMocka REQUIRED)
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMOCKA_INCLUDE_DIRS}
${NEON_INCLUDE_DIRS}
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
add_executable(send_test hbf_send_test.c)
target_link_libraries(send_test ${CMOCKA_LIBRARIES} ${NEON_LIBRARIES} ${HBF_LIBRARY} )
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.2 MiB

+162
Ver Arquivo
@@ -0,0 +1,162 @@
/*
* httpbf - send big files via http
*
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <string.h>
#include <cmocka.h>
#include "config_csync.h"
#include <httpbf.h>
// A test case that does nothing and succeeds.
static void null_test_success(void **state) {
(void) state;
}
static char* test_file( const char* name ) {
if( ! name ) return 0;
char path[260];
strcpy( path, TESTFILEDIR);
if(path[strlen(TESTFILEDIR)-1] != '/')
strcat( path, "/");
strcat( path, name );
return strdup(path);
}
static void test_get_transfer_url( void **state ) {
const char *url = "http://example.org/owncloud";
const char *turl = NULL;
char res[256];
int i;
Hbf_State hbf_state;
hbf_transfer_t *list = NULL;
list = hbf_init_transfer( url );
assert_non_null( list );
/* open a file */
int fd = open( test_file("church.jpg"), O_RDONLY );
assert_true(fd >= 0);
hbf_state = hbf_splitlist(list, fd);
assert_true( hbf_state == HBF_SUCCESS);
for( i=0; i < list->block_cnt; i++ ) {
turl = get_transfer_url( list, i );
sprintf(res, "%s-chunking-%d-%d-%d", url, list->transfer_id,
list->block_cnt, i );
printf( "XX: %s\n", res );
assert_string_equal( turl, res );
}
}
static void test_hbf_init_transfer( void **state ) {
hbf_transfer_t *list = NULL;
const char *url = "http://example.org/owncloud";
list = hbf_init_transfer( url );
assert_non_null( list );
assert_string_equal( url, list->url );
}
/* test with a file size that is not a multiply of the slize size. */
static void test_hbf_splitlist_odd( void **state ){
hbf_transfer_t *list = NULL;
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/church.jpg";
/* open a file */
int fd = open(test_file("church.jpg"), O_RDONLY);
assert_true(fd >= 0);
int prev_id = 0;
int i;
Hbf_State hbf_state;
/* do a smoke test for uniqueness */
for( i=0; i < 10000; i++) {
list = hbf_init_transfer(dest_url);
assert_non_null(list);
usleep(1);
hbf_state = hbf_splitlist(list, fd);
assert_int_not_equal(list->transfer_id, prev_id);
prev_id = list->transfer_id;
hbf_free_transfer(list);
}
list = hbf_init_transfer(dest_url);
assert_non_null(list);
hbf_state = hbf_splitlist(list, fd);
assert_non_null(list);
assert_int_equal(list->calc_size, list->stat_size);
assert_int_not_equal(list->block_cnt, 0);
assert_true( hbf_state == HBF_SUCCESS);
/* checks on the block list */
int seen_zero_seq = 0;
int prev_seq = -1;
int64_t prev_block_end = -1;
for( i=0; i < list->block_cnt; i++) {
hbf_block_t *blk = list->block_arr[i];
assert_non_null(blk);
if( blk->seq_number == 0 ) seen_zero_seq++;
assert_int_equal(prev_seq, blk->seq_number -1 );
prev_seq = blk->seq_number;
assert_true((prev_block_end+1) == (blk->start));
prev_block_end = blk->start + blk->size;
}
/* Make sure we exactly saw blk->seq_number == 0 exactly one times */
assert_int_equal( seen_zero_seq, 1 );
hbf_free_transfer( list );
}
int main(void) {
const UnitTest tests[] = {
unit_test(null_test_success),
unit_test(test_hbf_splitlist_odd),
unit_test(test_hbf_init_transfer),
unit_test(test_get_transfer_url)
};
return run_tests(tests);
}
+6
Ver Arquivo
@@ -36,6 +36,9 @@
#define CSYNC_LOG_CATEGORY_NAME "csync.vio.main"
#include "csync_log.h"
#if USE_NEON
#include "csync_owncloud.h"
#endif
csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) {
switch(ctx->replica) {
@@ -129,5 +132,8 @@ char *csync_vio_get_status_string(CSYNC *ctx) {
if(ctx->error_string) {
return ctx->error_string;
}
#ifdef USE_NEON
return owncloud_error_string(ctx);
#endif
return 0;
}
+10
Ver Arquivo
@@ -157,6 +157,13 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
return -1;
}
buf->name = c_basename(uri);
if (buf->name == NULL) {
csync_vio_file_stat_destroy(buf);
c_free_locale_string(wuri);
return -1;
}
buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
switch(sb.st_mode & S_IFMT) {
@@ -203,6 +210,9 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
#endif
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
buf->device = sb.st_dev;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DEVICE;
buf->inode = sb.st_ino;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
+88 -64
Ver Arquivo
@@ -116,31 +116,10 @@ int csync_vio_local_closedir(csync_vio_handle_t *dhandle) {
return rc;
}
static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder)
{
long long int t = filetime->dwHighDateTime;
t <<= 32;
t += (UINT32)filetime->dwLowDateTime;
t -= 116444736000000000LL;
if (t < 0)
{
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
return -1 - ((-t - 1) / 10000000);
}
else
{
if (remainder) *remainder = t % 10000000;
return t / 10000000;
}
}
csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
dhandle_t *handle = NULL;
csync_vio_file_stat_t *file_stat = NULL;
ULARGE_INTEGER FileIndex;
DWORD rem;
handle = (dhandle_t *) dhandle;
@@ -169,43 +148,13 @@ csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
file_stat->name = c_utf8_from_locale(handle->ffd.cFileName);
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT
&& handle->ffd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) {
file_stat->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
file_stat->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
file_stat->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
} else {
file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR;
}
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
} else {
file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR;
}
file_stat->flags = CSYNC_VIO_FILE_FLAGS_NONE;
/* Check for the hidden flag */
if( handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) {
file_stat->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow;
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
file_stat->atime = FileTimeToUnixTime(&handle->ffd.ftLastAccessTime, &rem);
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
file_stat->mtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem);
/* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
file_stat->ctime = FileTimeToUnixTime(&handle->ffd.ftCreationTime, &rem);
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
return file_stat;
return file_stat;
err:
SAFE_FREE(file_stat);
@@ -213,16 +162,29 @@ err:
return NULL;
}
static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder)
{
long long int t = filetime->dwHighDateTime;
t <<= 32;
t += (UINT32)filetime->dwLowDateTime;
t -= 116444736000000000LL;
if (t < 0)
{
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
return -1 - ((-t - 1) / 10000000);
}
else
{
if (remainder) *remainder = t % 10000000;
return t / 10000000;
}
}
int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
/* Almost nothing to do since csync_vio_local_readdir already filled up most of the information
But we still need to fetch the file ID.
Possible optimisation: only fetch the file id when we need it (for new files)
*/
HANDLE h;
HANDLE h, hFind;
FILETIME ftCreate, ftAccess, ftWrite;
BY_HANDLE_FILE_INFORMATION fileInfo;
WIN32_FIND_DATAW FindFileData;
ULARGE_INTEGER FileIndex;
mbchar_t *wuri = c_utf8_path_to_locale( uri );
@@ -243,6 +205,47 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
return -1;
}
buf->flags = CSYNC_VIO_FILE_FLAGS_NONE;
do {
// Check first if it is a symlink (code from c_islink)
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
hFind = FindFirstFileW(wuri, &FindFileData );
if (hFind != INVALID_HANDLE_VALUE) {
if( (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
(FindFileData.dwReserved0 & IO_REPARSE_TAG_SYMLINK) ) {
buf->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
break;
}
}
FindClose(hFind);
}
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|| fileInfo.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|| fileInfo.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
break;
}
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
break;
}
// fallthrough:
buf->type = CSYNC_VIO_FILE_TYPE_REGULAR;
break;
} while (0);
/* Check for the hidden flag */
if( fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) {
buf->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
buf->device = fileInfo.dwVolumeSerialNumber;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DEVICE;
/* Get the Windows file id as an inode replacement. */
FileIndex.HighPart = fileInfo.nFileIndexHigh;
FileIndex.LowPart = fileInfo.nFileIndexLow;
@@ -250,7 +253,28 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
/* printf("Index: %I64i\n", FileIndex.QuadPart); */
buf->inode = FileIndex.QuadPart;
buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + fileInfo.nFileSizeLow;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
/* Get the file time with a win32 call rather than through stat. See
* http://www.codeproject.com/Articles/1144/Beating-the-Daylight-Savings-Time-bug-and-getting
* for deeper explanation.
*/
if( GetFileTime(h, &ftCreate, &ftAccess, &ftWrite) ) {
DWORD rem;
buf->atime = FileTimeToUnixTime(&ftAccess, &rem);
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
buf->mtime = FileTimeToUnixTime(&ftWrite, &rem);
/* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
buf->ctime = FileTimeToUnixTime(&ftCreate, &rem);
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
}
c_free_locale_string(wuri);
CloseHandle(h);
return 0;
}
+5
Ver Arquivo
@@ -7,6 +7,7 @@ include_directories(
${CSTDLIB_PUBLIC_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}
${CMOCKA_INCLUDE_DIR}
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
include_directories(${CHECK_INCLUDE_DIRS})
@@ -53,6 +54,10 @@ add_cmocka_test(check_csync_update csync_tests/check_csync_update.c ${TEST_TARGE
# encoding
add_cmocka_test(check_encoding_functions encoding_tests/check_encoding.c ${TEST_TARGET_LIBRARIES})
# httpbf
set(TEST_HTTPBF_LIBRARIES ${TEST_TARGET_LIBRARIES} ${NEON_LIBRARIES})
add_cmocka_test(check_httpbf httpbf_tests/hbf_send_test.c ${TEST_HTTPBF_LIBRARIES} )
if(UNIT_TESTING)
INSTALL( FILES "${CMOCKA_LIBRARIES}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif(UNIT_TESTING)
+1 -17
Ver Arquivo
@@ -48,12 +48,6 @@ static void setup_init(void **state) {
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes));
assert_int_equal(rc, 0);
/* and add some unicode stuff */
rc = _csync_exclude_add(&(csync->excludes), "*.💩");
assert_int_equal(rc, 0);
rc = _csync_exclude_add(&(csync->excludes), "пятницы.*");
assert_int_equal(rc, 0);
*state = csync;
}
@@ -87,7 +81,7 @@ static void check_csync_exclude_load(void **state)
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes) );
assert_int_equal(rc, 0);
assert_string_equal(csync->excludes->vector[0], "*~");
assert_string_equal(csync->excludes->vector[0], "*.filepart");
assert_int_not_equal(csync->excludes->count, 0);
}
@@ -151,16 +145,6 @@ static void check_csync_excluded(void **state)
rc = csync_excluded(csync, ".netscape/cache", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* Not excluded */
rc = csync_excluded(csync, "unicode/中文.hé", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* excluded */
rc = csync_excluded(csync, "unicode/пятницы.txt", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "unicode/中文.💩", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
}
static void check_csync_excluded_traversal(void **state)
@@ -201,6 +201,7 @@ static csync_vio_file_stat_t* create_fstat(const char *name,
}
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
fs->device = 0;
fs->size = 157459;
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
+241
Ver Arquivo
@@ -0,0 +1,241 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2013 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <string.h>
#include <unistd.h>
#include <cmocka.h>
#include "config_test.h"
#if USE_NEON
#include "httpbf.c"
#endif
// A test case that does nothing and succeeds.
static void null_test_success(void **state) {
(void) state;
}
#if USE_NEON
static char* test_file( const char* name ) {
char path[260];
if( ! name ) return 0;
strcpy( path, TESTFILES_DIR);
if(path[strlen(TESTFILES_DIR)-1] != '/')
strcat( path, "/");
strcat( path, name );
return strdup(path);
}
static void test_get_transfer_url( void **state ) {
const char *url = "http://example.org/owncloud";
const char *turl = NULL;
int fd;
Hbf_State hbf_state;
hbf_transfer_t *list = NULL;
(void) state;
list = hbf_init_transfer( url );
assert_non_null( list );
/* open a file */
fd = open( test_file("church.jpg"), O_RDONLY );
assert_true(fd >= 0);
hbf_state = hbf_splitlist(list, fd);
assert_true( hbf_state == HBF_SUCCESS);
assert_true( list->block_cnt == 1);
turl = get_transfer_url( list, 0 );
assert_non_null( turl );
assert_string_equal( url, turl );
hbf_free_transfer( list );
}
static void test_get_transfer_url_bigfile( void **state ) {
const char *url = "http://example.org/big_file";
const char *turl = NULL;
char res[256];
int i, fd;
Hbf_State hbf_state;
hbf_transfer_t *list = NULL;
(void) state;
list = hbf_init_transfer( url );
assert_non_null( list );
list->threshold = list->block_size = (1024*1024); /* block size 1 MB */
/* open a file */
fd = open( test_file("church.jpg"), O_RDONLY );
assert_true(fd >= 0);
hbf_state = hbf_splitlist(list, fd);
assert_true( hbf_state == HBF_SUCCESS);
assert_true( list->block_cnt == 2 );
for( i=0; i < list->block_cnt; i++ ) {
turl = get_transfer_url( list, i );
assert_non_null(turl);
sprintf(res, "%s-chunking-%u-%u-%u", url, list->transfer_id,
list->block_cnt, i );
/* printf( "XX: %s\n", res ); */
assert_string_equal( turl, res );
}
hbf_free_transfer(list);
}
static void test_hbf_init_transfer( void **state ) {
hbf_transfer_t *list = NULL;
const char *url = "http://example.org/owncloud";
(void) state;
list = hbf_init_transfer( url );
assert_non_null( list );
assert_string_equal( url, list->url );
}
/* test with a file size that is not a multiply of the slize size. */
static void test_hbf_splitlist_odd( void **state ){
hbf_transfer_t *list = NULL;
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/church.jpg";
int prev_id = 0;
int i, fd;
Hbf_State hbf_state;
(void) state;
/* open a file */
fd = open(test_file("church.jpg"), O_RDONLY);
assert_true(fd >= 0);
/* do a smoke test for uniqueness */
for( i=0; i < 10000; i++) {
list = hbf_init_transfer(dest_url);
assert_non_null(list);
usleep(1);
hbf_state = hbf_splitlist(list, fd);
assert_int_not_equal(list->transfer_id, prev_id);
prev_id = list->transfer_id;
hbf_free_transfer(list);
}
list = hbf_init_transfer(dest_url);
assert_non_null(list);
hbf_state = hbf_splitlist(list, fd);
assert_non_null(list);
#ifndef NDEBUG
assert_int_equal(list->calc_size, list->stat_size);
#endif
assert_int_not_equal(list->block_cnt, 0);
assert_true( hbf_state == HBF_SUCCESS);
/* checks on the block list */
if( 1 ) {
int seen_zero_seq = 0;
int prev_seq = -1;
int64_t prev_block_end = -1;
for( i=0; i < list->block_cnt; i++) {
hbf_block_t *blk = list->block_arr[i];
assert_non_null(blk);
if( blk->seq_number == 0 ) seen_zero_seq++;
assert_int_equal(prev_seq, blk->seq_number -1 );
prev_seq = blk->seq_number;
assert_true((prev_block_end+1) == (blk->start));
prev_block_end = blk->start + blk->size;
}
/* Make sure we exactly saw blk->seq_number == 0 exactly one times */
assert_int_equal( seen_zero_seq, 1 );
}
hbf_free_transfer( list );
}
/* test with a file size that is not a multiply of the slize size. */
static void test_hbf_splitlist_zero( void **state ){
hbf_transfer_t *list = NULL;
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/zerofile.txt";
int fd;
Hbf_State hbf_state;
(void) state;
/* open a file */
fd = open(test_file("zerofile.txt"), O_RDONLY);
assert_true(fd >= 0);
list = hbf_init_transfer(dest_url);
assert_non_null(list);
hbf_state = hbf_splitlist(list, fd);
assert_non_null(list);
assert_int_equal(list->stat_size, 0);
#ifndef NDEBUG
assert_int_equal(list->calc_size, list->stat_size);
#endif
assert_int_equal(list->block_cnt, 1);
assert_true( hbf_state == HBF_SUCCESS);
hbf_free_transfer( list );
}
#endif
int main(void) {
const UnitTest tests[] = {
unit_test(null_test_success),
#if USE_NEON
unit_test(test_hbf_splitlist_odd),
unit_test(test_hbf_splitlist_zero),
unit_test(test_hbf_init_transfer),
unit_test(test_get_transfer_url),
unit_test(test_get_transfer_url_bigfile)
#endif
};
return run_tests(tests);
}
+52
Ver Arquivo
@@ -73,6 +73,15 @@ static void setup_dir(void **state) {
assert_int_equal(rc, 0);
}
static void setup_file(void **state) {
int rc;
setup_dir(state);
rc = system("echo \"This is a test\" > /tmp/csync_test/file.txt");
assert_int_equal(rc, 0);
}
static void teardown(void **state) {
CSYNC *csync = *state;
int rc;
@@ -155,6 +164,46 @@ static void check_csync_vio_readdir(void **state)
}
/*
* Test for general functions (stat, chmod, chown, ...)
*/
static void check_csync_vio_stat_dir(void **state)
{
CSYNC *csync = *state;
csync_vio_file_stat_t *fs;
int rc;
fs = csync_vio_file_stat_new();
assert_non_null(fs);
rc = csync_vio_stat(csync, CSYNC_TEST_DIR, fs);
assert_int_equal(rc, 0);
assert_string_equal(fs->name, "csync_test");
assert_int_equal(fs->type, CSYNC_VIO_FILE_TYPE_DIRECTORY);
csync_vio_file_stat_destroy(fs);
}
static void check_csync_vio_stat_file(void **state)
{
CSYNC *csync = *state;
csync_vio_file_stat_t *fs;
int rc;
fs = csync_vio_file_stat_new();
assert_non_null(fs);
rc = csync_vio_stat(csync, CSYNC_TEST_FILE, fs);
assert_int_equal(rc, 0);
assert_string_equal(fs->name, "file.txt");
assert_int_equal(fs->type, CSYNC_VIO_FILE_TYPE_REGULAR);
csync_vio_file_stat_destroy(fs);
}
int torture_run_tests(void)
{
const UnitTest tests[] = {
@@ -162,6 +211,9 @@ int torture_run_tests(void)
unit_test_setup_teardown(check_csync_vio_opendir_perm, setup, teardown),
unit_test(check_csync_vio_closedir_null),
unit_test_setup_teardown(check_csync_vio_readdir, setup_dir, teardown),
unit_test_setup_teardown(check_csync_vio_stat_dir, setup_dir, teardown),
unit_test_setup_teardown(check_csync_vio_stat_file, setup_file, teardown),
};
return run_tests(tests);
+3 -3
Ver Arquivo
@@ -11,8 +11,8 @@ the Updater will check for updates and notify you when a new version is
available.
.. note:: Because of various technical issues, desktop sync clients older than
1.7 will not be allowed to connect and sync with the ownCloud 8.1+ server. It
is highly recommended to keep your client updated.
1.7 will not be allowed to connect and sync with the ownCloud 8.1 server. It is
highly recommended to keep your client updated.
Basic Workflow
--------------
@@ -89,7 +89,7 @@ To prevent automatic updates and disallow manual overrides:
1. Edit this Registry key:
``HKEY_LOCAL_MACHINE\Software\Policies\ownCloud\ownCloud``
HKEY_LOCAL_MACHINE\Software\Policies\ownCloud\ownCloud
2. Add the key ``skipUpdateCheck`` (of type DWORD).
+3 -3
Ver Arquivo
@@ -48,9 +48,9 @@ copyright = u'2013, The ownCloud developers'
# built documents.
#
# The short X.Y version.
version = '@MIRALL_VERSION_MAJOR@.@MIRALL_VERSION_MINOR@'
version = '@VERSION_MAJOR@.@VERSION_MINOR@'
# The full version, including alpha/beta/rc tags.
release = '@MIRALL_VERSION@'
release = '@VERSION@'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -189,7 +189,7 @@ latex_documents = [
# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = '@LATEX_LOGO@'
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 5.5 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 5.7 KiB

+1 -5
Ver Arquivo
@@ -3,8 +3,7 @@ Installing the Desktop Synchronization Client
=============================================
You can download the latest version of the ownCloud Desktop Synchronization
Client from the `ownCloud download page
<https://owncloud.org/install/#desktop>`_.
Client from the `ownCloud Website <https://owncloud.org/install/#desktop>`_.
There are clients for Linux, Mac OS X, and Microsoft Windows.
Installation on Mac OS X and Windows is the same as for any software
@@ -22,9 +21,6 @@ will display a notification when an update is available.
Linux users must also have a password manager enabled, such as GNOME Keyring or
KWallet, so that the sync client can login automatically.
You will also find links to source code archives and older versions on the
download page.
Installation Wizard
-------------------
+3 -4
Ver Arquivo
@@ -13,15 +13,14 @@ Your files are always automatically synchronized between your ownCloud server
and local PC.
.. note:: Because of various technical issues, desktop sync clients older than
1.7 will not allowed to connect and sync with the ownCloud 8.1+ server. It
is highly recommended to keep your client updated.
1.7 will not allowed to connect and sync with the ownCloud 8.1 server. It is
highly recommended to keep your client updated.
Improvements and New Features
-----------------------------
The 2.0 release of the ownCloud desktop sync client has many new features and
improvements. (See the `complete changelog
<https://owncloud.org/changelog/desktop/>`_.)
improvements.
* Multi-account support
* Many UI improvements
Arquivo binário não exibido.
+13 -26
Ver Arquivo
@@ -92,27 +92,18 @@ have the following features:
removed synchronization on an account (see **Remove Sync** below).
The little button with three dots that sits to the right of the sync status bar
offers four additional options:
offers three additional options:
* Open Folder
* Choose What to Sync
* Pause Sync / Resume Sync
* Pause Sync
* Remove Sync
**Open Folder** opens a file explorer window displaying the client-side folder
that is being synced.
**Pause Sync** pauses sync operations without making any changes to your account.
**Choose What to Sync** opens the folder sync tree view. From there you can
choose to sync all or only some of the folders in the folder tree.
**Pause Sync** pauses sync operations for just this folder sync connection
without making any changes to your account.
**Resume Sync** resumes sync operations for this folder sync connection.
**Remove Sync** removes this folder sync connection without removing the
account. If you want to synchronize the folder tree again then click the
**Add Folder Sync Connection** button, and re-select the folder tree that
you want to sync.
**Remove Sync** suspends synchronization without removing the account, and it
removes your folder sync selection. When you're ready to resume synchronization
click the **Add Folder Sync Connection** button, and re-select the folders you
want to sync.
.. image:: images/client-7.png
:alt: Extra options for sync operations
@@ -131,15 +122,12 @@ Multi-Account Support
You may now configure multiple ownCloud accounts in your desktop sync client.
Simply click the **Add an Account** button on the General tab, and follow the
account creation wizard. The new account will appear as a new tab in the
settings dialog, where you can adjust its settings at any time.
account creation wizard. The new account will appear as a new tab in the settings dialog, where you can adjust its settings at any time.
Editing Ignored Files
---------------------
The Ignored Files Editor can be opened by clicking on the button in the General
tab of the settings dialog. The settings apply to all configured accounts. The
:guilabel:`Ignored Files Editor` provides a list of files that are ignored
The Ignored Files Editor can be opened by clicking on the button in the General tab of the settings dialog. The settings apply to all configured accounts. The :guilabel:`Ignored Files Editor` provides a list of files that are ignored
(that is, not synchronized) by the client and server during synchronizations.
You may add additional files or directories that you want to exclude from the
synchronization process. In addition to using standard characters, the Ignored
@@ -196,8 +184,7 @@ components of the path being checked.
.. note:: Custom entries are currently not validated for syntactical
correctness by the editor, so you will not see any warnings for bad
syntax. If your synchronization does not work as you expected, check your
syntax.
syntax. If your synchronization does not work as you expected, check your syntax.
Each pattern string in the list is preceded by a checkbox. When the check box
contains a check mark, in addition to ignoring the file or directory component
@@ -210,8 +197,8 @@ this list:
- The ownCloud Client always excludes files containing characters that cannot
be synchronized to other file systems.
- Files are removed that cause individual errors three times during a
synchronization. However, the client provides the option of retrying a
synchronization three additional times on files that produce errors.
- Files are removed that cause individual errors three times during a synchronization.
However, the client provides the option of retrying a synchronization three additional
times on files that produce errors.
For more detailed information see :ref:`ignored-files-label`.
+6 -6
Ver Arquivo
@@ -51,8 +51,8 @@ Where:
* ``Connected to <ownCloud instance> as <user>``: Indicates the ownCloud server
which the client is syncing with and the user account on that server.
* ``Add Folder Sync Connection...``: Provides the ability to add another folder to the sync
(see ``Adding a folder sync connection``).
* ``Add Folder...``: Provides the ability to add another folder to the sync
(see ``Adding a folder``).
* ``Pause/Resume``: Will pause the current sync or prevent the client from
starting a new sync. Resume will resume the sync process.
* ``Remove``: Will remove the selected folder from being synced. This is used,
@@ -74,11 +74,11 @@ Where:
.. image:: images/settings_account.png
:scale: 50 %
Adding a Folder Sync Connection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Adding a Folder
^^^^^^^^^^^^^^^
Adding a new sync is initiated by clicking ``Add Folder Sync Connection`` in
the ``Account`` settings.
Adding a new sync is initiated by clicking ``Add Folder...`` in the ``Account``
settings.
..note:: To add a folder, you must not already sync a folder that contains this
folder. By default, the wizard sets up the root folder of the ownCloud
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 243 B

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 122 B

@@ -66,11 +66,11 @@ private:
if (status.startsWith("OK"))
r << "ownCloud_ok";
if (status.startsWith("SYNC") || status.startsWith("NEW"))
r << "ownCloud_sync";
r << "owncloud_sync";
if (status.startsWith("IGNORE") || status.startsWith("WARN"))
r << "ownCloud_warn";
r << "owncloud_warn";
if (status.startsWith("ERROR"))
r << "ownCloud_error";
r << "owncloud_error";
if (status.contains("+SWM"))
r << "document-share";
+42 -48
Ver Arquivo
@@ -2,10 +2,6 @@
#
# Copyright (C) by Klaas Freitag <freitag@owncloud.com>
#
# This program is the core of OwnCloud integration to Nautilus
# It will be installed on /usr/share/nautilus-python/extensions/ with the paquet owncloud-client-nautilus
# (https://github.com/owncloud/client/edit/master/shell_integration/nautilus/syncstate.py)
#
# 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
@@ -22,9 +18,7 @@ import socket
from gi.repository import GObject, Nautilus
print("Initializing owncloud-client-nautilus extension")
# Do not touch the following line.
# do not touch the following line.
appname = 'ownCloud'
def get_local_path(url):
@@ -44,6 +38,7 @@ def get_runtime_dir():
return fallback
class SocketConnect(GObject.GObject):
def __init__(self):
GObject.GObject.__init__(self)
@@ -53,8 +48,8 @@ class SocketConnect(GObject.GObject):
self._sock = None
self._listeners = [self._update_registered_paths]
self._remainder = ''
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
# all over the other objects.
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
# all over the other objects.
# returns true when one should try again!
if self._connectToSocketServer():
@@ -82,38 +77,38 @@ class SocketConnect(GObject.GObject):
def _connectToSocketServer(self):
try:
self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
postfix = "/" + appname + "/socket" # Should use os.path.join instead
sock_file = get_runtime_dir() + postfix
postfix = "/"+appname+"/socket"
sock_file = get_runtime_dir()+postfix
print ("Socket: " + sock_file + " <=> " + postfix)
if sock_file != postfix:
try:
print("Socket File: " + sock_file)
print("Socket File: "+sock_file)
self._sock.connect(sock_file)
self.connected = True
print("Setting connected to %r." % self.connected )
print("Setting connected to %r" % self.connected )
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
print("Socket watch id: " + str(self._watch_id))
return False # Don't run again
print("Socket watch id: "+str(self._watch_id))
return False # don't run again
except Exception as e:
print("Could not connect to unix socket. " + str(e))
print("Could not connect to unix socket." + str(e))
else:
print("Sock-File not valid: " + sock_file)
except Exception as e: # Bad habbit
print("Connect could not be established, try again later.")
print("Sock-File not valid: "+sock_file)
except Exception as e:
print("Connect could not be established, try again later ")
self._sock.close()
return True # Run again, if enabled via timeout_add()
return True # run again, if enabled via timeout_add()
# Notify is the raw answer from the socket
# notify is the raw answer from the socket
def _handle_notify(self, source, condition):
data = source.recv(1024)
# Prepend the remaining data from last call
# prepend the remaining data from last call
if len(self._remainder) > 0:
data = self._remainder + data
data = self._remainder+data
self._remainder = ''
if len(data) > 0:
# Remember the remainder for next round
# remember the remainder for next round
lastNL = data.rfind('\n');
if lastNL > -1 and lastNL < len(data):
self._remainder = data[lastNL+1:]
@@ -124,10 +119,10 @@ class SocketConnect(GObject.GObject):
else:
return False
return True # Run again
return True # run again
def _handle_server_response(self, line):
print("Server response: " + line)
print("Server response: "+line)
parts = line.split(':')
action = parts[0]
args = parts[1:]
@@ -156,33 +151,32 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
def get_file_items(self, window, files):
if len(files) != 1:
return
file = files[0]
items = []
file=files[0]
items=[]
# Internal or external file?!
# internal or external file?!
syncedFile = False
for reg_path in socketConnect.registered_paths:
topLevelFolder = False
topLevelFolder=False
filename = get_local_path(file.get_uri())
# Check if its a folder (ends with an /), if yes add a "/"
# otherwise it will not find the entry in the table
if os.path.isdir(filename + "/"):
filename += "/"
# Check if toplevel folder, we need to ignore those as they cannot be shared
#check if its a folder (ends with an /), if yes add a "/" otherwise it will not find the entry in the table
if os.path.isdir(filename+"/"):
filename=filename+"/"
#check if toplevel folder, we need to ignore those as they cannot be shared
if filename.count("/") < (reg_path.count("/")+2):
topLevelFolder=True
# Only show the menu extension if the file is synced and the sync
# only show the menu extension if the file is synced and the sync
# status is ok. Not for ignored files etc.
# ignore top level folders
if filename.startswith(reg_path) and topLevelFolder == False and socketConnect.nautilusVFSFile_table[filename]['state'] == 'OK':
syncedFile = True
# If it is neither in a synced folder or is a directory
if not syncedFile:
# if it is neither in a synced folder or is a directory
if (not syncedFile):
return items
# Create an menu item
labelStr = "Share with " + appname + "..."
# create an menu item
labelStr = "Share with "+appname+"..."
item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
tip='Share file %s through ownCloud' % file.get_name())
item.connect("activate", self.menu_share, file)
@@ -193,8 +187,8 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
def menu_share(self, menu, file):
filename = get_local_path(file.get_uri())
print("Share file " + filename)
socketConnect.sendCommand("SHARE:" + filename + "\n")
print("Share file "+filename)
socketConnect.sendCommand("SHARE:"+filename+"\n")
class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
@@ -211,7 +205,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
return None
def askForOverlay(self, file):
# print("Asking for overlay for "+file) # For debug only
# print("Asking for overlay for "+file)
if os.path.isdir(file):
folderStatus = socketConnect.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
@@ -246,8 +240,8 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
'NOP' : appname +'_error'
}
# file = args[0] # For debug only
# print("Action for " + file + ": " + args[0]) # For debug only
# file = args[0]
# print "Action for " + file + ": "+args[0]
if action == 'STATUS':
newState = args[0]
emblem = Emblems[newState]
@@ -259,7 +253,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if( not itemStore['state'] or newState != itemStore['state'] ):
item = itemStore['item']
item.add_emblem(emblem)
# print("Setting emblem on " + args[1] + "<>" + emblem + "<>") # For debug only
# print "Setting emblem on " + args[1]+ "<>"+emblem+"<>"
socketConnect.nautilusVFSFile_table[args[1]] = {'item': item, 'state':newState}
elif action == 'UPDATE_VIEW':
@@ -284,9 +278,9 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if filename.startswith(reg_path):
socketConnect.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
# item.add_string_attribute('share_state', "share state") # ?
# item.add_string_attribute('share_state', "share state")
self.askForOverlay(filename)
break
else:
# print("Not in scope:" + filename) # For debug only
# print("Not in scope:"+filename)
pass
+9197 -40065
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+321 -1049
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+1
Ver Arquivo
@@ -10,6 +10,7 @@ if (APPLE)
endif()
add_subdirectory(libsync)
add_subdirectory(libclient)
if (NOT BUILD_LIBRARIES_ONLY)
add_subdirectory(gui)
add_subdirectory(cmd)
+22 -15
Ver Arquivo
@@ -42,6 +42,8 @@
#include <termios.h>
#endif
#include <csync_private.h>
using namespace OCC;
struct CmdOptions {
@@ -58,7 +60,6 @@ struct CmdOptions {
bool ignoreHiddenFiles;
QString exclude;
QString unsyncedfolders;
int restartTimes;
};
// we can't use csync_set_userdata because the SyncEngine sets it already.
@@ -156,18 +157,17 @@ void help()
std::cout << " --password, -p [pass] Use [pass] as password" << std::endl;
std::cout << " -n Use netrc (5) for login" << std::endl;
std::cout << " --non-interactive Do not block execution with interaction" << std::endl;
std::cout << " --max-sync-retries [n] Retries maximum n times (default to 3)" << std::endl;
std::cout << " -h Sync hidden files,do not ignore them" << std::endl;
std::cout << " --version, -v Display version and exit" << std::endl;
std::cout << "" << std::endl;
exit(0);
exit(1);
}
void showVersion() {
const char *binaryName = APPLICATION_EXECUTABLE "cmd";
std::cout << binaryName << " version " << qPrintable(Theme::instance()->version()) << std::endl;
exit(0);
exit(1);
}
void parseOptions( const QStringList& app_args, CmdOptions *options )
@@ -224,8 +224,6 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
options->exclude = it.next();
} else if( option == "--unsyncedfolders" && !it.peekNext().startsWith("-") ) {
options->unsyncedfolders = it.next();
} else if( option == "--max-sync-retries" && !it.peekNext().startsWith("-") ) {
options->restartTimes = it.next().toInt();
} else {
help();
}
@@ -272,7 +270,6 @@ int main(int argc, char **argv) {
options.useNetrc = false;
options.interactive = true;
options.ignoreHiddenFiles = true;
options.restartTimes = 3;
ClientProxy clientProxy;
parseOptions( app.arguments(), &options );
@@ -361,7 +358,6 @@ int main(int argc, char **argv) {
account->setCredentials(cred);
account->setSslErrorHandler(sslErrorHandler);
int restartCount = 0;
restart_sync:
CSYNC *_csync_ctx;
@@ -375,6 +371,7 @@ restart_sync:
csync_set_log_level(options.silent ? 1 : 11);
opts = &options;
cred->syncContextPreInit(_csync_ctx);
if( csync_init( _csync_ctx ) < 0 ) {
qFatal("Could not initialize csync!");
@@ -384,11 +381,15 @@ restart_sync:
// ignore hidden files or not
_csync_ctx->ignore_hidden_files = options.ignoreHiddenFiles;
csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx);
if( !options.proxy.isNull() ) {
QString host;
int port = 0;
bool ok;
// Set as default and let overwrite later
csync_set_module_property(_csync_ctx, "proxy_type", (void*) "NoProxy");
QStringList pList = options.proxy.split(':');
if(pList.count() == 3) {
// http: //192.168.178.23 : 8080
@@ -398,6 +399,13 @@ restart_sync:
port = pList.at(2).toInt(&ok);
if( !host.isNull() ) {
csync_set_module_property(_csync_ctx, "proxy_type", (void*) "HttpProxy");
csync_set_module_property(_csync_ctx, "proxy_host", host.toUtf8().data());
if( ok && port ) {
csync_set_module_property(_csync_ctx, "proxy_port", (void*) &port);
}
}
QNetworkProxyFactory::setUseSystemConfiguration(false);
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, host, port));
}
@@ -408,6 +416,7 @@ restart_sync:
url.remove(0, 8);
url = QString("http%1").arg(url);
}
clientProxy.setCSyncProxy(QUrl(url), _csync_ctx);
}
// Exclude lists
@@ -446,6 +455,8 @@ restart_sync:
return EXIT_FAILURE;
}
cred->syncContextPreStart(_csync_ctx);
Cmd cmd;
SyncJournalDb db(options.source_dir);
if (!selectiveSyncList.empty()) {
@@ -453,7 +464,7 @@ restart_sync:
}
SyncEngine engine(account, _csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
QObject::connect(&engine, SIGNAL(finished(bool)), &app, SLOT(quit()));
QObject::connect(&engine, SIGNAL(finished()), &app, SLOT(quit()));
QObject::connect(&engine, SIGNAL(transmissionProgress(ProgressInfo)), &cmd, SLOT(transmissionProgressSlot()));
// Have to be done async, else, an error before exec() does not terminate the event loop.
@@ -464,12 +475,8 @@ restart_sync:
csync_destroy(_csync_ctx);
if (engine.isAnotherSyncNeeded()) {
if (restartCount < options.restartTimes) {
restartCount++;
qDebug() << "Restarting Sync, because another sync is needed" << restartCount;
goto restart_sync;
}
qWarning() << "Another sync is needed, but not done because restart count is exceeded" << restartCount;
qDebug() << "Restarting Sync, because another sync is needed";
goto restart_sync;
}
return 0;
-16
Ver Arquivo
@@ -21,13 +21,8 @@ set(client_UI
ignorelisteditor.ui
networksettings.ui
protocolwidget.ui
activitywidget.ui
synclogdialog.ui
settingsdialog.ui
sharedialog.ui
sharelinkwidget.ui
shareusergroupwidget.ui
sharewidget.ui
sslerrordialog.ui
owncloudsetuppage.ui
addcertificatedialog.ui
@@ -55,35 +50,24 @@ set(client_SRCS
ignorelisteditor.cpp
logbrowser.cpp
networksettings.cpp
ocsjob.cpp
ocssharejob.cpp
ocsshareejob.cpp
openfilemanager.cpp
owncloudgui.cpp
owncloudsetupwizard.cpp
protocolwidget.cpp
activitywidget.cpp
activityitemdelegate.cpp
selectivesyncdialog.cpp
settingsdialog.cpp
share.cpp
sharedialog.cpp
sharelinkwidget.cpp
shareusergroupwidget.cpp
sharee.cpp
socketapi.cpp
sslbutton.cpp
sslerrordialog.cpp
syncrunfilelog.cpp
systray.cpp
thumbnailjob.cpp
quotainfo.cpp
accountstate.cpp
addcertificatedialog.cpp
authenticationdialog.cpp
proxyauthhandler.cpp
proxyauthdialog.cpp
synclogdialog.cpp
creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp
creds/shibbolethcredentials.cpp
-10
Ver Arquivo
@@ -215,16 +215,6 @@ AccountPtr AccountManager::load(QSettings& settings)
return acc;
}
AccountStatePtr AccountManager::account(const QString& name)
{
foreach (const auto& acc, _accounts) {
if (acc->account()->displayName() == name) {
return acc;
}
}
return AccountStatePtr();
}
AccountState *AccountManager::addAccount(const AccountPtr& newAccount)
{
auto id = newAccount->id();
-5
Ver Arquivo
@@ -58,11 +58,6 @@ public:
*/
QList<AccountStatePtr> accounts() { return _accounts; }
/**
* Return the account state pointer for an account identified by its display name
*/
AccountStatePtr account(const QString& name);
/**
* Delete the AccountState
*/
+23 -133
Ver Arquivo
@@ -83,16 +83,11 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) :
#else
ui->_folderList->setMinimumWidth( 300 );
#endif
createAccountToolbox();
connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState*)),
SLOT(slotAccountAdded(AccountState*)));
connect(ui->_folderList, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(slotCustomContextMenuRequested(QPoint)));
connect(ui->_folderList, SIGNAL(expanded(QModelIndex)) , this, SLOT(refreshSelectiveSyncStatus()));
connect(ui->_folderList, SIGNAL(collapsed(QModelIndex)) , this, SLOT(refreshSelectiveSyncStatus()));
connect(ui->selectiveSyncNotification, SIGNAL(linkActivated(QString)),
this, SLOT(slotLinkActivated(QString)));
connect(_model, SIGNAL(suggestExpand(QModelIndex)), ui->_folderList, SLOT(expand(QModelIndex)));
connect(_model, SIGNAL(dirtyChanged()), this, SLOT(refreshSelectiveSyncStatus()));
refreshSelectiveSyncStatus();
@@ -129,46 +124,8 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) :
connect( &_quotaInfo, SIGNAL(quotaUpdated(qint64,qint64)),
this, SLOT(slotUpdateQuota(qint64,qint64)));
}
void AccountSettings::createAccountToolbox()
{
QMenu *menu = new QMenu();
_addAccountAction = new QAction(tr("Add new"), this);
menu->addAction(_addAccountAction);
connect(_addAccountAction, SIGNAL(triggered(bool)), SLOT(slotOpenAccountWizard()));
_toggleSignInOutAction = new QAction(tr("Sign out"), this);
connect(_toggleSignInOutAction, SIGNAL(triggered(bool)), SLOT(slotToggleSignInState()));
menu->addAction(_toggleSignInOutAction);
QAction *action = new QAction(tr("Remove"), this);
menu->addAction(action);
connect( action, SIGNAL(triggered(bool)), SLOT(slotDeleteAccount()));
ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
ui->_accountToolbox->setMenu(menu);
ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
// Expand already on single click
ui->_folderList->setExpandsOnDoubleClick(false);
QObject::connect(ui->_folderList, SIGNAL(clicked(const QModelIndex &)),
this, SLOT(slotFolderListClicked(const QModelIndex&)));
}
void AccountSettings::slotOpenAccountWizard()
{
if (QSystemTrayIcon::isSystemTrayAvailable()) {
topLevelWidget()->close();
}
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), 0);
}
void AccountSettings::slotToggleSignInState()
{
bool signedInState = _accountState->isSignedOut();
_accountState->setSignedOut( !signedInState );
connect(ui->signInButton, SIGNAL(clicked()) , this, SLOT(slotSignInAccount()));
connect(ui->deleteButton, SIGNAL(clicked()) , this, SLOT(slotDeleteAccount()));
}
void AccountSettings::doExpand()
@@ -199,14 +156,15 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
QAction *ac = menu->addAction(tr("Open folder"));
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotOpenCurrentFolder()));
ac = menu->addAction(tr("Choose what to sync"));
ac = menu->addAction(tr("Choose What to Sync"));
ac->setEnabled(folderConnected);
connect(ac, SIGNAL(triggered(bool)), this, SLOT(doExpand()));
ac = menu->addAction(folderPaused ? tr("Resume sync") : tr("Pause sync"));
ac->setEnabled(folderConnected);
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotEnableCurrentFolder()));
ac = menu->addAction(tr("Remove folder sync connection"));
ac = menu->addAction(tr("Remove sync"));
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotRemoveCurrentFolder()));
menu->exec(tv->mapToGlobal(pos));
}
@@ -320,13 +278,13 @@ void AccountSettings::slotRemoveCurrentFolder()
qDebug() << "Remove Folder alias " << alias;
if( !alias.isEmpty() ) {
QMessageBox messageBox(QMessageBox::Question,
tr("Confirm Folder Sync Connection Removal"),
tr("Confirm Sync Removal"),
tr("<p>Do you really want to stop syncing the folder <i>%1</i>?</p>"
"<p><b>Note:</b> This will <b>not</b> delete any files.</p>").arg(alias),
QMessageBox::NoButton,
this);
QPushButton* yesButton =
messageBox.addButton(tr("Remove Folder Sync Connection"), QMessageBox::YesRole);
messageBox.addButton(tr("Stop syncing"), QMessageBox::YesRole);
messageBox.addButton(tr("Cancel"), QMessageBox::NoRole);
messageBox.exec();
@@ -496,15 +454,7 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
ui->quotaProgressBar->setToolTip(toolTip);
} else {
ui->quotaProgressBar->setVisible(false);
ui->quotaInfoLabel->setToolTip(QString());
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
if (total == 0 || total == -1) {
ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
} else {
QString usedStr = Utility::octetsToString(used);
ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
}
ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
}
}
@@ -526,6 +476,12 @@ void AccountSettings::slotAccountStateChanged(int state)
serverWithUser = tr("%1 as <i>%2</i>").arg(server, cred->user());
}
if (state != AccountState::SignedOut && ui->signInButton->hasFocus()) {
// The button is about to be hidden, clear the focus so the focus don't go to the
// "remove account" button
ui->signInButton->clearFocus();
}
ui->signInButton->setVisible(state == AccountState::SignedOut);
if (state == AccountState::Connected) {
showConnectionLabel( tr("Connected to %1.").arg(serverWithUser) );
} else if (state == AccountState::ServiceUnavailable) {
@@ -553,49 +509,6 @@ void AccountSettings::slotAccountStateChanged(int state)
ui->_folderList->setExpanded(_model->index(i), false);
}
}
/* set the correct label for the Account toolbox button */
if( _accountState ) {
bool isConnected = _accountState->isConnected();
if( isConnected ) {
_toggleSignInOutAction->setText(tr("Sign out"));
} else {
_toggleSignInOutAction->setText(tr("Sign in"));
}
}
}
void AccountSettings::slotLinkActivated(const QString& link)
{
// Parse folder alias and filename from the link, calculate the index
// and select it if it exists.
const QStringList li = link.split(QLatin1String("?folder="));
if( li.count() > 1) {
QString myFolder = li[0];
const QString alias = li[1];
if(myFolder.endsWith(QLatin1Char('/'))) myFolder.chop(1);
// Make sure the folder itself is expanded
Folder *f = FolderMan::instance()->folder(alias);
QModelIndex folderIndx = _model->indexForPath(f, QString());
if( !ui->_folderList->isExpanded(folderIndx)) {
ui->_folderList->setExpanded(folderIndx, true);
}
QModelIndex indx = _model->indexForPath(f, myFolder);
if( indx.isValid() ) {
// make sure all the parents are expanded
for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
if( !ui->_folderList->isExpanded(i)) {
ui->_folderList->setExpanded(i, true);
}
}
ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
ui->_folderList->setCurrentIndex(indx);
ui->_folderList->scrollTo(indx);
} else {
qDebug() << "Unable to find a valid index for " << myFolder;
}
}
}
AccountSettings::~AccountSettings()
@@ -606,6 +519,7 @@ AccountSettings::~AccountSettings()
void AccountSettings::refreshSelectiveSyncStatus()
{
bool shouldBeVisible = _model->isDirty();
QStringList undecidedFolder;
for (int i = 0; !shouldBeVisible && i < _model->rowCount(); ++i) {
auto index = _model->index(i);
if (ui->_folderList->isExpanded(index) && _model->rowCount(index) > 0) {
@@ -613,45 +527,29 @@ void AccountSettings::refreshSelectiveSyncStatus()
}
}
QString msg;
int cnt = 0;
foreach (Folder *folder, FolderMan::instance()->map().values()) {
if (folder->accountState() != _accountState) {
continue;
}
auto undecidedList = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
QString p;
foreach(const auto &it, undecidedList) {
// FIXME: add the folder alias in a hoover hint.
// folder->alias() + QLatin1String("/")
if( cnt++ ) {
msg += QLatin1String(", ");
}
QString myFolder = (it);
if( myFolder.endsWith('/')) {
myFolder.chop(1);
}
QModelIndex theIndx = _model->indexForPath(folder, myFolder);
if(theIndx.isValid()) {
msg += QString::fromLatin1("<a href=\"%1?folder=%2\">%1</a>").arg(myFolder).arg(folder->alias());
} else {
msg += myFolder; // no link because we do not know the index yet.
}
undecidedFolder.append(it);
}
}
if (msg.isEmpty()) {
if (undecidedFolder.isEmpty()) {
ui->selectiveSyncNotification->setVisible(false);
ui->selectiveSyncNotification->setText(QString());
} else {
ui->selectiveSyncNotification->setVisible(true);
QString wholeMsg = tr("There are new folders that were not synchronized because they are too big: ") + msg;
ui->selectiveSyncNotification->setText(wholeMsg);
ui->selectiveSyncNotification->setText(
tr("There are new folders that were not synchronized because they are too big: %1")
.arg(undecidedFolder.join(tr(", "))));
shouldBeVisible = true;
}
ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
ui->selectiveSyncApply->setEnabled(_model->isDirty() || !undecidedFolder.isEmpty());
bool wasVisible = !ui->selectiveSyncStatus->isHidden();
if (wasVisible != shouldBeVisible) {
QSize hint = ui->selectiveSyncStatus->sizeHint();
@@ -668,14 +566,9 @@ void AccountSettings::refreshSelectiveSyncStatus()
}
}
void AccountSettings::slotAccountAdded(AccountState*)
void AccountSettings::slotSignInAccount()
{
// if the theme is limited to single account, the button must hide if
// there is already one account.
if( AccountManager::instance()->accounts().size() > 1 &&
!Theme::instance()->multiAccount() ) {
_addAccountAction->setVisible(false);
}
_accountState->setSignedOut(false);
}
void AccountSettings::slotDeleteAccount()
@@ -706,9 +599,6 @@ void AccountSettings::slotDeleteAccount()
// if there is no more account, show the wizard.
if( manager->accounts().isEmpty() ) {
// allow to add a new account if there is non any more. Always think
// about single account theming!
_addAccountAction->setVisible(true);
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)));
}
+1 -7
Ver Arquivo
@@ -76,21 +76,17 @@ protected slots:
void slotOpenCurrentFolder();
void slotFolderWizardAccepted();
void slotFolderWizardRejected();
void slotSignInAccount();
void slotDeleteAccount();
void slotToggleSignInState();
void slotOpenAccountWizard();
void slotAccountAdded(AccountState *);
void refreshSelectiveSyncStatus();
void slotCustomContextMenuRequested(const QPoint&);
void slotFolderListClicked( const QModelIndex& indx );
void doExpand();
void slotLinkActivated(const QString &link);
private:
void showConnectionLabel(const QString& message,
QStringList errors = QStringList());
bool event(QEvent*) Q_DECL_OVERRIDE;
void createAccountToolbox();
Ui::AccountSettings *ui;
@@ -99,8 +95,6 @@ private:
bool _wasDisabledBefore;
AccountState *_accountState;
QuotaInfo _quotaInfo;
QAction *_toggleSignInOutAction;
QAction *_addAccountAction;
};
} // namespace OCC
+50 -16
Ver Arquivo
@@ -6,18 +6,30 @@
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>557</height>
<width>469</width>
<height>652</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="QWidget" name="accountStatus" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="SslButton" name="sslButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@@ -30,7 +42,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item>
<widget class="QLabel" name="connectLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
@@ -49,17 +61,42 @@
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="_accountToolbox">
<item>
<widget class="QPushButton" name="signInButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Enter your credentials to connect to the server</string>
</property>
<property name="text">
<string>...</string>
<string>Sign in</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Remove the account configuration from the client</string>
</property>
<property name="text">
<string>Remove Account</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<item>
<layout class="QHBoxLayout" name="storageGroupBox">
<item>
<widget class="QLabel" name="quotaInfoLabel">
@@ -110,7 +147,7 @@
</item>
</layout>
</item>
<item row="2" column="0">
<item>
<widget class="QTreeView" name="_folderList">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
@@ -129,7 +166,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item>
<widget class="QWidget" name="selectiveSyncStatus" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@@ -148,9 +185,6 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
+1 -1
Ver Arquivo
@@ -142,7 +142,7 @@ void AccountState::checkConnectivity(CredentialFetchMode credentialsFetchMode)
}
if (_connectionValidator) {
qDebug() << "ConnectionValidator already running, ignoring" << account()->displayName();
qDebug() << "ConnectionValidator already running, ignoring";
return;
}
_credentialsFetchMode = credentialsFetchMode;
-153
Ver Arquivo
@@ -1,153 +0,0 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
* Copyright (C) by Olivier Goffart <ogoffart@woboq.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 "activityitemdelegate.h"
#include "folderstatusmodel.h"
#include "folderman.h"
#include "accountstate.h"
#include "utility.h"
#include <theme.h>
#include <account.h>
#include <QFileIconProvider>
#include <QPainter>
#include <QApplication>
namespace OCC {
int ActivityItemDelegate::_iconHeight = 0;
int ActivityItemDelegate::_margin = 0;
int ActivityItemDelegate::iconHeight()
{
if( _iconHeight == 0 ) {
QStyleOptionViewItem option;
QFont font = option.font;
QFontMetrics fm(font);
_iconHeight = qRound(fm.height() / 5.0 * 8.0);
}
return _iconHeight;
}
int ActivityItemDelegate::rowHeight()
{
if( _margin == 0 ) {
QStyleOptionViewItem opt;
QFont f = opt.font;
QFontMetrics fm(f);
_margin = fm.height()/4;
}
return iconHeight() + 2 * _margin;
}
QSize ActivityItemDelegate::sizeHint(const QStyleOptionViewItem & option ,
const QModelIndex & /* index */) const
{
QFont font = option.font;
return QSize( 0, rowHeight() );
}
void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter,option,index);
QFont font = option.font;
QFontMetrics fm( font );
int margin = fm.height()/4;
painter->save();
QIcon actionIcon = qvariant_cast<QIcon>(index.data(ActionIconRole));
QIcon userIcon = qvariant_cast<QIcon>(index.data(UserIconRole));
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
QString pathText = qvariant_cast<QString>(index.data(PathRole));
QString remoteLink = qvariant_cast<QString>(index.data(LinkRole));
QString timeText = qvariant_cast<QString>(index.data(PointInTimeRole));
QString accountRole = qvariant_cast<QString>(index.data(AccountRole));
bool accountOnline = qvariant_cast<bool> (index.data(AccountConnectedRole));
QRect actionIconRect = option.rect;
QRect userIconRect = option.rect;
int iconHeight = qRound(fm.height() / 5.0 * 8.0);
int iconWidth = iconHeight;
actionIconRect.setLeft( option.rect.left() + margin );
actionIconRect.setWidth( iconWidth );
actionIconRect.setHeight( iconHeight );
actionIconRect.setTop( actionIconRect.top() + margin );
userIconRect.setLeft( actionIconRect.right() + margin );
userIconRect.setWidth( iconWidth );
userIconRect.setHeight( iconHeight );
userIconRect.setTop( actionIconRect.top() );
int textTopOffset = qRound( (iconHeight - fm.height())/ 2.0 );
// time rect
QRect timeBox;
int timeBoxWidth = fm.boundingRect(QLatin1String("4 hour(s) ago on longlongdomain.org")).width(); // FIXME.
timeBox.setTop( actionIconRect.top()+textTopOffset);
timeBox.setLeft( option.rect.right() - timeBoxWidth- margin );
timeBox.setWidth( timeBoxWidth);
timeBox.setHeight( fm.height() );
QRect actionTextBox = timeBox;
actionTextBox.setLeft( userIconRect.right()+margin );
actionTextBox.setRight( timeBox.left()-margin );
/* === start drawing === */
QPixmap pm = actionIcon.pixmap(iconWidth, iconHeight, QIcon::Normal);
painter->drawPixmap(QPoint(actionIconRect.left(), actionIconRect.top()), pm);
pm = userIcon.pixmap(iconWidth, iconHeight, QIcon::Normal);
painter->drawPixmap(QPoint(userIconRect.left(), userIconRect.top()), pm);
const QString elidedAction = fm.elidedText(actionText, Qt::ElideRight, actionTextBox.width());
painter->drawText(actionTextBox, elidedAction);
int atPos = accountRole.indexOf(QLatin1Char('@'));
if( atPos > -1 ) {
accountRole.remove(0, atPos+1);
}
QString timeStr = tr("%1 on %2").arg(timeText).arg(accountRole);
if( !accountOnline ) {
QPalette p = option.palette;
painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
timeStr.append(" ");
timeStr.append(tr("(disconnected)"));
}
const QString elidedTime = fm.elidedText(timeStr, Qt::ElideRight, timeBox.width());
painter->drawText(timeBox, elidedTime);
painter->restore();
}
bool ActivityItemDelegate::editorEvent ( QEvent * event, QAbstractItemModel * model,
const QStyleOptionViewItem & option, const QModelIndex & index )
{
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
} // namespace OCC
-53
Ver Arquivo
@@ -1,53 +0,0 @@
/*
* Copyright (C) by Klaas Freitag <freitag@kde.org>
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#pragma once
#include <QStyledItemDelegate>
namespace OCC {
/**
* @brief The ActivityItemDelegate class
* @ingroup gui
*/
class ActivityItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
enum datarole { ActionIconRole = Qt::UserRole + 1,
UserIconRole,
AccountRole,
ActionTextRole,
PathRole,
LinkRole,
PointInTimeRole,
AccountConnectedRole };
void paint( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const Q_DECL_OVERRIDE;
QSize sizeHint( const QStyleOptionViewItem&, const QModelIndex& ) const Q_DECL_OVERRIDE;
bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index ) Q_DECL_OVERRIDE;
static int rowHeight();
static int iconHeight();
private:
static int _margin;
static int _iconHeight;
};
} // namespace OCC
-433
Ver Arquivo
@@ -1,433 +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 "activitywidget.h"
#include "configfile.h"
#include "syncresult.h"
#include "logger.h"
#include "utility.h"
#include "theme.h"
#include "folderman.h"
#include "syncfileitem.h"
#include "folder.h"
#include "openfilemanager.h"
#include "owncloudpropagator.h"
#include "account.h"
#include "accountstate.h"
#include "accountmanager.h"
#include "activityitemdelegate.h"
#include "protocolwidget.h"
#include "QProgressIndicator.h"
#include "ui_activitywidget.h"
#include <climits>
namespace OCC {
void ActivityList::setAccountName( const QString& name )
{
_accountName = name;
}
QString ActivityList::accountName() const
{
return _accountName;
}
/* ==================================================================== */
ActivityListModel::ActivityListModel(QWidget *parent)
:QAbstractListModel(parent)
{
}
QVariant ActivityListModel::data(const QModelIndex &index, int role) const
{
Activity a;
if (!index.isValid())
return QVariant();
a = _finalList.at(index.row());
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
QStringList list;
if (role == Qt::EditRole)
return QVariant();
switch (role) {
case ActivityItemDelegate::PathRole:
list = FolderMan::instance()->findFileInLocalFolders(a._file);
if( list.count() > 0 ) {
return QVariant(list.at(0));
}
return QVariant();
break;
case ActivityItemDelegate::ActionIconRole:
return QVariant(); // FIXME once the action can be quantified, display on Icon
break;
case ActivityItemDelegate::UserIconRole:
return QIcon(QLatin1String(":/client/resources/account.png"));
break;
case Qt::ToolTipRole:
case ActivityItemDelegate::ActionTextRole:
return a._subject;
break;
case ActivityItemDelegate::LinkRole:
return a._link;
break;
case ActivityItemDelegate::AccountRole:
return a._accName;
break;
case ActivityItemDelegate::PointInTimeRole:
return Utility::timeAgoInWords(a._dateTime);
break;
case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected());
break;
default:
return QVariant();
}
return QVariant();
}
int ActivityListModel::rowCount(const QModelIndex&) const
{
return _finalList.count();
}
// current strategy: Fetch 100 items per Account
bool ActivityListModel::canFetchMore(const QModelIndex& ) const
{
if( _activityLists.count() == 0 ) return true;
QMap<AccountState*, ActivityList>::const_iterator i = _activityLists.begin();
while (i != _activityLists.end()) {
AccountState *ast = i.key();
if( !ast->isConnected() ) {
return false;
}
ActivityList activities = i.value();
if( activities.count() == 0 &&
! _currentlyFetching.contains(ast) ) {
return true;
}
++i;
}
return false;
}
void ActivityListModel::startFetchJob(AccountState* s)
{
if( !s->isConnected() ) {
return;
}
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
QObject::connect(job, SIGNAL(jsonRecieved(QVariantMap)), this, SLOT(slotActivitiesReceived(QVariantMap)));
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
QList< QPair<QString,QString> > params;
params.append(qMakePair(QLatin1String("page"), QLatin1String("0")));
params.append(qMakePair(QLatin1String("pagesize"), QLatin1String("100")));
job->addQueryParams(params);
_currentlyFetching.insert(s);
qDebug() << "Start fetching activities for " << s->account()->displayName();
job->start();
}
void ActivityListModel::slotActivitiesReceived(const QVariantMap& json)
{
auto activities = json.value("ocs").toMap().value("data").toList();
qDebug() << "*** activities" << activities;
ActivityList list;
AccountState* ai = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
_currentlyFetching.remove(ai);
list.setAccountName( ai->account()->displayName());
foreach( auto activ, activities ) {
auto json = activ.toMap();
Activity a;
a._accName = ai->account()->displayName();
a._id = json.value("id").toLongLong();
a._subject = json.value("subject").toString();
a._message = json.value("message").toString();
a._file = json.value("file").toString();
a._link = json.value("link").toUrl();
a._dateTime = json.value("date").toDateTime();
list.append(a);
}
_activityLists[ai] = list;
combineActivityLists();
}
void ActivityListModel::combineActivityLists()
{
ActivityList resultList;
foreach( ActivityList list, _activityLists.values() ) {
resultList.append(list);
}
std::sort( resultList.begin(), resultList.end() );
beginInsertRows(QModelIndex(), 0, resultList.count()-1);
_finalList = resultList;
endInsertRows();
}
void ActivityListModel::fetchMore(const QModelIndex &)
{
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts();
foreach (AccountStatePtr asp, accounts) {
bool newItem = false;
// if the account is not yet managed, add an empty list.
if( !_activityLists.contains(asp.data()) ) {
_activityLists[asp.data()] = ActivityList();
newItem = true;
}
ActivityList activities = _activityLists[asp.data()];
if( newItem ) {
startFetchJob(asp.data());
}
}
}
void ActivityListModel::slotRefreshActivity(AccountState *ast)
{
if(ast && _activityLists.contains(ast)) {
qDebug() << "**** Refreshing Activity list for" << ast->account()->displayName();
_activityLists.remove(ast);
}
startFetchJob(ast);
}
void ActivityListModel::slotRemoveAccount(AccountState *ast )
{
if( _activityLists.contains(ast) ) {
int i = 0;
const QString accountToRemove = ast->account()->displayName();
QMutableListIterator<Activity> it(_finalList);
while (it.hasNext()) {
Activity activity = it.next();
if( activity._accName == accountToRemove ) {
beginRemoveRows(QModelIndex(), i, i+1);
it.remove();
endRemoveRows();
}
}
_activityLists.remove(ast);
_currentlyFetching.remove(ast);
}
}
/* ==================================================================== */
ActivityWidget::ActivityWidget(QWidget *parent) :
QWidget(parent),
_ui(new Ui::ActivityWidget)
{
_ui->setupUi(this);
// Adjust copyToClipboard() when making changes here!
#if defined(Q_OS_MAC)
_ui->_activityList->setMinimumWidth(400);
#endif
_model = new ActivityListModel(this);
ActivityItemDelegate *delegate = new ActivityItemDelegate;
delegate->setParent(this);
_ui->_activityList->setItemDelegate(delegate);
_ui->_activityList->setAlternatingRowColors(true);
_ui->_activityList->setModel(_model);
_ui->_headerLabel->setText(tr("Server Activities"));
_copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
connect(_copyBtn, SIGNAL(clicked()), SIGNAL(copyToClipboard()));
connect(_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(rowsInserted()));
connect( _ui->_activityList, SIGNAL(activated(QModelIndex)), this,
SLOT(slotOpenFile(QModelIndex)));
}
ActivityWidget::~ActivityWidget()
{
delete _ui;
}
void ActivityWidget::slotRefresh(AccountState *ptr)
{
_model->slotRefreshActivity(ptr);
}
void ActivityWidget::slotRemoveAccount( AccountState *ptr )
{
_model->slotRemoveAccount(ptr);
}
// FIXME: Reused from protocol widget. Move over to utilities.
QString ActivityWidget::timeString(QDateTime dt, QLocale::FormatType format) const
{
const QLocale loc = QLocale::system();
QString dtFormat = loc.dateTimeFormat(format);
static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
dtFormat.replace(re, "\\1:mm:ss");
return loc.toString(dt, dtFormat);
}
void ActivityWidget::storeActivityList( QTextStream& ts )
{
ActivityList activities = _model->activityList();
foreach( Activity activity, activities ) {
ts << left
// account name
<< qSetFieldWidth(30)
<< activity._accName
// date and time
<< qSetFieldWidth(34)
<< activity._dateTime.toString()
// subject
<< qSetFieldWidth(10)
<< activity._subject
// file
<< qSetFieldWidth(30)
<< activity._file
// message (mostly empty)
<< qSetFieldWidth(55)
<< activity._message
//
<< qSetFieldWidth(0)
<< endl;
}
}
void ActivityWidget::slotOpenFile(QModelIndex indx)
{
if( indx.isValid() ) {
QString fullPath = indx.data(ActivityItemDelegate::PathRole).toString();
if (QFile::exists(fullPath)) {
showInFileManager(fullPath);
}
}
}
/* ==================================================================== */
ActivitySettings::ActivitySettings(QWidget *parent)
:QWidget(parent)
{
QHBoxLayout *hbox = new QHBoxLayout(this);
setLayout(hbox);
// create a tab widget for the three activity views
_tab = new QTabWidget(this);
hbox->addWidget(_tab);
_activityWidget = new ActivityWidget(this);
_tab->addTab(_activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
connect(_activityWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
_protocolWidget = new ProtocolWidget(this);
_tab->addTab(_protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
connect(_protocolWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
// Add the not-synced list into the tab
QWidget *w = new QWidget;
QVBoxLayout *vbox2 = new QVBoxLayout(this);
vbox2->addWidget(new QLabel(tr("List of ignored or errornous files"), this));
vbox2->addWidget(_protocolWidget->issueWidget());
QDialogButtonBox *dlgButtonBox = new QDialogButtonBox(this);
vbox2->addWidget(dlgButtonBox);
QPushButton *_copyBtn = dlgButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
_copyBtn->setEnabled(true);
connect(_copyBtn, SIGNAL(clicked()), this, SLOT(slotCopyToClipboard()));
w->setLayout(vbox2);
_tab->addTab(w, Theme::instance()->syncStateIcon(SyncResult::Problem), tr("Not Synced"));
// Add a progress indicator to spin if the acitivity list is updated.
_progressIndicator = new QProgressIndicator(this);
_tab->setCornerWidget(_progressIndicator);
// connect a model signal to stop the animation.
connect(_activityWidget, SIGNAL(rowsInserted()), _progressIndicator, SLOT(stopAnimation()));
}
void ActivitySettings::slotCopyToClipboard()
{
QString text;
QTextStream ts(&text);
int idx = _tab->currentIndex();
QString theSubject;
if( idx == 0 ) {
// the activity widget
_activityWidget->storeActivityList(ts);
theSubject = tr("server activity list");
} else if(idx == 1 ) {
// the protocol widget
_protocolWidget->storeSyncActivity(ts);
theSubject = tr("sync activity list");
} else if(idx == 2 ) {
// issues Widget
theSubject = tr("not syned items list");
_protocolWidget->storeSyncIssues(ts);
}
QApplication::clipboard()->setText(text);
emit guiLog(tr("Copied to clipboard"), tr("The %1 has been copied to the clipboard.").arg(theSubject));
}
void ActivitySettings::slotRemoveAccount( AccountState *ptr )
{
_activityWidget->slotRemoveAccount(ptr);
}
void ActivitySettings::slotRefresh( AccountState* ptr )
{
if( ptr && ptr->isConnected() ) {
_progressIndicator->startAnimation();
_activityWidget->slotRefresh(ptr);
}
}
ActivitySettings::~ActivitySettings()
{
}
}
-193
Ver Arquivo
@@ -1,193 +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 ACTIVITYWIDGET_H
#define ACTIVITYWIDGET_H
#include <QDialog>
#include <QDateTime>
#include <QLocale>
#include <QAbstractListModel>
#include "progressdispatcher.h"
#include "owncloudgui.h"
#include "account.h"
#include "ui_activitywidget.h"
class QPushButton;
class QProgressIndicator;
namespace OCC {
class Account;
class AccountStatusPtr;
class ProtocolWidget;
namespace Ui {
class ActivityWidget;
}
class Application;
/**
* @brief Activity Structure
* @ingroup gui
*
* contains all the information describing a single activity.
*/
class Activity
{
public:
qlonglong _id;
QString _subject;
QString _message;
QString _file;
QUrl _link;
QDateTime _dateTime;
QString _accName;
/**
* @brief Sort operator to sort the list youngest first.
* @param val
* @return
*/
bool operator<( const Activity& val ) const {
return _dateTime.toMSecsSinceEpoch() > val._dateTime.toMSecsSinceEpoch();
}
};
/**
* @brief The ActivityList
* @ingroup gui
*
* A QList based list of Activities
*/
class ActivityList:public QList<Activity>
{
public:
void setAccountName( const QString& name );
QString accountName() const;
private:
QString _accountName;
};
/**
* @brief The ActivityListModel
* @ingroup gui
*
* Simple list model to provide the list view with data.
*/
class ActivityListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ActivityListModel(QWidget *parent=0);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
bool canFetchMore(const QModelIndex& ) const Q_DECL_OVERRIDE;
void fetchMore(const QModelIndex&) Q_DECL_OVERRIDE;
ActivityList activityList() { return _finalList; }
public slots:
void slotRefreshActivity(AccountState* ast);
void slotRemoveAccount( AccountState *ast );
private slots:
void slotActivitiesReceived(const QVariantMap& json);
private:
void startFetchJob(AccountState* s);
void combineActivityLists();
QMap<AccountState*, ActivityList> _activityLists;
ActivityList _finalList;
QSet<AccountState*> _currentlyFetching;
};
/**
* @brief The ActivityWidget class
* @ingroup gui
*
* The list widget to display the activities, contained in the
* subsequent ActivitySettings widget.
*/
class ActivityWidget : public QWidget
{
Q_OBJECT
public:
explicit ActivityWidget(QWidget *parent = 0);
~ActivityWidget();
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
void storeActivityList(QTextStream &ts);
public slots:
void slotOpenFile(QModelIndex indx);
void slotRefresh(AccountState* ptr);
void slotRemoveAccount( AccountState *ptr );
signals:
void guiLog(const QString&, const QString&);
void copyToClipboard();
void rowsInserted();
private:
QString timeString(QDateTime dt, QLocale::FormatType format) const;
Ui::ActivityWidget *_ui;
QPushButton *_copyBtn;
ActivityListModel *_model;
};
/**
* @brief The ActivitySettings class
* @ingroup gui
*
* Implements a tab for the settings dialog, displaying the three activity
* lists.
*/
class ActivitySettings : public QWidget
{
Q_OBJECT
public:
explicit ActivitySettings(QWidget *parent = 0);
~ActivitySettings();
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
public slots:
void slotRefresh( AccountState* ptr );
void slotRemoveAccount( AccountState *ptr );
void slotCopyToClipboard();
signals:
void guiLog(const QString&, const QString&);
private:
QTabWidget *_tab;
ActivityWidget *_activityWidget;
ProtocolWidget *_protocolWidget;
QProgressIndicator *_progressIndicator;
};
}
#endif // ActivityWIDGET_H
-38
Ver Arquivo
@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::ActivityWidget</class>
<widget class="QWidget" name="OCC::ActivityWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>693</width>
<height>556</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="_headerLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QListView" name="_activityList"/>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
</item>
</layout>
</widget>
<tabstops>
<tabstop>_activityList</tabstop>
<tabstop>_dialogButtonBox</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
-1
Ver Arquivo
@@ -427,7 +427,6 @@ void Application::showVersion()
stream << _theme->appName().toLatin1().constData()
<< QLatin1String(" version ")
<< _theme->version().toLatin1().constData() << endl;
stream << "Using Qt " << qVersion() << endl;
displayHelpText(helpText);
}
+25
Ver Arquivo
@@ -73,6 +73,29 @@ void ShibbolethCredentials::setAccount(Account* account)
}
}
void ShibbolethCredentials::syncContextPreInit(CSYNC* ctx)
{
csync_set_auth_callback (ctx, handleNeonSSLProblems);
}
QByteArray ShibbolethCredentials::prepareCookieData() const
{
QString cookiesAsString;
QList<QNetworkCookie> cookies = accountCookies(_account);
foreach(const QNetworkCookie &cookie, cookies) {
cookiesAsString += cookie.toRawForm(QNetworkCookie::NameAndValueOnly) + QLatin1String("; ");
}
return cookiesAsString.toLatin1();
}
void ShibbolethCredentials::syncContextPreStart (CSYNC* ctx)
{
csync_set_module_property(ctx, "session_key", prepareCookieData().data());
}
bool ShibbolethCredentials::changed(AbstractCredentials* credentials) const
{
ShibbolethCredentials* other(qobject_cast< ShibbolethCredentials* >(credentials));
@@ -184,6 +207,8 @@ void ShibbolethCredentials::invalidateToken()
jar->clearSessionCookies();
removeShibCookie();
_shibCookie = QNetworkCookie();
// ### access to ctx missing, but might not be required at all
//csync_set_module_property(ctx, "session_key", "");
}
void ShibbolethCredentials::onShibbolethCookieReceived(const QNetworkCookie& shibCookie)
+2
Ver Arquivo
@@ -48,6 +48,8 @@ public:
ShibbolethCredentials(const QNetworkCookie &cookie);
void setAccount(Account* account) Q_DECL_OVERRIDE;
void syncContextPreInit(CSYNC* ctx) Q_DECL_OVERRIDE;
void syncContextPreStart(CSYNC* ctx) Q_DECL_OVERRIDE;
bool changed(AbstractCredentials* credentials) const Q_DECL_OVERRIDE;
QString authType() const Q_DECL_OVERRIDE;
QString user() const Q_DECL_OVERRIDE;
+15 -16
Ver Arquivo
@@ -28,11 +28,12 @@
#include "clientproxy.h"
#include "syncengine.h"
#include "syncrunfilelog.h"
#include "socketapi.h"
#include "theme.h"
#include "filesystem.h"
#include "excludedfiles.h"
#include <csync_private.h>
#include "creds/abstractcredentials.h"
#include <QDebug>
@@ -109,9 +110,10 @@ bool Folder::init()
_csync_ctx = 0;
} else {
csync_set_log_callback( csyncLogCatcher );
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
csync_set_log_level( 11 );
Q_ASSERT( _accountState );
_accountState->account()->credentials()->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);
@@ -702,16 +704,16 @@ void Folder::removeFromSettings() const
bool Folder::isFileExcludedAbsolute(const QString& fullPath) const
{
if (!fullPath.startsWith(path())) {
// Mark paths we're not responsible for as excluded...
return true;
}
QString myFullPath = fullPath;
if (myFullPath.endsWith(QLatin1Char('/'))) {
myFullPath.chop(1);
}
if (!myFullPath.startsWith(path())) {
// Mark paths we're not responsible for as excluded...
return true;
}
QString relativePath = myFullPath.mid(path().size());
auto excl = ExcludedFiles::instance().isExcluded(myFullPath, relativePath, _definition.ignoreHiddenFiles);
return excl != CSYNC_NOT_EXCLUDED;
@@ -771,8 +773,6 @@ void Folder::wipe()
// Delete files that have been partially downloaded.
slotDiscardDownloadProgress();
//Unregister the socket API so it does not keep the .sync_journal file open
FolderMan::instance()->socketApi()->slotUnregisterPath(alias());
_journal.close(); // close the sync journal
QFile file(stateDbFile);
@@ -791,8 +791,6 @@ void Folder::wipe()
QFile::remove( stateDbFile + "-shm" );
QFile::remove( stateDbFile + "-wal" );
QFile::remove( stateDbFile + "-journal" );
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
}
bool Folder::setIgnoredFiles()
@@ -843,10 +841,11 @@ void Folder::startSync(const QStringList &pathList)
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection);
return;
}
_clientProxy.setCSyncProxy(_accountState->account()->url(), _csync_ctx);
} else if (proxyDirty()) {
_clientProxy.setCSyncProxy(_accountState->account()->url(), _csync_ctx);
setProxyDirty(false);
}
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
if (isBusy()) {
qCritical() << "* ERROR csync is still running and new sync requested.";
@@ -887,7 +886,7 @@ void Folder::startSync(const QStringList &pathList)
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
@@ -959,7 +958,7 @@ void Folder::slotCsyncUnavailable()
_csyncUnavail = true;
}
void Folder::slotSyncFinished(bool success)
void Folder::slotSyncFinished()
{
qDebug() << " - client version" << qPrintable(Theme::instance()->version())
<< " Qt" << qVersion()
@@ -1017,7 +1016,7 @@ void Folder::slotSyncFinished(bool success)
qDebug() << "the last" << _consecutiveFailingSyncs << "syncs failed";
}
if (_syncResult.status() == SyncResult::Success && success) {
if (_syncResult.status() == SyncResult::Success) {
// Clear the white list as all the folders that should be on that list are sync-ed
journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, QStringList());
}
@@ -1144,7 +1143,7 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
QMessageBox msgBox(QMessageBox::Warning, tr("Remove All Files?"),
msg.arg(alias()));
msgBox.addButton(tr("Remove all files"), QMessageBox::DestructiveRole);
QPushButton* keepBtn = msgBox.addButton(tr("Keep files"), QMessageBox::AcceptRole);
QPushButton* keepBtn = msgBox.addButton(tr("Keep files"), QMessageBox::ActionRole);
if (msgBox.exec() == -1) {
*cancel = true;
return;
+1 -1
Ver Arquivo
@@ -251,7 +251,7 @@ private slots:
void slotSyncStarted();
void slotSyncError(const QString& );
void slotCsyncUnavailable();
void slotSyncFinished(bool);
void slotSyncFinished();
void slotFolderDiscovered(bool local, QString folderName);
void slotTransmissionProgress(const ProgressInfo& pi);
+8 -32
Ver Arquivo
@@ -427,13 +427,14 @@ void FolderMan::slotSetFolderPaused( Folder *f, bool paused )
return;
}
f->setSyncPaused(paused);
slotScheduleSync(f);
if (!paused) {
_disabledFolders.remove(f);
slotScheduleSync(f);
} else {
_disabledFolders.insert(f);
}
f->setSyncPaused(paused);
emit folderSyncStateChange(f);
}
@@ -680,22 +681,15 @@ void FolderMan::slotStartScheduledFolderSync()
return;
}
// Find the first folder in the queue that can be synced.
Folder* f = nullptr;
while( !_scheduleQueue.isEmpty() ) {
f = _scheduleQueue.dequeue();
Q_ASSERT(f);
if( f->canSync() ) {
break;
}
}
// Try to start the top scheduled sync.
Folder *f = _scheduleQueue.dequeue();
emit scheduleQueueChanged();
Q_ASSERT(f);
// Start syncing this folder!
if( f ) {
if(f->canSync()) {
_currentSyncFolder = f;
f->startSync( QStringList() );
}
}
@@ -824,24 +818,6 @@ Folder *FolderMan::folderForPath(const QString &path)
return 0;
}
QStringList FolderMan::findFileInLocalFolders( const QString& relPath )
{
QStringList re;
foreach(Folder* folder, this->map().values()) {
QString path = folder->cleanPath();
QString remRelPath;
// cut off the remote path from the server path.
remRelPath = relPath.mid(folder->remotePath().length());
path += remRelPath;
if( QFile::exists(path) ) {
re.append( path );
}
}
return re;
}
void FolderMan::slotRemoveFolder( Folder *f )
{
if( !f ) {
-7
Ver Arquivo
@@ -57,13 +57,6 @@ public:
/** Returns the folder which the file or directory stored in path is in */
Folder* folderForPath(const QString& path);
/**
* returns a list of local files that exist on the local harddisk for an
* incoming relative server path. The method checks with all existing sync
* folders.
*/
QStringList findFileInLocalFolders( const QString& relPath );
/** Returns the folder by alias or NULL if no folder with the alias exists. */
Folder *folder( const QString& );
+21 -24
Ver Arquivo
@@ -28,10 +28,6 @@
namespace OCC {
FolderStatusDelegate::FolderStatusDelegate() : QStyledItemDelegate() {
m_moreIcon = QIcon(QLatin1String(":/client/resources/more.png"));
}
QString FolderStatusDelegate::addFolderText()
{
return tr("Add Folder Sync Connection");
@@ -83,6 +79,12 @@ QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem & option ,
h += aliasMargin*2 + errMsgs.count()*fm.height();
}
if( qvariant_cast<bool>(index.data(AddProgressSpace)) ) {
int margin = fm.height()/4;
h += (5 * margin); // All the margins
h += 2* fm.boundingRect(tr("File")).height();
}
return QSize( 0, h);
}
@@ -219,23 +221,20 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
painter->setFont(aliasFont);
painter->drawText(aliasRect, elidedAlias);
const bool showProgess = !overallString.isEmpty() || !itemString.isEmpty();
if(!showProgess) {
painter->setFont(subFont);
QString elidedRemotePathText;
painter->setFont(subFont);
QString elidedRemotePathText;
if (remotePath.isEmpty() || remotePath == QLatin1String("/")) {
elidedRemotePathText = subFm.elidedText(tr("Syncing selected files in your account with"),
Qt::ElideRight, remotePathRect.width());
} else {
elidedRemotePathText = subFm.elidedText(tr("Remote path: %1").arg(remotePath),
Qt::ElideMiddle, remotePathRect.width());
}
painter->drawText(remotePathRect, elidedRemotePathText);
QString elidedPathText = subFm.elidedText(pathText, Qt::ElideMiddle, localPathRect.width());
painter->drawText(localPathRect, elidedPathText);
if (remotePath.isEmpty() || remotePath == QLatin1String("/")) {
elidedRemotePathText = subFm.elidedText(tr("Syncing selected files in your account with"),
Qt::ElideRight, remotePathRect.width());
} else {
elidedRemotePathText = subFm.elidedText(tr("Remote path: %1").arg(remotePath),
Qt::ElideMiddle, remotePathRect.width());
}
painter->drawText(remotePathRect, elidedRemotePathText);
QString elidedPathText = subFm.elidedText(pathText, Qt::ElideMiddle, localPathRect.width());
painter->drawText(localPathRect, elidedPathText);
// paint an error overlay if there is an error string
@@ -271,7 +270,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
h += aliasMargin;
// Sync File Progress Bar: Show it if syncFile is not empty.
if (showProgess) {
if( !overallString.isEmpty() || !itemString.isEmpty()) {
int fileNameTextHeight = subFm.boundingRect(tr("File")).height();
int barHeight = qMax(fileNameTextHeight, aliasFm.height()+4); ;
int overallWidth = option.rect.width()-aliasMargin-nextToIcon;
@@ -284,7 +283,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
// Overall Progress Bar.
QRect pBRect;
pBRect.setTop( remotePathRect.top() );
pBRect.setTop( h );
pBRect.setLeft( nextToIcon );
pBRect.setHeight(barHeight);
pBRect.setWidth( overallWidth - progressTextWidth - 2 * margin );
@@ -328,15 +327,13 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
{
QStyleOptionToolButton btnOpt;
//btnOpt.text = QLatin1String("...");
btnOpt.text = QLatin1String("...");
btnOpt.state = option.state;
btnOpt.state &= ~(QStyle::State_Selected | QStyle::State_HasFocus);
btnOpt.state |= QStyle::State_Raised;
btnOpt.arrowType = Qt::NoArrow;
btnOpt.subControls = QStyle::SC_ToolButton;
btnOpt.rect = optionsButtonRect(option.rect);
btnOpt.icon = m_moreIcon;
btnOpt.iconSize = btnOpt.rect.size();
QApplication::style()->drawComplexControl( QStyle::CC_ToolButton, &btnOpt, painter );
}
}
+1 -2
Ver Arquivo
@@ -26,8 +26,6 @@ class FolderStatusDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
QIcon m_moreIcon;
FolderStatusDelegate();
enum datarole { FolderAliasRole = Qt::UserRole + 100,
HeaderRole,
@@ -43,6 +41,7 @@ public:
SyncProgressOverallPercent,
SyncProgressOverallString,
SyncProgressItemString,
AddProgressSpace,
WarningCount,
SyncRunning,
+41 -76
Ver Arquivo
@@ -22,7 +22,6 @@
#include <QFileIconProvider>
#include <QVarLengthArray>
#include <set>
Q_DECLARE_METATYPE(QPersistentModelIndex)
@@ -100,7 +99,7 @@ Qt::ItemFlags FolderStatusModel::flags ( const QModelIndex &index ) const
case RootFolder:
return Qt::ItemIsEnabled;
case SubFolder:
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
}
return 0;
}
@@ -185,16 +184,11 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
case FolderStatusDelegate::FolderAliasRole : return f->alias();
case FolderStatusDelegate::FolderSyncPaused : return f->syncPaused();
case FolderStatusDelegate::FolderAccountConnected : return accountConnected;
case Qt::ToolTipRole: {
QString toolTip;
case Qt::ToolTipRole:
if ( accountConnected )
toolTip = Theme::instance()->statusHeaderText(f->syncResult().status());
return Theme::instance()->statusHeaderText(f->syncResult().status());
else
toolTip = tr("Signed out");
toolTip += "\n";
toolTip += folderInfo._folder->path();
return toolTip;
}
return tr("Signed out");
case FolderStatusDelegate::FolderStatusIconRole:
if ( accountConnected ) {
auto theme = Theme::instance();
@@ -218,6 +212,8 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
} else {
return Theme::instance()->folderOfflineIcon();
}
case FolderStatusDelegate::AddProgressSpace:
return !progress.isNull();
case FolderStatusDelegate::SyncProgressItemString:
return progress._progressString;
case FolderStatusDelegate::WarningCount:
@@ -355,21 +351,13 @@ FolderStatusModel::SubFolderInfo* FolderStatusModel::infoForIndex(const QModelIn
QModelIndex FolderStatusModel::indexForPath(Folder *f, const QString& path) const
{
if( !f ) {
return QModelIndex();
}
int slashPos = path.lastIndexOf('/');
if (slashPos == -1) {
// first level folder
for (int i = 0; i < _folders.size(); ++i) {
if (_folders.at(i)._folder == f) {
if( path.isEmpty() ) { // the folder object
return index(i, 0);
}
for (int j = 0; j < _folders.at(i)._subs.size(); ++j) {
const QString subName = _folders.at(i)._subs.at(j)._name;
if (subName == path) {
if (_folders.at(i)._subs.at(j)._name == path) {
return index(j, 0, index(i));
}
}
@@ -513,7 +501,6 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
job->start();
QPersistentModelIndex persistentIndex(parent);
job->setProperty(propertyParentIndexC , QVariant::fromValue(persistentIndex));
@@ -522,7 +509,7 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
QTimer::singleShot(1000, this, SLOT(slotShowFetchProgress()));
}
void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
{
auto job = qobject_cast<LsColJob *>(sender());
Q_ASSERT(job);
@@ -542,45 +529,46 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
parentInfo->_fetching = false;
parentInfo->_fetched = true;
auto list = list_;
list.removeFirst(); // remove the parent item
QUrl url = parentInfo->_folder->remoteUrl();
QString pathToRemove = url.path();
if (!pathToRemove.endsWith('/'))
pathToRemove += '/';
// Drop the folder base path and check for excludes.
QMutableListIterator<QString> it(list);
while (it.hasNext()) {
it.next();
it.value().remove(pathToRemove);
if (parentInfo->_folder->isFileExcludedRelative(it.value())) {
it.remove();
}
}
beginInsertRows(idx, 0, list.count() - 1);
QStringList selectiveSyncBlackList;
if (parentInfo->_checked == Qt::PartiallyChecked) {
selectiveSyncBlackList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
}
auto selectiveSyncUndecidedList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
QVarLengthArray<int, 10> undecidedIndexes;
QVector<SubFolderInfo> newSubs;
std::set<QString> selectiveSyncUndecidedSet; // not QSet because it's not sorted
foreach (const QString &str, selectiveSyncUndecidedList) {
if (str.startsWith(parentInfo->_path) || parentInfo->_path == QLatin1String("/")) {
selectiveSyncUndecidedSet.insert(str);
}
}
newSubs.reserve(list.size() - 1);
for (int i = 1; // skip the parent item (first in the list)
i < list.size(); ++i) {
const QString &path = list.at(i);
auto relativePath = path.mid(pathToRemove.size());
if (parentInfo->_folder->isFileExcludedRelative(relativePath)) {
continue;
}
QVarLengthArray<int> undecidedIndexes;
int i = 0;
foreach (QString path, list) {
SubFolderInfo newInfo;
newInfo._folder = parentInfo->_folder;
newInfo._pathIdx = parentInfo->_pathIdx;
newInfo._pathIdx << newSubs.size();
newInfo._pathIdx << i++;
auto size = job ? job->_sizes.value(path) : 0;
newInfo._size = size;
newInfo._path = relativePath;
newInfo._name = relativePath.split('/', QString::SkipEmptyParts).last();
newInfo._path = path;
newInfo._name = path.split('/', QString::SkipEmptyParts).last();
if (relativePath.isEmpty())
if (path.isEmpty())
continue;
if (parentInfo->_checked == Qt::Unchecked) {
@@ -589,50 +577,30 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
newInfo._checked = Qt::Checked;
} else {
foreach(const QString &str , selectiveSyncBlackList) {
if (str == relativePath || str == QLatin1String("/")) {
if (str == path || str == QLatin1String("/")) {
newInfo._checked = Qt::Unchecked;
break;
} else if (str.startsWith(relativePath)) {
} else if (str.startsWith(path)) {
newInfo._checked = Qt::PartiallyChecked;
}
}
}
auto it = selectiveSyncUndecidedSet.lower_bound(relativePath);
if (it != selectiveSyncUndecidedSet.end()) {
if (*it == relativePath) {
foreach(const QString &str , selectiveSyncUndecidedList) {
if (str == path) {
newInfo._isUndecided = true;
selectiveSyncUndecidedSet.erase(it);
} else if ((*it).startsWith(relativePath)) {
} else if (str.startsWith(path)) {
undecidedIndexes.append(newInfo._pathIdx.last());
// Remove all the items from the selectiveSyncUndecidedSet that starts with this path
QString relativePathNext = relativePath;
relativePathNext[relativePathNext.length()-1].unicode()++;
auto it2 = selectiveSyncUndecidedSet.lower_bound(relativePathNext);
selectiveSyncUndecidedSet.erase(it, it2);
}
}
newSubs.append(newInfo);
parentInfo->_subs.append(newInfo);
}
beginInsertRows(idx, 0, newSubs.size() - 1);
parentInfo->_subs = std::move(newSubs);
endInsertRows();
for (auto it = undecidedIndexes.begin(); it != undecidedIndexes.end(); ++it) {
suggestExpand(idx.child(*it, 0));
}
/* Try to remove the the undecided lists the items that are not on the server. */
auto it = std::remove_if(selectiveSyncUndecidedList.begin(), selectiveSyncUndecidedList.end(),
[&](const QString &s) { return selectiveSyncUndecidedSet.count(s); } );
if (it != selectiveSyncUndecidedList.end()) {
selectiveSyncUndecidedList.erase(it, selectiveSyncUndecidedList.end());
parentInfo->_folder->journalDb()->setSelectiveSyncList(
SyncJournalDb::SelectiveSyncUndecidedList, selectiveSyncUndecidedList);
emit dirtyChanged();
}
}
void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
@@ -695,7 +663,7 @@ void FolderStatusModel::slotUpdateFolderState(Folder *folder)
for (int i = 0; i < _folders.count(); ++i) {
if (_folders.at(i)._folder == folder) {
emit dataChanged(index(i), index(i),
QVector<int>() << FolderStatusDelegate::FolderStatus);
QVector<int>() << FolderStatusDelegate::AddProgressSpace);
}
}
}
@@ -788,7 +756,8 @@ void FolderStatusModel::slotSetProgress(const ProgressInfo &progress)
auto *pi = &_folders[folderIndex]._progress;
QVector<int> roles;
roles << FolderStatusDelegate::SyncProgressItemString << FolderStatusDelegate::WarningCount;
roles << FolderStatusDelegate::AddProgressSpace << FolderStatusDelegate::SyncProgressItemString
<< FolderStatusDelegate::WarningCount;
if (!progress._currentDiscoveredFolder.isEmpty()) {
pi->_progressString = tr("Checking for changes in '%1'").arg(progress._currentDiscoveredFolder);
@@ -925,10 +894,7 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
if (folderIndex < 0) { return; }
SyncResult::Status state = f->syncResult().status();
if (f->syncPaused()) {
// Reset progress info.
_folders[folderIndex]._progress = SubFolderInfo::Progress();
} else if (state == SyncResult::NotYetStarted) {
if (state == SyncResult::NotYetStarted) {
FolderMan* folderMan = FolderMan::instance();
int pos = folderMan->scheduleQueue().indexOf(f);
if (folderMan->currentSyncFolder()
@@ -959,8 +925,7 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
if (state == SyncResult::Success) {
foreach (const SyncFileItemPtr &i, f->syncResult().syncFileItemVector()) {
if (i->_isDirectory && (i->_instruction == CSYNC_INSTRUCTION_NEW
|| i->_instruction == CSYNC_INSTRUCTION_REMOVE
|| i->_instruction == CSYNC_INSTRUCTION_RENAME)) {
|| i->_instruction == CSYNC_INSTRUCTION_REMOVE)) {
// There is a new or a removed folder. reset all data
auto & info = _folders[folderIndex];
info.resetSubs(this, index(folderIndex));
-2
Ver Arquivo
@@ -14,8 +14,6 @@
#ifndef MIRALL_FOLDERWATCHER_H
#define MIRALL_FOLDERWATCHER_H
#include "config.h"
#include <QList>
#include <QObject>
#include <QString>
+2 -2
Ver Arquivo
@@ -559,9 +559,9 @@ FolderWizard::FolderWizard(AccountPtr account, QWidget *parent)
}
setPage(Page_SelectiveSync, _folderWizardSelectiveSyncPage);
setWindowTitle( tr("Add Folder Sync Connection") );
setWindowTitle( tr("Add Folder") );
setOptions(QWizard::CancelButtonOnLeft);
setButtonText(QWizard::FinishButton, tr("Add Sync Connection"));
setButtonText(QWizard::FinishButton, tr("Add Folder"));
}
FolderWizard::~FolderWizard()
+15 -2
Ver Arquivo
@@ -21,7 +21,6 @@
#include "configfile.h"
#include "owncloudsetupwizard.h"
#include "accountmanager.h"
#include "synclogdialog.h"
#include "updater/updater.h"
#include "updater/ocupdater.h"
@@ -84,12 +83,18 @@ GeneralSettings::GeneralSettings(QWidget *parent) :
_ui->monoIconsCheckBox->setVisible(QDir(themeDir).exists());
connect(_ui->ignoredFilesButton, SIGNAL(clicked()), SLOT(slotIgnoreFilesEditor()));
connect(_ui->addAccountButton, SIGNAL(clicked()), SLOT(slotOpenAccountWizard()));
connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState*)),
SLOT(slotAccountAddedOrRemoved()));
connect(AccountManager::instance(), SIGNAL(accountRemoved(AccountState*)),
SLOT(slotAccountAddedOrRemoved()));
slotAccountAddedOrRemoved();
}
GeneralSettings::~GeneralSettings()
{
delete _ui;
delete _syncLogDialog;
}
QSize GeneralSettings::sizeHint() const {
@@ -164,4 +169,12 @@ void GeneralSettings::slotOpenAccountWizard()
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), 0);
}
void GeneralSettings::slotAccountAddedOrRemoved()
{
_ui->addAccountButton->setVisible(
AccountManager::instance()->accounts().isEmpty()
|| !Theme::instance()->singleAccount());
}
} // namespace OCC
+2 -2
Ver Arquivo
@@ -19,7 +19,6 @@
namespace OCC {
class IgnoreListEditor;
class SyncLogDialog;
namespace Ui {
class GeneralSettings;
@@ -45,13 +44,14 @@ private slots:
void slotUpdateInfo();
void slotIgnoreFilesEditor();
void slotOpenAccountWizard();
void slotAccountAddedOrRemoved();
private:
void loadMiscSettings();
Ui::GeneralSettings *_ui;
QPointer<IgnoreListEditor> _ignoreEditor;
QPointer<SyncLogDialog> _syncLogDialog;
};
+65 -60
Ver Arquivo
@@ -13,8 +13,8 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="generalGroupBox">
<property name="title">
<string>General Settings</string>
@@ -23,14 +23,14 @@
<item row="0" column="0">
<widget class="QCheckBox" name="autostartCheckBox">
<property name="text">
<string>&amp;Launch on System Startup</string>
<string>Launch on System Startup</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="desktopNotificationsCheckBox">
<property name="text">
<string>Show &amp;Desktop Notifications</string>
<string>Show Desktop Notifications</string>
</property>
</widget>
</item>
@@ -40,45 +40,73 @@
<string>For System Tray</string>
</property>
<property name="text">
<string>Use &amp;Monochrome Icons</string>
<string>Use Monochrome Icons</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="ignoredFilesButton">
<property name="text">
<string>Edit &amp;Ignored Files</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="ignoredFilesButton">
<property name="text">
<string>Edit Ignored Files</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="0" column="1" colspan="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>555</width>
<height>20</height>
</size>
</property>
</spacer>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="addAccountButton">
<property name="text">
<string>Add an Account</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<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="1" column="0" colspan="3">
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="newFolderLimitCheckBox">
<property name="text">
<string>Ask &amp;confirmation before downloading folders larger than</string>
<string>Ask confirmation before downloading folders larger than</string>
</property>
<property name="checked">
<bool>true</bool>
@@ -118,39 +146,26 @@
</layout>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="crashreporterCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>S&amp;how crash reporter</string>
</property>
</widget>
</item>
<item row="2" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<widget class="QCheckBox" name="crashreporterCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
<property name="text">
<string>Show crash reporter</string>
</property>
</spacer>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<item>
<widget class="QGroupBox" name="aboutGroupBox">
<property name="title">
<string>About</string>
@@ -166,7 +181,7 @@
</layout>
</widget>
</item>
<item row="3" column="0">
<item>
<widget class="QGroupBox" name="updatesGroupBox">
<property name="title">
<string>Updates</string>
@@ -217,7 +232,7 @@
</layout>
</widget>
</item>
<item row="4" column="0">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -232,16 +247,6 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>autostartCheckBox</tabstop>
<tabstop>desktopNotificationsCheckBox</tabstop>
<tabstop>monoIconsCheckBox</tabstop>
<tabstop>ignoredFilesButton</tabstop>
<tabstop>newFolderLimitCheckBox</tabstop>
<tabstop>newFolderLimitSpinBox</tabstop>
<tabstop>crashreporterCheckBox</tabstop>
<tabstop>restartButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
-1
Ver Arquivo
@@ -110,7 +110,6 @@ void IgnoreListEditor::slotUpdateLocalIgnoreList()
QMessageBox::warning(this, tr("Could not open file"),
tr("Cannot write changes to '%1'.").arg(ignoreFile));
}
ignores.close(); //close the file before reloading stuff.
FolderMan * folderMan = FolderMan::instance();
-21
Ver Arquivo
@@ -119,27 +119,6 @@ void NetworkSettings::loadBWLimitSettings()
{
ConfigFile cfgFile;
#if QT_VERSION < QT_VERSION_CHECK(5,3,3)
// QNAM bandwidth limiting only works with versions of Qt greater or equal to 5.3.3
// (It needs Qt commits 097b641 and b99fa32)
const char *v = qVersion(); // "x.y.z";
if (QLatin1String(v) < QLatin1String("5.3.3")) {
QString tooltip = tr("Qt >= 5.4 is required in order to use the bandwidth limit");
_ui->downloadBox->setEnabled(false);
_ui->uploadBox->setEnabled(false);
_ui->downloadBox->setToolTip(tooltip);
_ui->uploadBox->setToolTip(tooltip);
_ui->noDownloadLimitRadioButton->setChecked(true);
_ui->noUploadLimitRadioButton->setChecked(true);
if (cfgFile.useUploadLimit() != 0 || cfgFile.useDownloadLimit() != 0) {
// Update from old mirall that was using neon propagator jobs.
saveBWLimitSettings();
}
return;
}
#endif
int useDownloadLimit = cfgFile.useDownloadLimit();
if ( useDownloadLimit >= 1 ) {
_ui->downloadLimitRadioButton->setChecked(true);
+5 -5
Ver Arquivo
@@ -179,7 +179,7 @@
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QGroupBox" name="downloadBox">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -258,7 +258,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="uploadBox">
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -284,6 +284,9 @@
<property name="text">
<string>Limit automatically</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
@@ -291,9 +294,6 @@
<property name="text">
<string>No limit</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
-124
Ver Arquivo
@@ -1,124 +0,0 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 "ocsjob.h"
#include "networkjobs.h"
#include "account.h"
#include "json.h"
#include <QBuffer>
namespace OCC {
OcsJob::OcsJob(AccountPtr account)
: AbstractNetworkJob(account, "")
{
_passStatusCodes.append(100);
setIgnoreCredentialFailure(true);
}
void OcsJob::setVerb(const QByteArray &verb)
{
_verb = verb;
}
void OcsJob::addParam(const QString &name, const QString &value)
{
_params.append(qMakePair(name, value));
}
void OcsJob::addPassStatusCode(int code)
{
_passStatusCodes.append(code);
}
void OcsJob::appendPath(const QString &id)
{
setPath(path() + QLatin1Char('/') + id);
}
void OcsJob::start()
{
QNetworkRequest req;
req.setRawHeader("Ocs-APIREQUEST", "true");
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
QUrl url = Account::concatUrlPath(account()->url(), path());
QBuffer *buffer = new QBuffer;
if (_verb == "GET") {
url.setQueryItems(_params);
} else if (_verb == "POST" || _verb == "PUT") {
// Url encode the _postParams and put them in a buffer.
QByteArray postData;
Q_FOREACH(auto tmp, _params) {
if (!postData.isEmpty()) {
postData.append("&");
}
postData.append(QUrl::toPercentEncoding(tmp.first));
postData.append("=");
postData.append(QUrl::toPercentEncoding(tmp.second));
}
buffer->setData(postData);
}
//We want json data
auto queryItems = url.queryItems();
queryItems.append(qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json")));
url.setQueryItems(queryItems);
setReply(davRequest(_verb, url, req, buffer));
setupConnections(reply());
buffer->setParent(reply());
AbstractNetworkJob::start();
}
bool OcsJob::finished()
{
const QString replyData = reply()->readAll();
bool success;
QVariantMap json = QtJson::parse(replyData, success).toMap();
if (!success) {
qDebug() << "Could not parse reply to"
<< _verb
<< Account::concatUrlPath(account()->url(), path())
<< _params
<< ":" << replyData;
}
QString message;
const int statusCode = getJsonReturnCode(json, message);
if (!_passStatusCodes.contains(statusCode)) {
qDebug() << "Reply to"
<< _verb
<< Account::concatUrlPath(account()->url(), path())
<< _params
<< "has unexpected status code:" << statusCode << replyData;
emit ocsError(statusCode, message);
} else {
emit jobFinished(json);
}
return true;
}
int OcsJob::getJsonReturnCode(const QVariantMap &json, QString &message)
{
//TODO proper checking
int code = json.value("ocs").toMap().value("meta").toMap().value("statuscode").toInt();
message = json.value("ocs").toMap().value("meta").toMap().value("message").toString();
return code;
}
}
-131
Ver Arquivo
@@ -1,131 +0,0 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 OCSJOB_H
#define OCSJOB_H
#include "accountfwd.h"
#include "abstractnetworkjob.h"
#include <QVector>
#include <QList>
#include <QPair>
#include <QUrl>
namespace OCC {
/**
* @brief The OcsShareJob class
* @ingroup gui
*
* Base class for jobs that talk to the OCS endpoints on the server.
* All the communication logic is handled in this class.
*
* All OCS jobs (e.g. sharing) should extend this class.
*/
class OcsJob : public AbstractNetworkJob {
Q_OBJECT
protected:
explicit OcsJob(AccountPtr account);
/**
* Set the verb for the job
*
* @param verb currently supported PUT POST DELETE
*/
void setVerb(const QByteArray& verb);
/**
* Add a new parameter to the request.
* Depending on the verb this is GET or POST parameter
*
* @param name The name of the parameter
* @param value The value of the parameter
*/
void addParam(const QString& name, const QString &value);
/**
* Set the post parameters
*
* @param postParams list of pairs to add (urlEncoded) to the body of the
* request
*/
void setPostParams(const QList<QPair<QString, QString> >& postParams);
/**
* List of expected statuscodes for this request
* A warning will be printed to the debug log if a different status code is
* encountered
*
* @param code Accepted status code
*/
void addPassStatusCode(int code);
/**
* The base path for an OcsJob is always the same. But it could be the case that
* certain operations need to append something to the URL.
*
* This function appends the common id. so <PATH>/<ID>
*/
void appendPath(const QString &id);
public:
/**
* Parse the response and return the status code and the message of the
* reply (metadata)
*
* @param json The reply from OCS
* @param message The message that is set in the metadata
* @return The statuscode of the OCS response
*/
static int getJsonReturnCode(const QVariantMap &json, QString &message);
protected slots:
/**
* Start the OCS request
*/
void start() Q_DECL_OVERRIDE;
signals:
/**
* Result of the OCS request
*
* @param reply the reply
*/
void jobFinished(QVariantMap reply);
/**
* The status code was not one of the expected (passing)
* status code for this command
*
* @param statusCode The actual status code
* @param message The message provided by the server
*/
void ocsError(int statusCode, const QString &message);
private slots:
virtual bool finished() Q_DECL_OVERRIDE;
private:
QByteArray _verb;
QList<QPair<QString, QString> > _params;
QVector<int> _passStatusCodes;
};
}
#endif // OCSJOB_H
-46
Ver Arquivo
@@ -1,46 +0,0 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@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 "ocsshareejob.h"
namespace OCC {
OcsShareeJob::OcsShareeJob(AccountPtr account)
: OcsJob(account)
{
setPath("ocs/v1.php/apps/files_sharing/api/v1/sharees");
connect(this, SIGNAL(jobFinished(QVariantMap)), SLOT(jobDone(QVariantMap)));
}
void OcsShareeJob::getSharees(const QString &search,
const QString &itemType,
int page,
int perPage)
{
setVerb("GET");
addParam(QString::fromLatin1("search"), search);
addParam(QString::fromLatin1("itemType"), itemType);
addParam(QString::fromLatin1("page"), QString::number(page));
addParam(QString::fromLatin1("perPage"), QString::number(perPage));
start();
}
void OcsShareeJob::jobDone(const QVariantMap &reply)
{
emit shareeJobFinished(reply);
}
}
-55
Ver Arquivo
@@ -1,55 +0,0 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@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 OCSSHAREEJOB_H
#define OCSSHAREEJOB_H
#include "ocsjob.h"
#include <QVariantMap>
namespace OCC {
/**
* @brief The OcsShareeJob class
* @ingroup gui
*
* Fetching sharees from the OCS Sharee API
*/
class OcsShareeJob : public OcsJob {
Q_OBJECT
public:
explicit OcsShareeJob(AccountPtr account);
/**
* Get a list of sharees
*
* @param path Path to request shares for (default all shares)
*/
void getSharees(const QString& search, const QString& itemType, int page = 1, int perPage = 50);
signals:
/**
* Result of the OCS request
*
* @param reply The reply
*/
void shareeJobFinished(const QVariantMap &reply);
private slots:
void jobDone(const QVariantMap &reply);
};
}
#endif // OCSSHAREEJOB_H
-135
Ver Arquivo
@@ -1,135 +0,0 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 "ocssharejob.h"
#include "networkjobs.h"
#include "account.h"
#include "json.h"
#include <QBuffer>
namespace OCC {
OcsShareJob::OcsShareJob(AccountPtr account)
: OcsJob(account)
{
setPath("ocs/v1.php/apps/files_sharing/api/v1/shares");
connect(this, SIGNAL(jobFinished(QVariantMap)), this, SLOT(jobDone(QVariantMap)));
}
void OcsShareJob::getShares(const QString &path)
{
setVerb("GET");
addParam(QString::fromLatin1("path"), path);
addPassStatusCode(404);
start();
}
void OcsShareJob::deleteShare(const QString &shareId)
{
appendPath(shareId);
setVerb("DELETE");
start();
}
void OcsShareJob::setExpireDate(const QString &shareId, const QDate &date)
{
appendPath(shareId);
setVerb("PUT");
if (date.isValid()) {
addParam(QString::fromLatin1("expireDate"), date.toString("yyyy-MM-dd"));
} else {
addParam(QString::fromLatin1("expireDate"), QString());
}
_value = date;
start();
}
void OcsShareJob::setPassword(const QString &shareId, const QString &password)
{
appendPath(shareId);
setVerb("PUT");
addParam(QString::fromLatin1("password"), password);
_value = password;
start();
}
void OcsShareJob::setPublicUpload(const QString &shareId, bool publicUpload)
{
appendPath(shareId);
setVerb("PUT");
const QString value = QString::fromLatin1(publicUpload ? "true" : "false");
addParam(QString::fromLatin1("publicUpload"), value);
_value = publicUpload;
start();
}
void OcsShareJob::setPermissions(const QString &shareId,
const Share::Permissions permissions)
{
appendPath(shareId);
setVerb("PUT");
addParam(QString::fromLatin1("permissions"), QString::number(permissions));
_value = (int)permissions;
start();
}
void OcsShareJob::createLinkShare(const QString &path,
const QString &password)
{
setVerb("POST");
addParam(QString::fromLatin1("path"), path);
addParam(QString::fromLatin1("shareType"), QString::number(Share::TypeLink));
if (!password.isEmpty()) {
addParam(QString::fromLatin1("password"), password);
}
addPassStatusCode(403);
start();
}
void OcsShareJob::createShare(const QString& path,
const Share::ShareType shareType,
const QString& shareWith,
const Share::Permissions permissions)
{
setVerb("POST");
addParam(QString::fromLatin1("path"), path);
addParam(QString::fromLatin1("shareType"), QString::number(shareType));
addParam(QString::fromLatin1("shareWith"), shareWith);
addParam(QString::fromLatin1("permissions"), QString::number(permissions));
start();
}
void OcsShareJob::jobDone(QVariantMap reply)
{
emit shareJobFinished(reply, _value);
}
}
-127
Ver Arquivo
@@ -1,127 +0,0 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 OCSSHAREJOB_H
#define OCSSHAREJOB_H
#include "ocsjob.h"
#include "share.h"
#include <QVector>
#include <QList>
#include <QPair>
namespace OCC {
/**
* @brief The OcsShareJob class
* @ingroup gui
*
* Handle talking to the OCS Share API.
* For creation, deletion and modification of shares.
*/
class OcsShareJob : public OcsJob {
Q_OBJECT
public:
/**
* Constructor for new shares or listing of shares
*/
explicit OcsShareJob(AccountPtr account);
/**
* Get all the shares
*
* @param path Path to request shares for (default all shares)
*/
void getShares(const QString& path = "");
/**
* Delete the current Share
*/
void deleteShare(const QString &shareId);
/**
* Set the expiration date of a share
*
* @param date The expire date, if this date is invalid the expire date
* will be removed
*/
void setExpireDate(const QString &shareId, const QDate& date);
/**
* Set the password of a share
*
* @param password The password of the share, if the password is empty the
* share will be removed
*/
void setPassword(const QString &shareId, const QString& password);
/**
* Void set the share to be public upload
*
* @param publicUpload Set or remove public upload
*/
void setPublicUpload(const QString &shareId, bool publicUpload);
/**
* Set the permissions
*
* @param permissions
*/
void setPermissions(const QString &shareId,
const Share::Permissions permissions);
/**
* Create a new link share
*
* @param path The path of the file/folder to share
* @param password Optionally a password for the share
*/
void createLinkShare(const QString& path,
const QString& password = "");
/**
* Create a new share
*
* @param path The path of the file/folder to share
* @param shareType The type of share (user/group/link/federated)
* @param shareWith The uid/gid/federated id to share with
* @param permissions The permissions the share will have
*/
void createShare(const QString& path,
const Share::ShareType shareType,
const QString& shareWith = "",
const Share::Permissions permissions = Share::PermissionRead);
signals:
/**
* Result of the OCS request
* The value parameter is only set if this was a put request.
* e.g. if we set the password to 'foo' the QVariant will hold a QString with 'foo'.
* This is needed so we can update the share objects properly
*
* @param reply The reply
* @param value To what did we set a variable (if we set any).
*/
void shareJobFinished(QVariantMap reply, QVariant value);
private slots:
void jobDone(QVariantMap reply);
private:
QVariant _value;
};
}
#endif // OCSSHAREJOB_H
+13 -19
Ver Arquivo
@@ -150,7 +150,7 @@ void ownCloudGui::slotOpenSettingsDialog()
{
// if account is set up, start the configuration wizard.
if( !AccountManager::instance()->accounts().isEmpty() ) {
if (_settingsDialog.isNull() || QApplication::activeWindow() != _settingsDialog) {
if (_settingsDialog.isNull() || !_settingsDialog->isVisible()) {
slotShowSettings();
} else {
_settingsDialog->close();
@@ -204,10 +204,6 @@ void ownCloudGui::slotSyncStateChange( Folder* folder )
if (result.status() == SyncResult::Success || result.status() == SyncResult::Error) {
Logger::instance()->enterNextLogFile();
}
if (result.status() == SyncResult::NotYetStarted) {
_settingsDialog->slotRefreshActivity( folder->accountState() );
}
}
void ownCloudGui::slotFoldersChanged()
@@ -290,7 +286,7 @@ void ownCloudGui::slotComputeOverallSyncStatus()
foreach(Folder* folder, map.values()) {
qDebug() << "Folder in overallStatus Message: " << folder << " with name " << folder->alias();
QString folderMessage = folderMan->statusToString(folder->syncResult().status(), folder->syncPaused());
allStatusStrings += tr("Folder %1: %2").arg(folder->aliasGui(), folderMessage);
allStatusStrings += tr("Folder %1: %2").arg(folder->alias(), folderMessage);
}
trayMessage = allStatusStrings.join(QLatin1String("\n"));
#endif
@@ -335,6 +331,13 @@ void ownCloudGui::addAccountContextMenu(AccountStatePtr accountState, QMenu *men
menu->addAction(tr("Managed Folders:"))->setDisabled(true);
}
// If there can only be a single sync folder, showing the alias is
// unnecessary.
QString folderName = folder->alias();
if (singleSyncFolder) {
folderName = Theme::instance()->appNameGUI();
}
QAction *action = new QAction( tr("Open folder '%1'").arg(folder->path()), this );
connect(action, SIGNAL(triggered()),_folderOpenActionMapper, SLOT(map()));
_folderOpenActionMapper->setMapping( action, folder->alias() );
@@ -380,7 +383,9 @@ void ownCloudGui::setupContextMenu()
_tray->hide();
}
_contextMenu->clear();
slotRebuildRecentMenus();
_recentActionsMenu->clear();
_recentActionsMenu->addAction(tr("None."));
_recentActionsMenu->addAction(_actionRecent);
} else {
_contextMenu.reset(new QMenu());
_recentActionsMenu = new QMenu(tr("Recent Changes"), _contextMenu.data());
@@ -547,16 +552,6 @@ void ownCloudGui::slotRebuildRecentMenus()
_recentActionsMenu->addAction(_actionRecent);
}
/// Returns true if the completion of a given item should show up in the
/// 'Recent Activity' menu
static bool shouldShowInRecentsMenu(const SyncFileItem& item)
{
return
!Progress::isIgnoredKind(item._status)
&& item._instruction != CSYNC_INSTRUCTION_EVAL
&& item._instruction != CSYNC_INSTRUCTION_NONE;
}
void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo& progress)
{
@@ -582,8 +577,7 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo&
_actionRecent->setIcon( QIcon() ); // Fixme: Set a "in-progress"-item eventually.
if (!progress._lastCompletedItem.isEmpty()
&& shouldShowInRecentsMenu(progress._lastCompletedItem)) {
if (!progress._lastCompletedItem.isEmpty() && !Progress::isIgnoredKind(progress._lastCompletedItem._status)) {
if (Progress::isWarningKind(progress._lastCompletedItem._status)) {
// display a warn icon if warnings happened.
+55 -91
Ver Arquivo
@@ -27,7 +27,6 @@
#include "folder.h"
#include "openfilemanager.h"
#include "owncloudpropagator.h"
#include "activityitemdelegate.h"
#include "ui_protocolwidget.h"
@@ -66,24 +65,17 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) :
#if defined(Q_OS_MAC)
_ui->_treeWidget->setMinimumWidth(400);
#endif
_ui->_headerLabel->setText(tr("Local sync protocol"));
QPushButton *copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
copyBtn->setEnabled(true);
connect(copyBtn, SIGNAL(clicked()), SIGNAL(copyToClipboard()));
connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
// this view is used to display all errors such as real errors, soft errors and ignored files
// it is instantiated here, but made accessible via the method issueWidget() so that it can
// be embedded into another gui element.
_issueItemView = new QTreeWidget(this);
header.removeLast();
_issueItemView->setHeaderLabels( header );
_issueItemView->setColumnWidth(1, 180);
_issueItemView->setColumnCount(4);
_issueItemView->setRootIsDecorated(false);
_issueItemView->setTextElideMode(Qt::ElideMiddle);
_issueItemView->header()->setObjectName("ActivityErrorListHeader");
_retrySyncBtn = _ui->_dialogButtonBox->addButton(tr("Retry Sync"), QDialogButtonBox::ActionRole);
_retrySyncBtn->setEnabled(false);
connect(_retrySyncBtn, SIGNAL(clicked()), SLOT(slotRetrySync()));
_copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
_copyBtn->setEnabled(false);
connect(_copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
}
ProtocolWidget::~ProtocolWidget()
@@ -91,7 +83,38 @@ ProtocolWidget::~ProtocolWidget()
delete _ui;
}
#if 0
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(30)
<< 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::slotRetrySync()
{
FolderMan *folderMan = FolderMan::instance();
@@ -110,7 +133,6 @@ void ProtocolWidget::slotRetrySync()
folderMan->slotScheduleAllFolders();
}
#endif
void ProtocolWidget::showEvent(QShowEvent *ev)
{
@@ -136,18 +158,8 @@ void ProtocolWidget::cleanIgnoreItems(const QString& folder)
itemCnt--;
}
// limit also in the protocol widget
itemCnt = _issueItemView->topLevelItemCount();
// Limit the number of items in the issue view
while(itemCnt > 2000) {
delete _issueItemView->takeTopLevelItem(itemCnt - 1);
itemCnt--;
}
// clean up the issue list
for( int cnt = itemCnt-1; cnt >=0 ; cnt-- ) {
QTreeWidgetItem *item = _issueItemView->topLevelItem(cnt);
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt);
bool isErrorItem = item->data(0, IgnoredIndicatorRole).toBool();
QString itemFolder = item->data(2, Qt::UserRole).toString();
if( isErrorItem && itemFolder == folder ) {
@@ -180,6 +192,15 @@ void ProtocolWidget::slotOpenFile( QTreeWidgetItem *item, int )
}
}
QString ProtocolWidget::fixupFilename( const QString& name )
{
if( Utility::isMac() ) {
QString n(name);
return n.replace(QChar(':'), QChar('/'));
}
return name;
}
QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& folder, const SyncFileItem& item)
{
auto f = FolderMan::instance()->folder(folder);
@@ -193,7 +214,7 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
const QString longTimeStr = timeString(timestamp, QLocale::LongFormat);
columns << timeStr;
columns << Utility::fileNameForGuiUse(item._originalFile);
columns << fixupFilename(item._originalFile);
columns << f->shortGuiPath();
// If the error string is set, it's prefered because it is a useful user message.
@@ -221,7 +242,6 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
twitem->setData(0, IgnoredIndicatorRole, true);
}
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
twitem->setIcon(0, icon);
twitem->setToolTip(0, longTimeStr);
twitem->setToolTip(1, item._file);
@@ -232,7 +252,6 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
void ProtocolWidget::computeResyncButtonEnabled()
{
#if 0
FolderMan *folderMan = FolderMan::instance();
Folder::Map folders = folderMan->map();
@@ -253,7 +272,6 @@ void ProtocolWidget::computeResyncButtonEnabled()
_retrySyncBtn->setEnabled(enabled);
_retrySyncBtn->setToolTip(t);
#endif
}
@@ -277,65 +295,11 @@ void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItem
QTreeWidgetItem *line = createCompletedTreewidgetItem(folder, item);
if(line) {
if( item.hasErrorStatus() ) {
_issueItemView->insertTopLevelItem(0, line);
} else {
_ui->_treeWidget->insertTopLevelItem(0, line);
_ui->_treeWidget->insertTopLevelItem(0, line);
if (!_copyBtn->isEnabled()) {
_copyBtn->setEnabled(true);
}
}
}
void ProtocolWidget::storeSyncActivity(QTextStream& ts)
{
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(30)
<< 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;
}
}
void ProtocolWidget::storeSyncIssues(QTextStream& ts)
{
int topLevelItems = _issueItemView->topLevelItemCount();
for (int i = 0; i < topLevelItems; i++) {
QTreeWidgetItem *child = _issueItemView->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(30)
<< child->data(2, Qt::DisplayRole).toString()
// action
<< qSetFieldWidth(15)
<< child->data(3, Qt::DisplayRole).toString()
<< qSetFieldWidth(0)
<< endl;
}
}
}
+9 -6
Ver Arquivo
@@ -45,26 +45,28 @@ public:
~ProtocolWidget();
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
QTreeWidget *issueWidget() { return _issueItemView; }
void storeSyncActivity(QTextStream& ts);
void storeSyncIssues(QTextStream& ts);
public slots:
void slotProgressInfo( const QString& folder, const ProgressInfo& progress );
void slotItemCompleted( const QString& folder, const SyncFileItem& item, const PropagatorJob& job);
void slotOpenFile( QTreeWidgetItem* item, int );
protected slots:
void copyToClipboard();
void slotRetrySync();
protected:
void showEvent(QShowEvent *);
void hideEvent(QHideEvent *);
signals:
void copyToClipboard();
void guiLog(const QString&, const QString&);
private:
void setSyncResultStatus(const SyncResult& result );
void cleanIgnoreItems( const QString& folder );
void computeResyncButtonEnabled();
QString fixupFilename( const QString& name );
QTreeWidgetItem* createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item );
@@ -72,7 +74,8 @@ private:
const int IgnoredIndicatorRole;
Ui::ProtocolWidget *_ui;
QTreeWidget *_issueItemView;
QPushButton *_retrySyncBtn;
QPushButton *_copyBtn;
};
}
+49 -43
Ver Arquivo
@@ -13,53 +13,59 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="_headerLabel">
<property name="text">
<string>TextLabel</string>
<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>false</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 notr="true">3</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">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>
<item row="1" column="0" colspan="2">
<widget class="QTreeWidget" name="_treeWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="columnCount">
<number>4</number>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">2</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">3</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">4</string>
</property>
</column>
</widget>
</item>
<item row="2" column="1">
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
</item>
</layout>
</widget>
<resources/>
+2 -3
Ver Arquivo
@@ -102,10 +102,9 @@ void QuotaInfo::slotUpdateLastQuota(const QVariantMap &result)
{
// The server can return fractional bytes (#1374)
// <d:quota-available-bytes>1374532061.2</d:quota-available-bytes>
qint64 avail = result["quota-available-bytes"].toDouble();
quint64 avail = result["quota-available-bytes"].toDouble();
_lastQuotaUsedBytes = result["quota-used-bytes"].toDouble();
// negative value of the available quota have special meaning (#3940)
_lastQuotaTotalBytes = avail >= 0 ? _lastQuotaUsedBytes + avail : avail;
_lastQuotaTotalBytes = _lastQuotaUsedBytes + avail;
emit quotaUpdated(_lastQuotaTotalBytes, _lastQuotaUsedBytes);
_jobRestartTimer.start(defaultIntervalT);
_lastQuotaRecieved = QDateTime::currentDateTime();

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais