Merge remote-tracking branch 'origin/master' into new_nsis_modules

This commit is contained in:
Daniel Molkentin 2015-07-31 18:20:27 +02:00
commit b8d21bdd51
91 changed files with 1663 additions and 384 deletions

View file

@ -74,6 +74,10 @@ message(STATUS "GIT_SHA1 ${GIT_SHA1}")
set(SYSCONFDIR ${SYSCONF_INSTALL_DIR})
set(DATADIR ${DATA_INSTALL_DIR})
if(WIN32)
set(DATADIR "share")
endif(WIN32)
set(SHAREDIR ${DATADIR})
#####
## handle BUILD_OWNCLOUD_OSX_BUNDLE
@ -210,6 +214,7 @@ endif(UNIT_TESTING)
if(BUILD_OWNCLOUD_OSX_BUNDLE)
install(FILES sync-exclude.lst DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/)
configure_file(sync-exclude.lst bin/${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/sync-exclude.lst COPYONLY)
else()
install( FILES sync-exclude.lst DESTINATION ${SYSCONFDIR}/${APPLICATION_SHORTNAME} )
configure_file(sync-exclude.lst bin/sync-exclude.lst COPYONLY)

View file

@ -1021,11 +1021,11 @@
<key>CONCLUSION_ACTION</key>
<integer>0</integer>
<key>IDENTIFIER</key>
<string>com.owncCloud.finderPlugin</string>
<string>com.ownCloud.finderPlugin</string>
<key>LOCATION</key>
<integer>0</integer>
<key>NAME</key>
<string>Finder Plugin</string>
<string>Legacy Finder Plugin (OS X 10.9 or older)</string>
<key>OVERWRITE_PERMISSIONS</key>
<false/>
<key>VERSION</key>

View file

@ -8,4 +8,9 @@ tell application "Finder"
end tell
EOF
# Always enable the new 10.10 finder plugin if available
if [ -x "$(command -v pluginkit)" ]; then
pluginkit -e use -i @APPLICATION_REV_DOMAIN@.FinderSyncExt
fi
exit 0

View file

@ -45,5 +45,7 @@ iconv -t CP932 -o Japanese.nsh Japanese.nsh
iconv -t CP1250 -o Slovak.nsh Slovak.nsh
iconv -t CP1254 -o Turkish.nsh Turkish.nsh
iconv -t CP1252 -o Norwegian.nsh Norwegian.nsh
iconv -t CP1250 -o Polish.nsh Polish.nsh
iconv -t CP851 -o Slovak.nsh Slovak.nsh
iconv -t CP851 -o Czech.nsh Czech.nsh

View file

@ -21,7 +21,7 @@
#cmakedefine ZLIB_FOUND @ZLIB_FOUND@
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
#cmakedefine DATADIR "@DATADIR@"
#cmakedefine SHAREDIR "@SHAREDIR@"
#ifndef NEON_WITH_LFS
#cmakedefine NEON_WITH_LFS "@NEON_WITH_LFS@"

View file

@ -1,7 +1,6 @@
#cmakedefine PACKAGE "${APPLICATION_NAME}"
#cmakedefine VERSION "${APPLICATION_VERSION}"
#cmakedefine LOCALEDIR "${LOCALE_INSTALL_DIR}"
#cmakedefine DATADIR "${DATADIR}"
#cmakedefine LIBDIR "${LIBDIR}"
#cmakedefine PLUGINDIR "${PLUGINDIR}"
#cmakedefine SYSCONFDIR "${SYSCONFDIR}"

View file

@ -124,6 +124,8 @@ int csync_create(CSYNC **csync, const char *local, const char *remote) {
ctx->abort = false;
ctx->ignore_hidden_files = true;
*csync = ctx;
return 0;
}
@ -435,6 +437,7 @@ static int _csync_treewalk_visitor(void *obj, void *data) {
trav.error_status = cur->error_status;
trav.should_update_metadata = cur->should_update_metadata;
trav.has_ignored_files = cur->has_ignored_files;
if( other_node ) {
csync_file_stat_t *other_stat = (csync_file_stat_t*)other_node->data;
@ -592,6 +595,7 @@ int csync_commit(CSYNC *ctx) {
ctx->remote.read_from_db = 0;
ctx->read_remote_from_db = true;
ctx->db_is_empty = false;
ctx->ignore_hidden_files = true; // do NOT sync hidden files by default.
/* Create new trees */

View file

@ -49,41 +49,37 @@ struct csync_client_certs_s {
char *certificatePasswd;
};
/**
* Instruction enum. In the file traversal structure, it describes
* the csync state of a file.
*/
enum csync_status_codes_e {
CSYNC_STATUS_OK = 0,
CSYNC_STATUS_ERROR = 1024, /* don't use this code,
*/
CSYNC_STATUS_UNSUCCESSFUL,
CSYNC_STATUS_NO_LOCK, /* OBSOLETE does not happen anymore */
CSYNC_STATUS_STATEDB_LOAD_ERROR,
CSYNC_STATUS_STATEDB_CORRUPTED,
CSYNC_STATUS_NO_MODULE,
CSYNC_STATUS_TIMESKEW, /* OBSOLETE */
CSYNC_STATUS_UNSUCCESSFUL, /* Unspecific problem happend */
CSYNC_STATUS_NO_LOCK, /* OBSOLETE does not happen anymore */
CSYNC_STATUS_STATEDB_LOAD_ERROR, /* Statedb can not be loaded. */
CSYNC_STATUS_STATEDB_CORRUPTED, /* Statedb is corrupted */
CSYNC_STATUS_NO_MODULE, /* URL passed to csync does not start with owncloud:// or ownclouds:// */
CSYNC_STATUS_TIMESKEW, /* OBSOLETE */
CSYNC_STATUS_FILESYSTEM_UNKNOWN, /* UNUSED */
CSYNC_STATUS_TREE_ERROR,
CSYNC_STATUS_MEMORY_ERROR,
CSYNC_STATUS_PARAM_ERROR,
CSYNC_STATUS_UPDATE_ERROR,
CSYNC_STATUS_RECONCILE_ERROR,
CSYNC_STATUS_PROPAGATE_ERROR, /* OBSOLETE */
CSYNC_STATUS_TREE_ERROR, /* csync trees could not be created */
CSYNC_STATUS_MEMORY_ERROR, /* not enough memory problem */
CSYNC_STATUS_PARAM_ERROR, /* parameter is zero where not expected */
CSYNC_STATUS_UPDATE_ERROR, /* general update or discovery error */
CSYNC_STATUS_RECONCILE_ERROR, /* general reconcile error */
CSYNC_STATUS_PROPAGATE_ERROR, /* OBSOLETE */
CSYNC_STATUS_REMOTE_ACCESS_ERROR, /* UNUSED */
CSYNC_STATUS_REMOTE_CREATE_ERROR, /* UNUSED */
CSYNC_STATUS_REMOTE_STAT_ERROR, /* UNUSED */
CSYNC_STATUS_REMOTE_STAT_ERROR, /* UNUSED */
CSYNC_STATUS_LOCAL_CREATE_ERROR, /* UNUSED */
CSYNC_STATUS_LOCAL_STAT_ERROR, /* UNUSED */
CSYNC_STATUS_PROXY_ERROR, /* UNUSED */
CSYNC_STATUS_LOOKUP_ERROR,
CSYNC_STATUS_SERVER_AUTH_ERROR,
CSYNC_STATUS_PROXY_AUTH_ERROR,
CSYNC_STATUS_CONNECT_ERROR,
CSYNC_STATUS_TIMEOUT,
CSYNC_STATUS_HTTP_ERROR,
CSYNC_STATUS_PERMISSION_DENIED,
CSYNC_STATUS_LOCAL_STAT_ERROR, /* UNUSED */
CSYNC_STATUS_PROXY_ERROR, /* UNUSED */
CSYNC_STATUS_LOOKUP_ERROR, /* Neon fails to find proxy. Almost OBSOLETE */
CSYNC_STATUS_SERVER_AUTH_ERROR, /* UNUSED */
CSYNC_STATUS_PROXY_AUTH_ERROR, /* UNUSED */
CSYNC_STATUS_CONNECT_ERROR, /* neon driven connection failed */
CSYNC_STATUS_TIMEOUT, /* UNUSED */
CSYNC_STATUS_HTTP_ERROR, /* UNUSED */
CSYNC_STATUS_PERMISSION_DENIED, /* */
CSYNC_STATUS_NOT_FOUND,
CSYNC_STATUS_FILE_EXISTS,
CSYNC_STATUS_OUT_OF_SPACE,
@ -103,7 +99,8 @@ enum csync_status_codes_e {
CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST,
CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS,
CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME,
CYSNC_STATUS_FILE_LOCKED_OR_OPEN
CYSNC_STATUS_FILE_LOCKED_OR_OPEN,
CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN
};
typedef enum csync_status_codes_e CSYNC_STATUS;
@ -119,7 +116,10 @@ typedef enum csync_status_codes_e CSYNC_STATUS;
#define CSYNC_STATUS_IS_ERR(x) (unlikely((x) >= CSYNC_STATUS_ERROR))
#define CSYNC_STATUS_IS_EQUAL(x, y) ((x) == (y))
/**
* Instruction enum. In the file traversal structure, it describes
* the csync state of a file.
*/
enum csync_instructions_e {
CSYNC_INSTRUCTION_NONE = 0x00000000, /* Nothing to do (UPDATE|RECONCILE) */
CSYNC_INSTRUCTION_EVAL = 0x00000001, /* There was changed compared to the DB (UPDATE) */
@ -249,6 +249,9 @@ struct csync_tree_walk_file_s {
/* For directories: If the etag has been updated and need to be writen on the db */
int should_update_metadata;
/* For directories: Does it have children that were ignored (hidden or ignore pattern) */
int has_ignored_files;
const char *rename_path;
const char *etag;
const char *file_id;

View file

@ -27,7 +27,8 @@ enum csync_exclude_type_e {
CSYNC_FILE_EXCLUDE_AND_REMOVE,
CSYNC_FILE_EXCLUDE_LIST,
CSYNC_FILE_EXCLUDE_INVALID_CHAR,
CSYNC_FILE_EXCLUDE_LONG_FILENAME
CSYNC_FILE_EXCLUDE_LONG_FILENAME,
CSYNC_FILE_EXCLUDE_HIDDEN
};
typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;

View file

@ -92,7 +92,7 @@ struct csync_s {
/* hooks for checking the white list (uses the update_callback_userdata) */
int (*checkSelectiveSyncBlackListHook)(void*, const char*);
int (*checkSelectiveSyncNewShareHook)(void*, const char*);
int (*checkSelectiveSyncNewFolderHook)(void*, const char*);
csync_vio_opendir_hook remote_opendir_hook;
@ -168,6 +168,8 @@ struct csync_s {
*/
bool db_is_empty;
bool ignore_hidden_files;
struct csync_owncloud_ctx_s *owncloud_context;
};

View file

@ -298,20 +298,23 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
//hide instruction NONE messages when log level is set to debug,
//only show these messages on log level trace
const char *repo = ctx->current == REMOTE_REPLICA ? "server" : "client";
if(cur->instruction ==CSYNC_INSTRUCTION_NONE)
{
if(cur->type == CSYNC_FTW_TYPE_DIR)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,
"%-20s dir: %s",
"%-20s %s dir: %s",
csync_instruction_str(cur->instruction),
repo,
cur->path);
}
else
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,
"%-20s file: %s",
"%-20s %s file: %s",
csync_instruction_str(cur->instruction),
repo,
cur->path);
}
}
@ -320,15 +323,17 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
if(cur->type == CSYNC_FTW_TYPE_DIR)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"%-20s dir: %s",
"%-20s %s dir: %s",
csync_instruction_str(cur->instruction),
repo,
cur->path);
}
else
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"%-20s file: %s",
"%-20s %s file: %s",
csync_instruction_str(cur->instruction),
repo,
cur->path);
}
}

View file

@ -283,6 +283,9 @@ static int _csync_file_stat_from_metadata_table( csync_file_stat_t **st, sqlite3
if(column_count > 12 && sqlite3_column_int64(stmt,12)) {
(*st)->size = sqlite3_column_int64(stmt, 12);
}
if(column_count > 13) {
(*st)->has_ignored_files = sqlite3_column_int(stmt, 13);
}
}
} else {
if( rc != SQLITE_DONE ) {
@ -435,7 +438,7 @@ char *csync_statedb_get_etag( CSYNC *ctx, uint64_t jHash ) {
return ret;
}
#define BELOW_PATH_QUERY "SELECT phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize FROM metadata WHERE pathlen>? AND path LIKE(?)"
#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;

View file

@ -163,29 +163,27 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
len = strlen(path);
/* This code should probably be in csync_exclude, but it does not have the fs parameter.
Keep it here for now and TODO also find out if we want this for Windows
https://github.com/owncloud/mirall/issues/2086 */
if (fs->flags & CSYNC_VIO_FILE_FLAGS_HIDDEN) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file excluded because it is a hidden file: %s", path);
return 0;
}
/* Check if file is excluded */
excluded = csync_excluded(ctx, path,type);
if (excluded != CSYNC_NOT_EXCLUDED) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", path, excluded);
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) {
return 1;
}
if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
return 1;
}
if (ctx->current_fs) {
ctx->current_fs->has_ignored_files = true;
}
if( excluded == CSYNC_NOT_EXCLUDED ) {
/* Even if it is not excluded by a pattern, maybe it is to be ignored
* because it's a hidden file that should not be synced.
* This code should probably be in csync_exclude, but it does not have the fs parameter.
* Keep it here for now */
if (ctx->ignore_hidden_files && (fs->flags & CSYNC_VIO_FILE_FLAGS_HIDDEN)) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file excluded because it is a hidden file: %s", path);
excluded = CSYNC_FILE_EXCLUDE_HIDDEN;
}
} else {
/* File is ignored because it's matched by a user- or system exclude pattern. */
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", path, excluded);
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) {
return 1;
}
if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
return 1;
}
}
if (ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncBlackListHook) {
@ -208,9 +206,9 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
st->child_modified = 0;
st->has_ignored_files = 0;
/* check hardlink count */
/* FIXME: Under which conditions are the following two ifs true and the code
* is executed? */
if (type == CSYNC_FTW_TYPE_FILE ) {
if (fs->mtime == 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - mtime is zero!", path);
@ -242,12 +240,14 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
st->instruction = CSYNC_INSTRUCTION_NONE;
goto out;
}
if (excluded > CSYNC_NOT_EXCLUDED || type == CSYNC_FTW_TYPE_SLINK) {
if( type == CSYNC_FTW_TYPE_SLINK ) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK; /* Symbolic links are ignored. */
}
st->instruction = CSYNC_INSTRUCTION_IGNORE;
goto out;
if (ctx->current_fs) {
ctx->current_fs->has_ignored_files = true;
}
goto out;
}
/* Update detection: Check if a database entry exists.
@ -268,10 +268,10 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
/* we have an update! */
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Database entry found, compare: %" PRId64 " <-> %" PRId64
", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64
", size: %" PRId64 " <-> %" PRId64 ", perms: %s <-> %s",
", size: %" PRId64 " <-> %" PRId64 ", perms: %s <-> %s, ignore: %d",
((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 );
(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;
@ -316,6 +316,12 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Need to update metadata for: %s", path);
st->should_update_metadata = true;
}
/* If it was remembered in the db that the remote dir has ignored files, store
* that so that the reconciler can make advantage of.
*/
if( ctx->current == REMOTE_REPLICA ) {
st->has_ignored_files = tmp->has_ignored_files;
}
st->instruction = CSYNC_INSTRUCTION_NONE;
} else {
enum csync_vio_file_type_e tmp_vio_type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
@ -395,12 +401,10 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
/* file not found in statedb */
st->instruction = CSYNC_INSTRUCTION_NEW;
if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY && ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncNewShareHook) {
if (strchr(fs->remotePerm, 'S') != NULL) { /* check that the directory is shared */
if (ctx->callbacks.checkSelectiveSyncNewShareHook(ctx->callbacks.update_callback_userdata, path)) {
SAFE_FREE(st);
return 1;
}
if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY && ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncNewFolderHook) {
if (ctx->callbacks.checkSelectiveSyncNewFolderHook(ctx->callbacks.update_callback_userdata, path)) {
SAFE_FREE(st);
return 1;
}
}
goto out;
@ -418,13 +422,19 @@ out:
/* Set the ignored error string. */
if (st->instruction == CSYNC_INSTRUCTION_IGNORE) {
if (excluded == CSYNC_FILE_EXCLUDE_LIST) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST; /* File listed on ignore list. */
} else if (excluded == CSYNC_FILE_EXCLUDE_INVALID_CHAR) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS; /* File contains invalid characters. */
} else if (excluded == CSYNC_FILE_EXCLUDE_LONG_FILENAME) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME; /* File name is too long. */
}
if( type == CSYNC_FTW_TYPE_SLINK ) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK; /* Symbolic links are ignored. */
} else {
if (excluded == CSYNC_FILE_EXCLUDE_LIST) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST; /* File listed on ignore list. */
} else if (excluded == CSYNC_FILE_EXCLUDE_INVALID_CHAR) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS; /* File contains invalid characters. */
} else if (excluded == CSYNC_FILE_EXCLUDE_LONG_FILENAME) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME; /* File name is too long. */
} else if (excluded == CSYNC_FILE_EXCLUDE_HIDDEN ) {
st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN;
}
}
}
if (st->instruction != CSYNC_INSTRUCTION_NONE && st->instruction != CSYNC_INSTRUCTION_IGNORE
&& type != CSYNC_FTW_TYPE_DIR) {

View file

@ -224,7 +224,12 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
buf->type = CSYNC_VIO_FILE_TYPE_REGULAR;
break;
} while (0);
/* TODO Do we want to parse for CSYNC_VIO_FILE_FLAGS_HIDDEN ? */
/* 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;

View file

@ -175,9 +175,12 @@ static void create_dirs( const char *path )
*
* It appends a listing to the result member of the incoming struct in *state
* that can be compared later to what was expected in the calling functions.
*
* The int parameter cnt contains the number of seen files (not dirs) in the
* whole tree.
*
*/
static void traverse_dir(void **state, const char *dir)
static void traverse_dir(void **state, const char *dir, int *cnt)
{
csync_vio_handle_t *dh;
csync_vio_file_stat_t *dirent;
@ -216,20 +219,24 @@ static void traverse_dir(void **state, const char *dir)
is_dir ? "<DIR>":" ",
subdir), -1 );
if( !sv->result ) {
sv->result = c_strdup( subdir_out);
} else {
int newlen = 1+strlen(sv->result)+strlen(subdir_out);
char *tmp = sv->result;
sv->result = c_malloc(newlen);
strcpy( sv->result, tmp);
SAFE_FREE(tmp);
if( is_dir ) {
if( !sv->result ) {
sv->result = c_strdup( subdir_out);
} else {
int newlen = 1+strlen(sv->result)+strlen(subdir_out);
char *tmp = sv->result;
sv->result = c_malloc(newlen);
strcpy( sv->result, tmp);
SAFE_FREE(tmp);
strcat( sv->result, subdir_out );
strcat( sv->result, subdir_out );
}
} else {
*cnt = *cnt +1;
}
output(subdir_out);
if( is_dir ) {
traverse_dir( state, subdir);
traverse_dir( state, subdir, cnt);
}
SAFE_FREE(subdir);
@ -295,8 +302,9 @@ static void check_readdir_shorttree(void **state)
const char *t1 = "alibaba/und/die/vierzig/räuber/";
create_dirs( t1 );
traverse_dir(state, CSYNC_TEST_DIR);
int files_cnt = 0;
traverse_dir(state, CSYNC_TEST_DIR, &files_cnt);
assert_string_equal( sv->result,
"<DIR> C:/tmp/csync_test/alibaba"
@ -304,11 +312,13 @@ static void check_readdir_shorttree(void **state)
"<DIR> C:/tmp/csync_test/alibaba/und/die"
"<DIR> C:/tmp/csync_test/alibaba/und/die/vierzig"
"<DIR> C:/tmp/csync_test/alibaba/und/die/vierzig/räuber" );
assert_int_equal(files_cnt, 0);
}
static void check_readdir_with_content(void **state)
{
statevar *sv = (statevar*) *state;
int files_cnt = 0;
const char *t1 = "warum/nur/40/Räuber/";
create_dirs( t1 );
@ -317,15 +327,16 @@ static void check_readdir_with_content(void **state)
create_file( t1, "пя́тница.txt", "Am Freitag tanzt der Ürk");
traverse_dir(state, CSYNC_TEST_DIR);
traverse_dir(state, CSYNC_TEST_DIR, &files_cnt);
assert_string_equal( sv->result,
"<DIR> C:/tmp/csync_test/warum"
"<DIR> C:/tmp/csync_test/warum/nur"
"<DIR> C:/tmp/csync_test/warum/nur/40"
"<DIR> C:/tmp/csync_test/warum/nur/40/Räuber"
" C:/tmp/csync_test/warum/nur/40/Räuber/Räuber Max.txt"
" C:/tmp/csync_test/warum/nur/40/Räuber/пя́тница.txt");
"<DIR> C:/tmp/csync_test/warum/nur/40/Räuber");
/* " C:/tmp/csync_test/warum/nur/40/Räuber/Räuber Max.txt"
" C:/tmp/csync_test/warum/nur/40/Räuber/пя́тница.txt"); */
assert_int_equal(files_cnt, 2); /* Two files in the sub dir */
}
static void check_readdir_longtree(void **state)
@ -390,7 +401,7 @@ static void check_readdir_longtree(void **state)
/* assemble the result string ... */
int overall_len = 1+strlen(r1)+strlen(r2)+strlen(r3);
int files_cnt = 0;
char *result = c_malloc(overall_len);
*result = '\0';
@ -398,8 +409,8 @@ static void check_readdir_longtree(void **state)
strcat(result, r2);
strcat(result, r3);
traverse_dir(state, CSYNC_TEST_DIR);
traverse_dir(state, CSYNC_TEST_DIR, &files_cnt);
assert_int_equal(files_cnt, 0);
/* and compare. */
assert_string_equal( sv->result, result);
}

View file

@ -48,6 +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.
# returns true when one should try again!
if self._connectToSocketServer():
@ -156,7 +158,9 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
syncedFile = False
for reg_path in socketConnect.registered_paths:
filename = get_local_path(file.get_uri())
if filename.startswith(reg_path):
# only show the menu extension if the file is synced and the sync
# status is ok. Not for ignored files etc.
if filename.startswith(reg_path) and socketConnect.nautilusVFSFile_table[filename]['state'] == 'OK':
syncedFile = True
# if it is neither in a synced folder or is a directory
@ -183,12 +187,12 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
def __init__(self):
GObject.GObject.__init__(self)
self.nautilusVFSFile_table = {}
socketConnect.nautilusVFSFile_table = {}
socketConnect.addListener(self.handle_commands)
def find_item_for_file(self, path):
if path in self.nautilusVFSFile_table:
return self.nautilusVFSFile_table[path]
if path in socketConnect.nautilusVFSFile_table:
return socketConnect.nautilusVFSFile_table[path]
else:
return None
@ -202,12 +206,12 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
def invalidate_items_underneath(self, path):
update_items = []
if not self.nautilusVFSFile_table:
if not socketConnect.nautilusVFSFile_table:
self.askForOverlay(path)
else:
for p in self.nautilusVFSFile_table:
for p in socketConnect.nautilusVFSFile_table:
if p == path or p.startswith(path):
item = self.nautilusVFSFile_table[p]['item']
item = socketConnect.nautilusVFSFile_table[p]['item']
update_items.append(item)
for item in update_items:
@ -233,14 +237,16 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if action == 'STATUS':
newState = args[0]
emblem = Emblems[newState]
filename = ':'.join(args[1:])
if emblem:
itemStore = self.find_item_for_file(args[1])
itemStore = self.find_item_for_file(filename)
if itemStore:
if( not itemStore['state'] or newState != itemStore['state'] ):
item = itemStore['item']
item.add_emblem(emblem)
# print "Setting emblem on " + args[1]+ "<>"+emblem+"<>"
self.nautilusVFSFile_table[args[1]] = {'item': item, 'state':newState}
socketConnect.nautilusVFSFile_table[args[1]] = {'item': item, 'state':newState}
elif action == 'UPDATE_VIEW':
# Search all items underneath this path and invalidate them
@ -262,7 +268,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
for reg_path in socketConnect.registered_paths:
if filename.startswith(reg_path):
self.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
socketConnect.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
# item.add_string_attribute('share_state', "share state")
self.askForOverlay(filename)

@ -1 +1 @@
Subproject commit 8828099fd05b191cedf4d52f62f2405de6471415
Subproject commit 044580c32837edba05a055aabca27245939454eb

View file

@ -55,6 +55,7 @@ struct CmdOptions {
bool trustSSL;
bool useNetrc;
bool interactive;
bool ignoreHiddenFiles;
QString exclude;
QString unsyncedfolders;
};
@ -113,7 +114,7 @@ public:
_sslTrusted(false)
{}
QString queryPassword(bool *ok) Q_DECL_OVERRIDE {
QString queryPassword(bool *ok, const QString&) Q_DECL_OVERRIDE {
if (ok) {
*ok = true;
}
@ -154,6 +155,7 @@ 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 << " -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(1);
@ -216,6 +218,8 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
options->trustSSL = true;
} else if( option == "-n") {
options->useNetrc = true;
} else if( option == "-h") {
options->ignoreHiddenFiles = false;
} else if( option == "--non-interactive") {
options->interactive = false;
} else if( (option == "-u" || option == "--user") && !it.peekNext().startsWith("-") ) {
@ -264,11 +268,14 @@ void selectiveSyncFixup(OCC::SyncJournalDb *journal, const QStringList &newList)
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
qsrand(QTime::currentTime().msec() * QCoreApplication::applicationPid());
CmdOptions options;
options.silent = false;
options.trustSSL = false;
options.useNetrc = false;
options.interactive = true;
options.ignoreHiddenFiles = true;
ClientProxy clientProxy;
parseOptions( app.arguments(), &options );
@ -364,6 +371,9 @@ restart_sync:
return EXIT_FAILURE;
}
// 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;
@ -389,6 +399,8 @@ restart_sync:
csync_set_module_property(_csync_ctx, "proxy_port", (void*) &port);
}
}
QNetworkProxyFactory::setUseSystemConfiguration(false);
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, host, port));
}
} else {
clientProxy.setupQtProxyFromConfig();

View file

@ -26,6 +26,7 @@ set(client_UI
sslerrordialog.ui
owncloudsetuppage.ui
addcertificatedialog.ui
proxyauthdialog.ui
wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui
wizard/owncloudhttpcredspage.ui
@ -66,6 +67,8 @@ set(client_SRCS
accountstate.cpp
addcertificatedialog.cpp
authenticationdialog.cpp
proxyauthhandler.cpp
proxyauthdialog.cpp
creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp
creds/shibbolethcredentials.cpp
@ -219,7 +222,7 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
endforeach( _file )
endif(NOT WIN32)
install(FILES ${client_I18N} DESTINATION share/${APPLICATION_EXECUTABLE}/i18n)
install(FILES ${client_I18N} DESTINATION ${DATADIR}/${APPLICATION_EXECUTABLE}/i18n)
# we may not add MACOSX_BUNDLE here, if not building one
@ -298,6 +301,6 @@ endif()
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT WIN32)
configure_file(${CMAKE_SOURCE_DIR}/mirall.desktop.in
${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop DESTINATION share/applications )
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop DESTINATION ${DATADIR}/applications )
endif()

View file

@ -13,6 +13,7 @@
#include "accountmanager.h"
#include "sslerrordialog.h"
#include "proxyauthhandler.h"
#include <theme.h>
#include <creds/credentialsfactory.h>
#include <creds/abstractcredentials.h>
@ -168,7 +169,7 @@ void AccountManager::save(const AccountPtr& acc, QSettings& settings)
AccountPtr AccountManager::load(QSettings& settings)
{
auto acc = Account::create();
auto acc = createAccount();
acc->setUrl(settings.value(QLatin1String(urlC)).toUrl());
@ -186,7 +187,6 @@ AccountPtr AccountManager::load(QSettings& settings)
// now the cert, it is in the general group
settings.beginGroup(QLatin1String("General"));
acc->setApprovedCerts(QSslCertificate::fromData(settings.value(caCertsKeyC).toByteArray()));
acc->setSslErrorHandler(new SslDialogErrorHandler);
settings.endGroup();
return acc;
@ -219,6 +219,15 @@ void AccountManager::deleteAccount(AccountState* account)
emit accountRemoved(account);
}
AccountPtr AccountManager::createAccount()
{
AccountPtr acc = Account::create();
acc->setSslErrorHandler(new SslDialogErrorHandler);
connect(acc.data(), SIGNAL(proxyAuthenticationRequired(QNetworkProxy, QAuthenticator*)),
ProxyAuthHandler::instance(), SLOT(handleProxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
return acc;
}
void AccountManager::shutdown()
{

View file

@ -64,6 +64,12 @@ public:
void deleteAccount(AccountState *account);
/**
* Creates an account and sets up some basic handlers.
* Does *not* add the account to the account manager just yet.
*/
static AccountPtr createAccount();
private:
void save(const AccountPtr& account, QSettings& settings);
AccountPtr load(QSettings& settings);

View file

@ -117,7 +117,10 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) :
ui->quotaInfoLabel->setFont(smallFont);
_quotaLabel = new QLabel(ui->quotaProgressBar);
(new QVBoxLayout(ui->quotaProgressBar))->addWidget(_quotaLabel);
QVBoxLayout *quotaProgressLayout = new QVBoxLayout(ui->quotaProgressBar);
quotaProgressLayout->setContentsMargins(-1,0,-1,0);
quotaProgressLayout->setSpacing(0);
quotaProgressLayout->addWidget(_quotaLabel);
// This ensures the progress bar is big enough for the label.
ui->quotaProgressBar->setMinimumHeight(_quotaLabel->height());
@ -192,6 +195,31 @@ void AccountSettings::slotFolderWizardAccepted()
definition.alias = folderWizard->field(QLatin1String("alias")).toString();
definition.localPath = folderWizard->field(QLatin1String("sourceFolder")).toString();
definition.targetPath = folderWizard->property("targetPath").toString();
{
QDir dir(definition.localPath);
if (!dir.exists()) {
qDebug() << "Creating folder" << definition.localPath;
if (!dir.mkpath(".")) {
QMessageBox::warning(this, tr("Folder creation failed"),
tr("<p>Could not create local folder <i>%1</i>.")
.arg(QDir::toNativeSeparators(definition.localPath)));
return;
}
}
}
bool ignoreHidden = true;
/* take the value from the definition of already existing folders. All folders have
* the same setting so far, that's why it's ok to check the first one.
* The default is to not sync hidden files
*/
if( folderMan->map().count() > 0) {
ignoreHidden = folderMan->map().begin().value()->ignoreHiddenFiles();
}
definition.ignoreHiddenFiles = ignoreHidden;
auto selectiveSyncBlackList = folderWizard->property("selectiveSyncBlackList").toStringList();
folderMan->setSyncEnabled(true);

View file

@ -23,6 +23,7 @@
#include "folder.h"
#include "quotainfo.h"
#include "progressdispatcher.h"
#include "owncloudgui.h"
class QModelIndex;
class QNetworkReply;
@ -52,6 +53,7 @@ class AccountSettings : public QWidget
public:
explicit AccountSettings(AccountState *accountState, QWidget *parent = 0);
~AccountSettings();
QSize sizeHint() const Q_DECL_OVERRIDE { return ownCloudGui::settingsDialogSize(); }
signals:

View file

@ -54,6 +54,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Remove the account configuration from the client</string>
</property>
<property name="text">
<string>Delete</string>
</property>

View file

@ -32,10 +32,9 @@
#include "utility.h"
#include "clientproxy.h"
#include "sharedialog.h"
#include "updater/updater.h"
#include "accountmanager.h"
#include "creds/abstractcredentials.h"
#include "updater/ocupdater.h"
#include "config.h"
@ -77,7 +76,7 @@ QString applicationTrPath()
#elif defined(Q_OS_MAC)
return QApplication::applicationDirPath()+QLatin1String("/../Resources/Translations"); // path defaults to app dir.
#elif defined(Q_OS_UNIX)
return QString::fromLatin1(DATADIR "/" APPLICATION_EXECUTABLE "/i18n/");
return QString::fromLatin1(SHAREDIR "/" APPLICATION_EXECUTABLE "/i18n/");
#endif
}
}
@ -167,12 +166,12 @@ Application::Application(int &argc, char **argv) :
// Also check immediatly
QTimer::singleShot( 0, this, SLOT( slotCheckConnection() ));
if( cfg.skipUpdateCheck() ) {
qDebug() << Q_FUNC_INFO << "Skipping update check";
} else {
QTimer::singleShot( 3000, this, SLOT( slotStartUpdateDetector() ));
}
// Update checks
UpdaterScheduler *updaterScheduler = new UpdaterScheduler(this);
connect(updaterScheduler, SIGNAL(updaterAnnouncement(QString, QString)),
_gui, SLOT(slotShowTrayMessage(QString, QString)));
// Cleanup at Quit.
connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup()));
}
@ -212,12 +211,6 @@ void Application::slotCleanup()
_gui->deleteLater();
}
void Application::slotStartUpdateDetector()
{
Updater *updater = Updater::instance();
updater->backgroundCheckForUpdate();
}
void Application::slotCheckConnection()
{
auto list = AccountManager::instance()->accounts();

View file

@ -79,7 +79,6 @@ signals:
protected slots:
void slotParseMessage(const QString&, QObject*);
void slotCheckConnection();
void slotStartUpdateDetector();
void slotUseMonoIconsChanged( bool );
void slotCleanup();
void slotAccountStateAdded(AccountState *accountState);

View file

@ -23,17 +23,24 @@ using namespace QKeychain;
namespace OCC
{
QString HttpCredentialsGui::queryPassword(bool *ok)
QString HttpCredentialsGui::queryPassword(bool *ok, const QString& hint)
{
if (ok) {
QString str = QInputDialog::getText(0, tr("Enter Password"),
tr("Please enter %1 password:\n\nUser: %2\nAccount: %3\n")
.arg(Theme::instance()->appNameGUI(), _user, _account->displayName()),
QLineEdit::Password, _previousPassword, ok);
return str;
} else {
if (!ok) {
return QString();
}
QString msg = tr("Please enter %1 password:\n"
"\n"
"User: %2\n"
"Account: %3\n")
.arg(Theme::instance()->appNameGUI(), _user, _account->displayName());
if (!hint.isEmpty()) {
msg += QLatin1String("\n") + hint + QLatin1String("\n");
}
return QInputDialog::getText(0, tr("Enter Password"), msg,
QLineEdit::Password, _previousPassword,
ok);
}
} // namespace OCC

View file

@ -28,7 +28,7 @@ class HttpCredentialsGui : public HttpCredentials {
public:
explicit HttpCredentialsGui() : HttpCredentials() {}
HttpCredentialsGui(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd) : HttpCredentials(user, password, certificatePath, certificatePasswd) {}
QString queryPassword(bool *ok) Q_DECL_OVERRIDE;
QString queryPassword(bool *ok, const QString& hint) Q_DECL_OVERRIDE;
};
} // namespace OCC

View file

@ -191,6 +191,17 @@ QString Folder::path() const
return p;
}
bool Folder::ignoreHiddenFiles()
{
bool re(_definition.ignoreHiddenFiles);
return re;
}
void Folder::setIgnoreHiddenFiles(bool ignore)
{
_definition.ignoreHiddenFiles = ignore;
}
QString Folder::cleanPath()
{
QString cleanedPath = QDir::cleanPath(_definition.localPath);
@ -813,6 +824,9 @@ void Folder::startSync(const QStringList &pathList)
return;
}
// pass the setting if hidden files are to be ignored, will be read in csync_update
_csync_ctx->ignore_hidden_files = _definition.ignoreHiddenFiles;
_engine.reset(new SyncEngine( _accountState->account(), _csync_ctx, path(), remoteUrl().path(), remotePath(), &_journal));
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
@ -836,14 +850,14 @@ void Folder::startSync(const QStringList &pathList)
connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo)));
connect(_engine.data(), SIGNAL(jobCompleted(const SyncFileItem &)), this, SLOT(slotJobCompleted(const SyncFileItem &)));
connect(_engine.data(), SIGNAL(syncItemDiscovered(const SyncFileItem &)), this, SLOT(slotSyncItemDiscovered(const SyncFileItem &)));
connect(_engine.data(), SIGNAL(newSharedFolder(QString)), this, SLOT(slotNewSharedBigFolderDiscovered(QString)));
connect(_engine.data(), SIGNAL(newBigFolder(QString)), this, SLOT(slotNewBigFolderDiscovered(QString)));
setDirtyNetworkLimits();
ConfigFile cfgFile;
auto newFolderLimit = cfgFile.newSharedFolderSizeLimit();
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
quint64 limit = newFolderLimit.first ? newFolderLimit.second * 1000 * 1000 : -1; // convert from MB to B
_engine->setNewSharedFolderSizeLimit(limit);
_engine->setNewBigFolderSizeLimit(limit);
QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::QueuedConnection);
@ -857,10 +871,14 @@ void Folder::setDirtyNetworkLimits()
if (_engine) {
ConfigFile cfg;
int downloadLimit = 0;
if (cfg.useDownloadLimit()) {
int downloadLimit = -75; // 75%
int useDownLimit = cfg.useDownloadLimit();
if (useDownLimit >= 1) {
downloadLimit = cfg.downloadLimit() * 1000;
} else if (useDownLimit == 0) {
downloadLimit = 0;
}
int uploadLimit = -75; // 75%
int useUpLimit = cfg.useUploadLimit();
if ( useUpLimit >= 1) {
@ -894,6 +912,14 @@ void Folder::slotCsyncUnavailable()
void Folder::slotSyncFinished()
{
qDebug() << " - client version" << qPrintable(Theme::instance()->version())
<< " Qt" << qVersion()
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
<< " SSL " << QSslSocket::sslLibraryVersionString().toUtf8().data()
#endif
;
if( _csyncError ) {
qDebug() << "-> SyncEngine finished with ERROR, warn count is" << _syncResult.warnCount();
} else {
@ -1026,7 +1052,7 @@ void Folder::slotSyncItemDiscovered(const SyncFileItem & item)
emit ProgressDispatcher::instance()->syncItemDiscovered(alias(), item);
}
void Folder::slotNewSharedBigFolderDiscovered(const QString &newF)
void Folder::slotNewBigFolderDiscovered(const QString &newF)
{
auto newFolder = newF;
if (!newFolder.endsWith(QLatin1Char('/'))) {
@ -1047,8 +1073,14 @@ void Folder::slotNewSharedBigFolderDiscovered(const QString &newF)
if (!undecidedList.contains(newFolder)) {
undecidedList.append(newFolder);
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, undecidedList);
emit newSharedBigFolderDiscovered(newFolder);
emit newBigFolderDiscovered(newFolder);
}
QString message = tr("A new folder larger than %1 MB has been added: %2.\n"
"Please go in the settings to select it if you wish to download it.")
.arg(ConfigFile().newBigFolderSizeLimit().second).arg(newF);
auto logger = Logger::instance();
logger->postOptionalGuiLog(Theme::instance()->appNameGUI(), message);
}
@ -1086,6 +1118,7 @@ void FolderDefinition::save(QSettings& settings, const FolderDefinition& folder)
settings.setValue(QLatin1String("localPath"), folder.localPath);
settings.setValue(QLatin1String("targetPath"), folder.targetPath);
settings.setValue(QLatin1String("paused"), folder.paused);
settings.setValue(QLatin1String("ignoreHiddenFiles"), folder.ignoreHiddenFiles);
settings.endGroup();
}
@ -1097,6 +1130,7 @@ bool FolderDefinition::load(QSettings& settings, const QString& alias,
folder->localPath = settings.value(QLatin1String("localPath")).toString();
folder->targetPath = settings.value(QLatin1String("targetPath")).toString();
folder->paused = settings.value(QLatin1String("paused")).toBool();
folder->ignoreHiddenFiles = settings.value(QLatin1String("ignoreHiddenFiles"), QVariant(true)).toBool();
settings.endGroup();
return true;
}

View file

@ -52,7 +52,7 @@ class FolderDefinition
{
public:
FolderDefinition()
: paused(false)
: paused(false), ignoreHiddenFiles(false)
{}
/// The name of the folder in the ui and internally
@ -63,6 +63,8 @@ public:
QString targetPath;
/// whether the folder is paused
bool paused;
/// whether the folder syncs hidden files
bool ignoreHiddenFiles;
/// Saves the folder definition, creating a new settings group.
static void save(QSettings& settings, const FolderDefinition& folder);
@ -168,6 +170,13 @@ public:
void setDirtyNetworkLimits();
/**
* Ignore syncing of hidden files or not. This is defined in the
* folder definition
*/
bool ignoreHiddenFiles();
void setIgnoreHiddenFiles(bool ignore);
// Used by the Socket API
SyncJournalDb *journalDb() { return &_journal; }
@ -189,7 +198,7 @@ signals:
void syncFinished(const SyncResult &result);
void scheduleToSync(Folder*);
void progressInfo(const ProgressInfo& progress);
void newSharedBigFolderDiscovered(const QString &); // A new folder bigger than the threshold was discovered
void newBigFolderDiscovered(const QString &); // A new folder bigger than the threshold was discovered
public slots:
@ -244,7 +253,7 @@ private slots:
void slotEmitFinishedDelayed();
void watcherSlot(QString);
void slotNewSharedBigFolderDiscovered(const QString &);
void slotNewBigFolderDiscovered(const QString &);
private:
bool init();

View file

@ -151,6 +151,7 @@ void FolderMan::registerFolderMonitor( Folder *folder )
ConfigFile cfg;
fw->addIgnoreListFile( cfg.excludeFile(ConfigFile::SystemScope) );
fw->addIgnoreListFile( cfg.excludeFile(ConfigFile::UserScope) );
fw->setIgnoreHidden( folder->ignoreHiddenFiles() );
// Connect the pathChanged signal, which comes with the changed path,
// to the signal mapper which maps to the folder alias. The changed path
@ -413,6 +414,8 @@ Folder* FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
folder->saveToSettings();
}
qDebug() << "Migrated!";
settings.sync();
return folder;
}
@ -845,12 +848,12 @@ QString FolderMan::getBackupName( QString fullPathName ) const
if( fullPathName.isEmpty() ) return QString::null;
QString newName = fullPathName + QLatin1String(".oC_bak");
QString newName = fullPathName + tr(" (backup)");
QFileInfo fi( newName );
int cnt = 1;
do {
if( fi.exists() ) {
newName = fullPathName + QString( ".oC_bak_%1").arg(cnt++);
newName = fullPathName + tr(" (backup %1)").arg(cnt++);
fi.setFile(newName);
}
} while( fi.exists() );
@ -1096,15 +1099,21 @@ QString FolderMan::statusToString( SyncResult syncStatus, bool paused ) const
return folderMessage;
}
QString FolderMan::checkPathValidityForNewFolder(const QString& path)
QString FolderMan::checkPathValidityForNewFolder(const QString& path, bool forNewDirectory)
{
if (path.isEmpty()) {
return tr("No valid folder selected!");
}
QFileInfo selFile( path );
QString userInput = selFile.canonicalFilePath();
QStringList warnStrings;
if (!selFile.exists()) {
return checkPathValidityForNewFolder(selFile.dir().path(), true);
}
if( !selFile.isDir() ) {
return tr("No valid local folder selected!");
return tr("The selected path is not a directory!");
}
if ( !selFile.isWritable() ) {
@ -1126,12 +1135,12 @@ QString FolderMan::checkPathValidityForNewFolder(const QString& path)
return tr("The local path %1 is already an upload folder. Please pick another one!")
.arg(QDir::toNativeSeparators(userInput));
}
if (QDir::cleanPath(folderDir).startsWith(QDir::cleanPath(userInput)+'/')) {
if (!forNewDirectory && QDir::cleanPath(folderDir).startsWith(QDir::cleanPath(userInput)+'/')) {
return tr("An already configured folder is contained in the current entry.");
}
QString absCleanUserFolder = QDir::cleanPath(QDir(userInput).canonicalPath())+'/';
if (QDir::cleanPath(folderDir).startsWith(absCleanUserFolder) ) {
if (!forNewDirectory && QDir::cleanPath(folderDir).startsWith(absCleanUserFolder) ) {
return tr("The selected folder is a symbolic link. An already configured "
"folder is contained in the folder this link is pointing to.");
}

View file

@ -93,9 +93,11 @@ public:
* Check if @a path is a valid path for a new folder considering the already sync'ed items.
* Make sure that this folder, or any subfolder is not sync'ed alrady.
*
* \a forNewDirectory is internal and is used for recursion.
*
* @returns an empty string if it is allowed, or an error if it is not allowed
*/
QString checkPathValidityForNewFolder(const QString &path);
QString checkPathValidityForNewFolder(const QString &path, bool forNewDirectory = false);
signals:
/**

View file

@ -58,7 +58,7 @@ void FolderStatusModel::setAccountState(const AccountState* accountState)
connect(f, SIGNAL(progressInfo(ProgressInfo)), this, SLOT(slotSetProgress(ProgressInfo)), Qt::UniqueConnection);
connect(f, SIGNAL(syncStateChange()), this, SLOT(slotFolderSyncStateChange()), Qt::UniqueConnection);
connect(f, SIGNAL(newSharedBigFolderDiscovered(QString)), this, SIGNAL(dirtyChanged()), Qt::UniqueConnection);
connect(f, SIGNAL(newBigFolderDiscovered(QString)), this, SIGNAL(dirtyChanged()), Qt::UniqueConnection);
}
endResetModel();
@ -307,7 +307,7 @@ FolderStatusModel::SubFolderInfo* FolderStatusModel::infoForIndex(const QModelIn
QModelIndex FolderStatusModel::index(int row, int column, const QModelIndex& parent) const
{
if (!parent.isValid()) {
return createIndex(row, column, nullptr);
return createIndex(row, column/*, nullptr*/);
}
switch(classify(parent)) {
case AddButton: return QModelIndex();
@ -342,7 +342,7 @@ QModelIndex FolderStatusModel::parent(const QModelIndex& child) const
int i = 1;
Q_ASSERT(pathIdx.at(0) < _folders.count());
if (pathIdx.count() == 2) {
return createIndex(pathIdx.at(0), 0, nullptr);
return createIndex(pathIdx.at(0), 0/*, nullptr*/);
}
const SubFolderInfo *info = &_folders[pathIdx.at(0)];

View file

@ -36,7 +36,8 @@
namespace OCC {
FolderWatcher::FolderWatcher(const QString &root, QObject *parent)
: QObject(parent)
: QObject(parent),
_ignoreHidden(true)
{
_d.reset(new FolderWatcherPrivate(this, root));
@ -46,6 +47,16 @@ FolderWatcher::FolderWatcher(const QString &root, QObject *parent)
FolderWatcher::~FolderWatcher()
{ }
void FolderWatcher::setIgnoreHidden(bool ignore)
{
_ignoreHidden = ignore;
}
bool FolderWatcher::ignoreHidden()
{
return _ignoreHidden;
}
void FolderWatcher::addIgnoreListFile( const QString& file )
{
if( file.isEmpty() ) return;
@ -71,10 +82,14 @@ bool FolderWatcher::pathIsIgnored( const QString& path )
{
if( path.isEmpty() ) return true;
QFileInfo fInfo(path);
if( fInfo.isHidden() ) {
qDebug() << "* Discarded as is hidden!" << fInfo.filePath();
return true;
// if events caused by changes to hidden files should be ignored, a QFileInfo
// object will tell us if the file is hidden
if( _ignoreHidden ) {
QFileInfo fInfo(path);
if( fInfo.isHidden() ) {
qDebug() << "* Discarded as is hidden!" << fInfo.filePath();
return true;
}
}
// TODO: Best use csync_excluded_no_ctx() here somehow!

View file

@ -77,6 +77,10 @@ public:
/* Check if the path is ignored. */
bool pathIsIgnored( const QString& path );
/* set if the folderwatcher ignores events of hidden files */
void setIgnoreHidden(bool ignore);
bool ignoreHidden();
signals:
/** Emitted when one of the watched directories or one
* of the contained files is changed. */
@ -98,6 +102,7 @@ private:
QStringList _ignores;
QTime _timer;
QSet<QString> _lastPaths;
bool _ignoreHidden;
friend class FolderWatcherPrivate;
};

View file

@ -83,21 +83,24 @@ GeneralSettings::~GeneralSettings()
delete _ui;
}
QSize GeneralSettings::sizeHint() const {
return QSize(ownCloudGui::settingsDialogSize().width(), QWidget::sizeHint().height());
}
void GeneralSettings::loadMiscSettings()
{
ConfigFile cfgFile;
_ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons());
_ui->desktopNotificationsCheckBox->setChecked(cfgFile.optionalDesktopNotifications());
_ui->crashreporterCheckBox->setChecked(cfgFile.crashReporter());
auto newFolderLimit = cfgFile.newSharedFolderSizeLimit();
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
_ui->newFolderLimitCheckBox->setChecked(newFolderLimit.first);
_ui->newFolderLimitSpinBox->setValue(newFolderLimit.second);
}
void GeneralSettings::slotUpdateInfo()
{
if (OCUpdater *updater = dynamic_cast<OCUpdater*>(Updater::instance()))
{
if (OCUpdater *updater = dynamic_cast<OCUpdater*>(Updater::instance())) {
connect(updater, SIGNAL(downloadStateChanged()), SLOT(slotUpdateInfo()), Qt::UniqueConnection);
connect(_ui->restartButton, SIGNAL(clicked()), updater, SLOT(slotStartInstaller()), Qt::UniqueConnection);
connect(_ui->restartButton, SIGNAL(clicked()), qApp, SLOT(quit()), Qt::UniqueConnection);
@ -117,7 +120,7 @@ void GeneralSettings::saveMiscSettings()
Theme::instance()->setSystrayUseMonoIcons(isChecked);
cfgFile.setCrashReporter(_ui->crashreporterCheckBox->isChecked());
cfgFile.setNewSharedFolderSizeLimit(_ui->newFolderLimitCheckBox->isChecked(),
cfgFile.setNewBigFolderSizeLimit(_ui->newFolderLimitCheckBox->isChecked(),
_ui->newFolderLimitSpinBox->value());
}

View file

@ -35,6 +35,7 @@ class GeneralSettings : public QWidget
public:
explicit GeneralSettings(QWidget *parent = 0);
~GeneralSettings();
QSize sizeHint() const;
private slots:
void saveMiscSettings();

View file

@ -36,6 +36,9 @@
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="monoIconsCheckBox">
<property name="toolTip">
<string>For System Tray</string>
</property>
<property name="text">
<string>Use Monochrome Icons</string>
</property>
@ -55,7 +58,7 @@
<item>
<widget class="QCheckBox" name="newFolderLimitCheckBox">
<property name="text">
<string>Ask confirmation before downloading shared folders larger than</string>
<string>Ask confirmation before downloading folders larger than</string>
</property>
<property name="checked">
<bool>true</bool>

View file

@ -57,6 +57,13 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
ui->tableWidget->horizontalHeader()->setResizeMode(patternCol, QHeaderView::Stretch);
ui->tableWidget->verticalHeader()->setVisible(false);
/* value for syncing hidden files */
bool ignoreHidden = true;
if( FolderMan::instance()->map().count() > 0 ) {
ignoreHidden = FolderMan::instance()->map().begin().value()->ignoreHiddenFiles();
}
ui->ignoreHiddenFilesCheckBox->setChecked( !ignoreHidden );
}
IgnoreListEditor::~IgnoreListEditor()
@ -64,6 +71,11 @@ IgnoreListEditor::~IgnoreListEditor()
delete ui;
}
bool IgnoreListEditor::ignoreHiddenFiles()
{
return ! ui->ignoreHiddenFilesCheckBox->isChecked();
}
void IgnoreListEditor::slotItemSelectionChanged()
{
QTableWidgetItem *item = ui->tableWidget->currentItem();
@ -110,6 +122,17 @@ void IgnoreListEditor::slotUpdateLocalIgnoreList()
QMessageBox::warning(this, tr("Could not open file"),
tr("Cannot write changes to '%1'.").arg(ignoreFile));
}
/* handle the hidden file checkbox */
bool ignoreHiddenFiles = ! ui->ignoreHiddenFilesCheckBox->isChecked();
/* the ignoreHiddenFiles flag is a folder specific setting, but for now, it is
* handled globally. Save it to every folder that is defined.
*/
foreach (Folder* folder, FolderMan::instance()->map()) {
folder->setIgnoreHiddenFiles(ignoreHiddenFiles);
folder->saveToSettings();
}
}
void IgnoreListEditor::slotAddPattern()

View file

@ -36,6 +36,8 @@ public:
explicit IgnoreListEditor(QWidget *parent = 0);
~IgnoreListEditor();
bool ignoreHiddenFiles();
private slots:
void slotItemSelectionChanged();
void slotRemoveCurrentItem();

View file

@ -6,96 +6,121 @@
<rect>
<x>0</x>
<y>0</y>
<width>471</width>
<height>359</height>
<width>438</width>
<height>463</height>
</rect>
</property>
<property name="windowTitle">
<string>Ignored Files Editor</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Global Ignore Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="ignoreHiddenFilesCheckBox">
<property name="text">
<string>Sync hidden files</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Files Ingored by Patterns</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QPushButton" name="removePushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="addPushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3">
<widget class="QTableWidget" name="tableWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>2</number>
</property>
<column>
<property name="text">
<string>Pattern</string>
</property>
</column>
<column>
<property name="text">
<string>Allow Deletion</string>
</property>
</column>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="enabled">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>213</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="descriptionLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="enabled">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>213</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="addPushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="removePushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="descriptionLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="3">
<widget class="QTableWidget" name="tableWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>2</number>
</property>
<column>
<property name="text">
<string>Pattern</string>
</property>
</column>
<column>
<property name="text">
<string>Allow Deletion</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<resources/>

View file

@ -63,6 +63,7 @@ NetworkSettings::NetworkSettings(QWidget *parent) :
connect(_ui->userLineEdit, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
connect(_ui->passwordLineEdit, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
connect(_ui->portSpinBox, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
connect(_ui->authRequiredcheckBox, SIGNAL(toggled(bool)), SLOT(saveProxySettings()));
connect(_ui->uploadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
connect(_ui->noUploadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
@ -79,6 +80,10 @@ NetworkSettings::~NetworkSettings()
delete _ui;
}
QSize NetworkSettings::sizeHint() const {
return QSize(ownCloudGui::settingsDialogSize().width(), QWidget::sizeHint().height());
}
void NetworkSettings::loadProxySettings()
{
// load current proxy settings
@ -105,12 +110,9 @@ void NetworkSettings::loadProxySettings()
if (port == 0)
port = 8080;
_ui->portSpinBox->setValue(port);
if (!cfgFile.proxyUser().isEmpty())
{
_ui->authRequiredcheckBox->setChecked(true);
_ui->userLineEdit->setText(cfgFile.proxyUser());
_ui->passwordLineEdit->setText(cfgFile.proxyPassword());
}
_ui->authRequiredcheckBox->setChecked(cfgFile.proxyNeedsAuth());
_ui->userLineEdit->setText(cfgFile.proxyUser());
_ui->passwordLineEdit->setText(cfgFile.proxyPassword());
}
void NetworkSettings::loadBWLimitSettings()

View file

@ -34,6 +34,7 @@ class NetworkSettings : public QWidget
public:
explicit NetworkSettings(QWidget *parent = 0);
~NetworkSettings();
QSize sizeHint() const;
private slots:
void saveProxySettings();

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>542</width>
<height>391</height>
<height>396</height>
</rect>
</property>
<property name="windowTitle">
@ -103,9 +103,6 @@
</item>
<item>
<widget class="QSpinBox" name="portSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -155,9 +152,6 @@
</property>
<item>
<widget class="QLineEdit" name="userLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
@ -165,9 +159,6 @@
</item>
<item>
<widget class="QLineEdit" name="passwordLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
@ -255,6 +246,9 @@
</item>
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="autoDownloadLimitRadioButton">
<property name="toolTip">
<string>Limit to 3/4 of estimated bandwidth</string>
</property>
<property name="text">
<string>Limit automatically</string>
</property>
@ -284,6 +278,9 @@
</item>
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="autoUploadLimitRadioButton">
<property name="toolTip">
<string>Limit to 3/4 of estimated bandwidth</string>
</property>
<property name="text">
<string>Limit automatically</string>
</property>

View file

@ -132,7 +132,7 @@ void ownCloudGui::setupOverlayIcons()
p.write(aScript.toUtf8());
p.closeWriteChannel();
//p.waitForReadyRead(-1);
p.waitForFinished();
p.waitForFinished(5000);
QByteArray result = p.readAll();
QString resultAsString(result); // if appropriate
qDebug() << "Laod Finder Overlay-Plugin: " << resultAsString << ": " << p.exitCode()

View file

@ -23,6 +23,7 @@
#include <QAction>
#include <QMenu>
#include <QSignalMapper>
#include <QSize>
namespace OCC {
@ -48,6 +49,7 @@ public:
bool checkAccountExists(bool openSettings);
static void raiseDialog(QWidget *raiseWidget);
static QSize settingsDialogSize() { return QSize(800, 500); }
void setupOverlayIcons();
signals:

View file

@ -78,10 +78,8 @@ void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *
void OwncloudSetupWizard::startWizard()
{
FolderMan *folderMan = FolderMan::instance();
AccountPtr account = Account::create();
AccountPtr account = AccountManager::createAccount();
account->setCredentials(CredentialsFactory::create("dummy"));
account->setSslErrorHandler(new SslDialogErrorHandler);
_ocWizard->setAccount(account);
_ocWizard->setOCUrl(account->url().toString());

View file

@ -19,6 +19,7 @@
#include <QLocale>
#include "progressdispatcher.h"
#include "owncloudgui.h"
#include "ui_protocolwidget.h"
@ -42,6 +43,7 @@ class ProtocolWidget : public QWidget
public:
explicit ProtocolWidget(QWidget *parent = 0);
~ProtocolWidget();
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
public slots:
void slotProgressInfo( const QString& folder, const ProgressInfo& progress );

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2015 by Christian Kamm <kamm@incasoftware.de>
*
* 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 "proxyauthdialog.h"
#include "ui_proxyauthdialog.h"
namespace OCC {
ProxyAuthDialog::ProxyAuthDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ProxyAuthDialog)
{
ui->setupUi(this);
}
ProxyAuthDialog::~ProxyAuthDialog()
{
delete ui;
}
void ProxyAuthDialog::setProxyAddress(const QString &address)
{
ui->proxyAddress->setText(address);
}
QString ProxyAuthDialog::username() const
{
return ui->usernameEdit->text();
}
QString ProxyAuthDialog::password() const
{
return ui->passwordEdit->text();
}
void ProxyAuthDialog::reset()
{
ui->usernameEdit->setFocus();
ui->usernameEdit->clear();
ui->passwordEdit->clear();
}
} // namespace OCC

52
src/gui/proxyauthdialog.h Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2015 by Christian Kamm <kamm@incasoftware.de>
*
* 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 OCC_PROXYAUTHDIALOG_H
#define OCC_PROXYAUTHDIALOG_H
#include <QDialog>
namespace OCC {
namespace Ui {
class ProxyAuthDialog;
}
/**
* @brief Ask for username and password for a given proxy.
*
* Used by ProxyAuthHandler.
*/
class ProxyAuthDialog : public QDialog
{
Q_OBJECT
public:
explicit ProxyAuthDialog(QWidget *parent = 0);
~ProxyAuthDialog();
void setProxyAddress(const QString& address);
QString username() const;
QString password() const;
/// Resets the dialog for new credential entry.
void reset();
private:
Ui::ProxyAuthDialog *ui;
};
} // namespace OCC
#endif // OCC_PROXYAUTHDIALOG_H

115
src/gui/proxyauthdialog.ui Normal file
View file

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::ProxyAuthDialog</class>
<widget class="QDialog" name="OCC::ProxyAuthDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>351</width>
<height>141</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Proxy authentication required</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Username:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="usernameEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Proxy:</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>The proxy server needs a username and password.</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Password:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="passwordEdit">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="proxyAddress">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OCC::ProxyAuthDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OCC::ProxyAuthDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,263 @@
/*
* Copyright (C) 2015 by Christian Kamm <kamm@incasoftware.de>
*
* 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 "proxyauthhandler.h"
#include "proxyauthdialog.h"
#include "theme.h"
#include "configfile.h"
#include "account.h"
#include <QApplication>
#include <keychain.h>
using namespace OCC;
ProxyAuthHandler* ProxyAuthHandler::instance()
{
static ProxyAuthHandler inst;
return &inst;
}
ProxyAuthHandler::ProxyAuthHandler()
: _blocked(false)
, _waitingForDialog(0)
, _waitingForKeychain(0)
, _keychainJobRunning(false)
{
_dialog = new ProxyAuthDialog();
_configFile.reset(new ConfigFile);
_settings.reset(new QSettings(_configFile->configFile(), QSettings::IniFormat));
_settings->beginGroup(QLatin1String("Proxy"));
_settings->beginGroup(QLatin1String("Credentials"));
}
ProxyAuthHandler::~ProxyAuthHandler()
{
delete _dialog;
}
void ProxyAuthHandler::handleProxyAuthenticationRequired(
const QNetworkProxy& proxy,
QAuthenticator* authenticator)
{
if (!_dialog) {
return;
}
QString key = QString::fromLatin1("%1:%2").arg(
proxy.hostName(), QString::number(proxy.port()));
// If the proxy server has changed, forget what we know.
if (key != _proxy) {
_proxy = key;
_username.clear();
_password.clear();
_blocked = false;
_gaveCredentialsTo.clear();
// If the user explicitly configured the proxy in the
// network settings, don't ask about it.
if (_configFile->proxyType() == QNetworkProxy::HttpProxy
|| _configFile->proxyType() == QNetworkProxy::Socks5Proxy) {
_blocked = true;
}
}
if (_blocked) {
return;
}
// Find the responsible QNAM if possible.
QNetworkAccessManager* sending_qnam = qobject_cast<QNetworkAccessManager*>(sender());
if (Account* account = qobject_cast<Account*>(sender())) {
sending_qnam = account->networkAccessManager();
}
if (!sending_qnam) {
qDebug() << "Could not get the sending QNAM for" << sender();
}
qDebug() << Q_FUNC_INFO << key << proxy.type();
// If we already had a username but auth still failed,
// invalidate the old credentials! Unfortunately, authenticator->user()
// isn't reliable, so we also invalidate credentials if we previously
// gave presumably valid credentials to the same QNAM.
bool invalidated = false;
if (!_waitingForDialog && !_waitingForKeychain &&
(!authenticator->user().isEmpty()
|| (sending_qnam && _gaveCredentialsTo.contains(sending_qnam)))) {
qDebug() << "invalidating old creds" << key;
_username.clear();
_password.clear();
invalidated = true;
_gaveCredentialsTo.clear();
}
if (_username.isEmpty() || _waitingForKeychain) {
if (invalidated || !getCredsFromKeychain()) {
if (getCredsFromDialog()) {
storeCredsInKeychain();
} else {
// dialog was cancelled, never ask for that proxy again
_blocked = true;
return;
}
}
}
qDebug() << "got creds for" << _proxy;
authenticator->setUser(_username);
authenticator->setPassword(_password);
if (sending_qnam) {
_gaveCredentialsTo.insert(sending_qnam);
connect(sending_qnam, SIGNAL(destroyed(QObject*)),
SLOT(slotSenderDestroyed(QObject*)));
}
}
void ProxyAuthHandler::slotKeychainJobDone()
{
_keychainJobRunning = false;
}
void ProxyAuthHandler::slotSenderDestroyed(QObject* obj)
{
_gaveCredentialsTo.remove(obj);
}
bool ProxyAuthHandler::getCredsFromDialog()
{
// Open the credentials dialog
if (!_waitingForDialog) {
_dialog->reset();
_dialog->setProxyAddress(_proxy);
_dialog->open();
}
// This function can be reentered while the dialog is open.
// If that's the case, continue processing the dialog until
// it's done.
++_waitingForDialog;
while (_dialog && _dialog->isVisible()) {
QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers, 200);
}
--_waitingForDialog;
if (_dialog && _dialog->result() == QDialog::Accepted) {
qDebug() << "got creds for" << _proxy << "from dialog";
_username = _dialog->username();
_password = _dialog->password();
return true;
}
return false;
}
bool ProxyAuthHandler::getCredsFromKeychain()
{
using namespace QKeychain;
if (_waitingForDialog) {
return false;
}
qDebug() << "trying to load" << _proxy;
if (!_waitingForKeychain) {
_username = _settings->value(keychainUsernameKey()).toString();
if (_username.isEmpty()) {
return false;
}
_readPasswordJob.reset(new ReadPasswordJob(Theme::instance()->appName()));
_readPasswordJob->setSettings(_settings.data());
_readPasswordJob->setInsecureFallback(false);
_readPasswordJob->setKey(keychainPasswordKey());
_readPasswordJob->setAutoDelete(false);
connect(_readPasswordJob.data(), SIGNAL(finished(QKeychain::Job*)),
SLOT(slotKeychainJobDone()));
_keychainJobRunning = true;
_readPasswordJob->start();
}
// While we wait for the password job to be done, this code may be reentered.
// This really needs the counter and the flag here, because otherwise we get
// bad behavior when we reenter this code after the flag has been switched
// but before the while loop has finished.
++_waitingForKeychain;
_keychainJobRunning = true;
while (_keychainJobRunning) {
QApplication::processEvents(QEventLoop::AllEvents, 200);
}
--_waitingForKeychain;
if (_readPasswordJob->error() == NoError) {
qDebug() << "got creds for" << _proxy << "from keychain";
_password = _readPasswordJob->textData();
return true;
}
_username.clear();
if (_readPasswordJob->error() != EntryNotFound) {
qDebug() << "ReadPasswordJob failed with" << _readPasswordJob->errorString();
}
return false;
}
void ProxyAuthHandler::storeCredsInKeychain()
{
using namespace QKeychain;
if (_waitingForKeychain) {
return;
}
qDebug() << "storing" << _proxy;
_settings->setValue(keychainUsernameKey(), _username);
WritePasswordJob* job = new WritePasswordJob(Theme::instance()->appName(), this);
job->setSettings(_settings.data());
job->setInsecureFallback(false);
job->setKey(keychainPasswordKey());
job->setTextData(_password);
job->setAutoDelete(false);
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotKeychainJobDone()));
_keychainJobRunning = true;
job->start();
++_waitingForKeychain;
_keychainJobRunning = true;
while (_keychainJobRunning) {
QApplication::processEvents(QEventLoop::AllEvents, 200);
}
--_waitingForKeychain;
job->deleteLater();
if (job->error() != NoError) {
qDebug() << "WritePasswordJob failed with" << job->errorString();
}
}
QString ProxyAuthHandler::keychainUsernameKey() const
{
return QString::fromLatin1("%1/username").arg(_proxy);
}
QString ProxyAuthHandler::keychainPasswordKey() const
{
return QString::fromLatin1("%1/password").arg(_proxy);
}

112
src/gui/proxyauthhandler.h Normal file
View file

@ -0,0 +1,112 @@
/*
* Copyright (C) 2015 by Christian Kamm <kamm@incasoftware.de>
*
* 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.
*/
#pragma once
#include "owncloudgui.h"
#include <QObject>
#include <QString>
#include <QNetworkProxy>
#include <QAuthenticator>
#include <QPointer>
#include <QScopedPointer>
#include <QSettings>
#include <QSet>
namespace QKeychain {
class Job;
class ReadPasswordJob;
}
namespace OCC {
class ConfigFile;
class ProxyAuthDialog;
/**
* @brief Handle proxyAuthenticationRequired signals from our QNetworkAccessManagers.
*
* The main complication here is that the slot needs to return credential information
* synchronously - but running a dialog or getting password data from synchronous
* storage are asynchronous operations. This leads to reentrant calls that are
* fairly complicated to handle.
*/
class ProxyAuthHandler : public QObject
{
Q_OBJECT
public:
static ProxyAuthHandler* instance();
virtual ~ProxyAuthHandler();
public slots:
/// Intended for QNetworkAccessManager::proxyAuthenticationRequired()
void handleProxyAuthenticationRequired(const QNetworkProxy& proxy,
QAuthenticator* authenticator);
private slots:
void slotKeychainJobDone();
void slotSenderDestroyed(QObject*);
private:
ProxyAuthHandler();
/// Runs the ProxyAuthDialog and returns true if new credentials were entered.
bool getCredsFromDialog();
/// Checks the keychain for credentials of the current proxy.
bool getCredsFromKeychain();
/// Stores the current credentials in the keychain.
void storeCredsInKeychain();
QString keychainUsernameKey() const;
QString keychainPasswordKey() const;
/// The hostname:port of the current proxy, used for detetcting switches
/// to a different proxy.
QString _proxy;
QString _username;
QString _password;
/// If the user cancels the credential dialog, blocked will be set to
/// true and we won't bother him again.
bool _blocked;
/// In several instances handleProxyAuthenticationRequired() can be called
/// while it is still running. These counters detect what we're currently
/// waiting for.
int _waitingForDialog;
int _waitingForKeychain;
bool _keychainJobRunning;
QPointer<ProxyAuthDialog> _dialog;
/// The QSettings instance to securely store username/password in the keychain.
QScopedPointer<QSettings> _settings;
/// Pointer to the most-recently-run ReadPasswordJob, needed due to reentrancy.
QScopedPointer<QKeychain::ReadPasswordJob> _readPasswordJob;
/// For checking the proxy config settings.
QScopedPointer<ConfigFile> _configFile;
/// To distinguish between a new QNAM asking for credentials and credentials
/// failing for an existing QNAM, we keep track of the senders of the
/// proxyAuthRequired signal here.
QSet<QObject*> _gaveCredentialsTo;
};
} // namespace OCC

View file

@ -20,6 +20,7 @@
#include "generalsettings.h"
#include "networksettings.h"
#include "accountsettings.h"
#include "creds/abstractcredentials.h"
#include "configfile.h"
#include "progressdispatcher.h"
#include "owncloudgui.h"
@ -113,7 +114,19 @@ void SettingsDialogMac::accountAdded(AccountState *s)
QIcon accountIcon = MacStandardIcon::icon(MacStandardIcon::UserAccounts);
auto accountSettings = new AccountSettings(s, this);
insertPreferencesPanel(0, accountIcon, s->account()->displayName(), accountSettings);
QString userWithoutMailHost = s->account()->credentials()->user();
if (userWithoutMailHost.contains('@')) {
userWithoutMailHost = userWithoutMailHost.left(userWithoutMailHost.lastIndexOf('@'));
}
QString hostWithoutTld = s->account()->url().host();
if (hostWithoutTld.contains('.')) {
hostWithoutTld = hostWithoutTld.left(hostWithoutTld.lastIndexOf('.'));
hostWithoutTld = hostWithoutTld.replace(QLatin1String("www."), QLatin1String(""));
}
QString displayName = tr("%1\n%2").arg(userWithoutMailHost, hostWithoutTld);
insertPreferencesPanel(0, accountIcon, displayName, accountSettings);
connect( accountSettings, &AccountSettings::folderChanged, _gui, &ownCloudGui::slotFoldersChanged);
connect( accountSettings, &AccountSettings::openFolderAlias, _gui, &ownCloudGui::slotFolderOpenAction);

View file

@ -23,6 +23,7 @@
#include "theme.h"
#include "syncresult.h"
#include "configfile.h"
#include "capabilities.h"
#include "QProgressIndicator.h"
#include <QBuffer>
@ -141,6 +142,22 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
_ui->errorLabel->setFrameShape(QFrame::Box);
_ui->errorLabel->setContentsMargins(QMargins(12,12,12,12));
_ui->errorLabel->hide();
// Parse capabilities
// If password is enforced make don't allow users to disable it
if (_account->capabilities().publicLinkEnforcePassword()) {
_ui->checkBox_password->setEnabled(false);
}
// If expiredate is enforced do not allow disable and set max days
if (_account->capabilities().publicLinkEnforceExpireDate()) {
_ui->checkBox_expire->setEnabled(false);
_ui->calendar->setMaximumDate(QDate::currentDate().addDays(
_account->capabilities().publicLinkExpireDateDays()
));
}
}
void ShareDialog::done( int r ) {
@ -247,6 +264,11 @@ void ShareDialog::setPassword(const QString &password)
OcsShareJob *job = new OcsShareJob(verb, url, _account, this);
job->setPostParams(requestParams);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPasswordSet(QVariantMap)));
if (_public_share_id == 0) {
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotCreateShareFetched(QVariantMap)));
}
job->start();
_passwordJobRunning = true;
}
@ -279,6 +301,12 @@ void ShareDialog::getShares()
job->addPassStatusCode(404); // don't report error if share doesn't exist yet
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotSharesFetched(QVariantMap)));
job->start();
if (QFileInfo(_localPath).isFile()) {
ThumbnailJob *job2 = new ThumbnailJob(_sharePath, _account, this);
connect(job2, SIGNAL(jobFinished(int, QByteArray)), SLOT(slotThumbnailFetched(int, QByteArray)));
job2->start();
}
}
void ShareDialog::slotSharesFetched(const QVariantMap &reply)
@ -329,8 +357,11 @@ void ShareDialog::slotSharesFetched(const QVariantMap &reply)
}
QString url;
// From ownCloud server version 8 on, a different share link scheme is used.
if (versionString.contains('.') && versionString.split('.')[0].toInt() >= 8) {
// From ownCloud server 8.2 the url field is always set for public shares
if (data.contains("url")) {
url = data.value("url").toString();
} else if (versionString.contains('.') && versionString.split('.')[0].toInt() >= 8) {
// From ownCloud server version 8 on, a different share link scheme is used.
url = Account::concatUrlPath(_account->url(), QString("index.php/s/%1").arg(data.value("token").toString())).toString();
} else {
QList<QPair<QString, QString>> queryArgs;
@ -430,6 +461,23 @@ void ShareDialog::slotCheckBoxShareLinkClicked()
QList<QPair<QString, QString> > postParams;
postParams.append(qMakePair(QString::fromLatin1("path"), _sharePath));
postParams.append(qMakePair(QString::fromLatin1("shareType"), QString::number(SHARETYPE_PUBLIC)));
/*
* Check the capabilities if the server requires a password for a share
* Ask for it directly
*/
if (_account->capabilities().publicLinkEnforcePassword()) {
_ui->checkBox_password->setChecked(true);
_ui->checkBox_password->setEnabled(false);
_ui->checkBox_password->setText(tr("Public sh&aring requires a password"));
_ui->lineEdit_password->setFocus();
_ui->pushButton_copy->hide();
_ui->widget_shareLink->show();
slotCheckBoxPasswordClicked();
return;
}
OcsShareJob *job = new OcsShareJob("POST", url, _account, this);
job->setPostParams(postParams);
job->addPassStatusCode(403); // "password required" is not an error
@ -731,4 +779,25 @@ bool OcsShareJob::finished()
return true;
}
ThumbnailJob::ThumbnailJob(const QString &path, AccountPtr account, QObject* parent)
: AbstractNetworkJob(account, "", parent)
{
_url = Account::concatUrlPath(account->url(), QLatin1String("index.php/apps/files/api/v1/thumbnail/150/150/") + path);
setIgnoreCredentialFailure(true);
}
void ThumbnailJob::start()
{
qDebug() << Q_FUNC_INFO;
setReply(getRequest(_url));
setupConnections(reply());
AbstractNetworkJob::start();
}
bool ThumbnailJob::finished()
{
emit jobFinished(reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), reply()->readAll());
return true;
}
}

View file

@ -49,6 +49,21 @@ private:
};
class ThumbnailJob : public AbstractNetworkJob {
Q_OBJECT
public:
explicit ThumbnailJob(const QString& path, AccountPtr account, QObject* parent = 0);
public slots:
void start() Q_DECL_OVERRIDE;
signals:
void jobFinished(int statusCode, QByteArray reply);
private slots:
virtual bool finished() Q_DECL_OVERRIDE;
private:
QUrl _url;
};
namespace Ui {
class ShareDialog;
}

View file

@ -420,6 +420,13 @@ void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
const QString folderForPath = shareFolder->path();
const QString remotePath = shareFolder->remotePath() + localFile.right(localFile.count()-folderForPath.count()+1);
// Can't share root folder
if (QDir::cleanPath(remotePath) == "/") {
const QString message = QLatin1String("SHARE:CANNOTSHAREROOT:")+QDir::toNativeSeparators(localFile);
sendMessage(socket, message);
return;
}
SyncJournalFileRecord rec = dbFileRecord_capi(shareFolder, localFile);
bool allowReshare = true; // lets assume the good

View file

@ -194,7 +194,7 @@ void SslButton::updateAccountState(AccountState *accountState)
} else {
setIcon(QIcon(QPixmap(Theme::hidpiFileName(":/client/resources/lock-http.png"))));
setToolTip(tr("This connection is NOT secure as it is not encrypted.\n"));
setMenu(nullptr);
setMenu(0);
}
}

View file

@ -36,13 +36,59 @@ static const char seenVersionC[] = "Updater/seenVersion";
static const char autoUpdateFailedVersionC[] = "Updater/autoUpdateFailedVersion";
static const char autoUpdateAttemptedC[] = "Updater/autoUpdateAttempted";
UpdaterScheduler::UpdaterScheduler(QObject *parent) :
QObject(parent)
{
connect( &_updateCheckTimer, SIGNAL(timeout()),
this, SLOT(slotTimerFired()) );
// Note: the sparkle-updater is not an OCUpdater and thus the dynamic_cast
// returns NULL. Clever detail.
if (OCUpdater *updater = dynamic_cast<OCUpdater*>(Updater::instance())) {
connect(updater, SIGNAL(newUpdateAvailable(QString,QString)),
this, SIGNAL(updaterAnnouncement(QString,QString)) );
}
// at startup, do a check in any case.
QTimer::singleShot(3000, this, SLOT(slotTimerFired()));
ConfigFile cfg;
auto checkInterval = cfg.updateCheckInterval();
_updateCheckTimer.start(checkInterval);
}
void UpdaterScheduler::slotTimerFired()
{
ConfigFile cfg;
// re-set the check interval if it changed in the config file meanwhile
auto checkInterval = cfg.updateCheckInterval();
if( checkInterval != _updateCheckTimer.interval() ) {
_updateCheckTimer.setInterval(checkInterval);
qDebug() << "Setting new update check interval " << checkInterval;
}
// consider the skipUpdateCheck flag in the config.
if( cfg.skipUpdateCheck() ) {
qDebug() << Q_FUNC_INFO << "Skipping update check because of config file";
return;
}
Updater::instance()->backgroundCheckForUpdate();
}
/* ----------------------------------------------------------------- */
OCUpdater::OCUpdater(const QUrl &url, QObject *parent) :
QObject(parent)
, _updateUrl(url)
, _state(Unknown)
, _accessManager(new AccessManager(this))
, _timer(new QTimer(this))
, _timeoutWatchdog(new QTimer(this))
{
}
bool OCUpdater::performUpdate()
@ -50,7 +96,8 @@ bool OCUpdater::performUpdate()
ConfigFile cfg;
QSettings settings(cfg.configFile(), QSettings::IniFormat);
QString updateFile = settings.value(updateAvailableC).toString();
if (!updateFile.isEmpty() && QFile(updateFile).exists()) {
if (!updateFile.isEmpty() && QFile(updateFile).exists()
&& !updateSucceeded() /* Someone might have run the updater manually between restarts */ ) {
const QString name = Theme::instance()->appNameGUI();
if (QMessageBox::information(0, tr("New %1 Update Ready").arg(name),
tr("A new update for %1 is about to be installed. The updater may ask\n"
@ -64,8 +111,24 @@ bool OCUpdater::performUpdate()
void OCUpdater::backgroundCheckForUpdate()
{
// FIXME
checkForUpdate();
int dlState = downloadState();
// do the real update check depending on the internal state of updater.
switch( dlState ) {
case Unknown:
case UpToDate:
case DownloadFailed:
case DownloadTimedOut:
qDebug() << Q_FUNC_INFO << "checking for available update";
checkForUpdate();
break;
case DownloadComplete:
qDebug() << "Update is downloaded, skip new check.";
break;
case UpdateOnlyAvailableThroughSystem:
qDebug() << "Update is only available through system, skip check.";
break;
}
}
QString OCUpdater::statusString() const
@ -101,8 +164,17 @@ int OCUpdater::downloadState() const
void OCUpdater::setDownloadState(DownloadState state)
{
auto oldState = _state;
_state = state;
emit downloadStateChanged();
// show the notification if the download is complete (on every check)
// or once for system based updates.
if( _state == OCUpdater::DownloadComplete ||
(oldState != OCUpdater::UpdateOnlyAvailableThroughSystem
&& _state == OCUpdater::UpdateOnlyAvailableThroughSystem) ) {
emit newUpdateAvailable(tr("Update Check"), statusString() );
}
}
void OCUpdater::slotStartInstaller()
@ -119,8 +191,8 @@ void OCUpdater::slotStartInstaller()
void OCUpdater::checkForUpdate()
{
QNetworkReply *reply = _accessManager->get(QNetworkRequest(_updateUrl));
connect(_timer, SIGNAL(timeout()), this, SLOT(slotTimedOut()));
_timer->start(30*1000);
connect(_timeoutWatchdog, SIGNAL(timeout()), this, SLOT(slotTimedOut()));
_timeoutWatchdog->start(30*1000);
connect(reply, SIGNAL(finished()), this, SLOT(slotVersionInfoArrived()));
setDownloadState(CheckingServer);
@ -143,7 +215,7 @@ bool OCUpdater::updateSucceeded() const
void OCUpdater::slotVersionInfoArrived()
{
_timer->stop();
_timeoutWatchdog->stop();
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if( reply->error() != QNetworkReply::NoError ) {
qDebug() << "Failed to reach version check url: " << reply->errorString();
@ -354,10 +426,11 @@ PassiveUpdateNotifier::PassiveUpdateNotifier(const QUrl &url, QObject *parent)
void PassiveUpdateNotifier::versionInfoArrived(const UpdateInfo &info)
{
qint64 currentVer = Helper::currentVersionToInt();
qint64 remoteVer = Helper::stringVersionToInt(info.version());
if( info.version().isEmpty() ||
Helper::stringVersionToInt(info.version())
>= Helper::currentVersionToInt() )
{
currentVer >= remoteVer ) {
qDebug() << "Client is on latest version!";
setDownloadState(UpToDate);
} else {

View file

@ -18,16 +18,69 @@
#include <QObject>
#include <QUrl>
#include <QTemporaryFile>
#include <QTimer>
#include "updater/updateinfo.h"
#include "updater/updater.h"
class QNetworkAccessManager;
class QNetworkReply;
class QTimer;
namespace OCC {
/**
* @brief Schedule update checks every couple of hours if the client runs.
* @ingroup gui
*
* This class schedules regular update checks. It also checks the config
* if update checks are wanted at all.
*
* To reflect that all platforms have its own update scheme, a little
* complex class design was set up:
*
* For Windows and Linux, the updaters are inherited from OCUpdater, while
* the MacOSX SparkleUpdater directly uses the class Updater. On windows,
* NSISUpdater starts the update if a new version of the client is available.
* On MacOSX, the sparkle framework handles the installation of the new
* version. On Linux, the update capabilities by the underlying linux distro
* is relied on, and thus the PassiveUpdateNotifier just shows a notification
* if there is a new version once at every start of the application.
*
* Simple class diagram of the updater:
*
* +---------------------------+
* +-----+ UpdaterScheduler +-----+
* | +------------+--------------+ |
* v v v
* +------------+ +---------------------+ +----------------+
* |NSISUpdater | |PassiveUpdateNotifier| | SparkleUpdater |
* +-+----------+ +---+-----------------+ +-----+----------+
* | | |
* | v +------------------+
* | +---------------+ v
* +-->| OCUpdater +------+
* +--------+------+ |
* | Updater |
* +-------------+
*/
class UpdaterScheduler : public QObject
{
Q_OBJECT
public:
UpdaterScheduler(QObject *parent);
signals:
void updaterAnnouncement(const QString& title, const QString& msg);
private slots:
void slotTimerFired();
private:
QTimer _updateCheckTimer; /** Timer for the regular update check. */
};
/**
* @brief Class that uses an ownCloud propritary XML format to fetch update information
* @ingroup gui
@ -45,7 +98,6 @@ public:
bool performUpdate();
void checkForUpdate() Q_DECL_OVERRIDE;
void backgroundCheckForUpdate() Q_DECL_OVERRIDE;
QString statusString() const;
int downloadState() const;
@ -53,11 +105,14 @@ public:
signals:
void downloadStateChanged();
void newUpdateAvailable(const QString& header, const QString& message);
public slots:
void slotStartInstaller();
private slots:
void backgroundCheckForUpdate() Q_DECL_OVERRIDE;
void slotOpenUpdateUrl();
void slotVersionInfoArrived();
void slotTimedOut();
@ -71,7 +126,7 @@ private:
QUrl _updateUrl;
int _state;
QNetworkAccessManager *_accessManager;
QTimer *_timer;
QTimer *_timeoutWatchdog; /** Timer to guard the timeout of an individual network request */
UpdateInfo _updateInfo;
};

View file

@ -120,7 +120,7 @@ bool autoUpdaterAllowed()
if ([expectedPath isEqualTo:bundlePath]) {
return true;
}
qWarning() << "ERROR: We are not in /Applications, won't check for update!";
qDebug() << "ERROR: We are not in /Applications, won't check for update!";
return false;
}

View file

@ -96,6 +96,7 @@ Updater *Updater::create()
#else
return new PassiveUpdateNotifier(QUrl(updateBaseUrl));
#endif
}
@ -125,4 +126,9 @@ QString Updater::clientVersion()
return QString::fromLatin1(MIRALL_STRINGIFY(MIRALL_VERSION_FULL));
}
int Updater::downloadState() const
{
return instance()->downloadState();
}
} // namespace OCC

View file

@ -33,6 +33,8 @@ public:
virtual void checkForUpdate() = 0;
virtual void backgroundCheckForUpdate() = 0;
int downloadState() const;
virtual bool handleStartup() = 0;
protected:

View file

@ -35,6 +35,7 @@ endif()
set(libsync_SRCS
account.cpp
bandwidthmanager.cpp
capabilities.cpp
clientproxy.cpp
connectionvalidator.cpp
cookiejar.cpp

View file

@ -157,7 +157,8 @@ void AbstractNetworkJob::slotFinished()
}
if( _reply->error() != QNetworkReply::NoError ) {
qDebug() << Q_FUNC_INFO << _reply->error() << _reply->errorString();
qDebug() << Q_FUNC_INFO << _reply->error() << _reply->errorString()
<< _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (_reply->error() == QNetworkReply::ProxyAuthenticationRequiredError) {
qDebug() << Q_FUNC_INFO << _reply->rawHeader("Proxy-Authenticate");
}

View file

@ -23,6 +23,10 @@
#include "accessmanager.h"
#include "utility.h"
#include <QInputDialog>
#include <QMutexLocker>
#include <QApplication>
namespace OCC
{
@ -36,9 +40,6 @@ AccessManager::AccessManager(QObject* parent)
setProxy(proxy);
#endif
setCookieJar(new CookieJar);
connect(this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(slotProxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
}
void AccessManager::setRawCookie(const QByteArray &rawCookie, const QUrl &url)
@ -72,16 +73,4 @@ QNetworkReply* AccessManager::createRequest(QNetworkAccessManager::Operation op,
return QNetworkAccessManager::createRequest(op, newRequest, outgoingData);
}
void AccessManager::slotProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
{
Q_UNUSED(authenticator);
qDebug() << Q_FUNC_INFO << proxy.type();
// We put in the password here and in ClientProxy in the proxy itself.
if (!proxy.user().isEmpty() || !proxy.password().isEmpty()) {
authenticator->setUser(proxy.user());
authenticator->setPassword(proxy.password());
}
}
} // namespace OCC

View file

@ -38,8 +38,6 @@ public:
protected:
QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0) Q_DECL_OVERRIDE;
protected slots:
void slotProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
};
} // namespace OCC

View file

@ -18,6 +18,7 @@
#include "accessmanager.h"
#include "creds/abstractcredentials.h"
#include "../3rdparty/certificates/p12topem.h"
#include "capabilities.h"
#include <QSettings>
#include <QMutex>
@ -35,6 +36,7 @@ namespace OCC {
Account::Account(QObject *parent)
: QObject(parent)
, _capabilities(QVariantMap())
, _am(0)
, _credentials(0)
, _treatSslErrorsAsFailure(false)
@ -134,7 +136,9 @@ void Account::setCredentials(AbstractCredentials *cred)
_am->setCookieJar(jar);
}
connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
SLOT(slotHandleSslErrors(QNetworkReply*,QList<QSslError>)));
connect(_am, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(_credentials, SIGNAL(fetched()),
SLOT(slotCredentialsFetched()));
}
@ -178,7 +182,9 @@ void Account::resetNetworkAccessManager()
_am = _credentials->getQNAM();
_am->setCookieJar(jar); // takes ownership of the old cookie jar
connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
SLOT(slotHandleSslErrors(QNetworkReply*,QList<QSslError>)));
connect(_am, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
}
QNetworkAccessManager *Account::networkAccessManager()
@ -368,7 +374,7 @@ void Account::setCredentialSetting(const QString &key, const QVariant &value)
}
}
void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> errors)
void Account::slotHandleSslErrors(QNetworkReply *reply , QList<QSslError> errors)
{
NetworkJobTimeoutPauser pauser(reply);
QString out;
@ -434,14 +440,14 @@ void Account::setMigrated(bool mig)
_wasMigrated = mig;
}
QVariantMap Account::capabilities()
const Capabilities &Account::capabilities() const
{
return _capabilities;
}
void Account::setCapabilities(const QVariantMap &caps)
{
_capabilities = caps;
_capabilities = Capabilities(caps);
}
QString Account::serverVersion()

View file

@ -26,6 +26,7 @@
#include <QSharedPointer>
#include "utility.h"
#include <memory>
#include "capabilities.h"
class QSettings;
class QNetworkReply;
@ -51,7 +52,6 @@ public:
virtual bool handleErrors(QList<QSslError>, const QSslConfiguration &conf, QList<QSslCertificate>*, AccountPtr) = 0;
};
/**
* @brief The Account class represents an account on an ownCloud Server
* @ingroup libsync
@ -139,7 +139,7 @@ public:
void setCertificate(const QByteArray certficate = QByteArray(), const QString privateKey = QString());
void setCapabilities(const QVariantMap &caps);
QVariantMap capabilities();
const Capabilities &capabilities() const;
void setServerVersion(const QString &version);
QString serverVersion();
@ -157,8 +157,11 @@ signals:
void invalidCredentials();
void credentialsFetched(AbstractCredentials* credentials);
/// Forwards from QNetworkAccessManager::proxyAuthenticationRequired().
void proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*);
protected Q_SLOTS:
void slotHandleErrors(QNetworkReply*,QList<QSslError>);
void slotHandleSslErrors(QNetworkReply*,QList<QSslError>);
void slotCredentialsFetched();
private:
@ -170,7 +173,7 @@ private:
QUrl _url;
QList<QSslCertificate> _approvedCerts;
QSslConfiguration _sslConfiguration;
QVariantMap _capabilities;
Capabilities _capabilities;
QString _serverVersion;
QScopedPointer<AbstractSslErrorHandler> _sslErrorHandler;
QuotaInfo *_quotaInfo;

View file

@ -0,0 +1,41 @@
/*
* 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 "capabilities.h"
#include <QVariantMap>
namespace OCC {
Capabilities::Capabilities(const QVariantMap &capabilities)
: _capabilities(capabilities)
{
}
bool Capabilities::publicLinkEnforcePassword() const
{
return _capabilities["files_sharing"].toMap()["public"].toMap()["password"].toMap()["enforced"].toBool();
}
bool Capabilities::publicLinkEnforceExpireDate() const
{
return _capabilities["files_sharing"].toMap()["public"].toMap()["expire_date"].toMap()["enforced"].toBool();
}
int Capabilities::publicLinkExpireDateDays() const
{
return _capabilities["files_sharing"].toMap()["public"].toMap()["expire_date"].toMap()["days"].toInt();
}
}

View file

@ -0,0 +1,44 @@
/*
* 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 CAPABILITIES_H
#define CAPABILITIES_H
#include "owncloudlib.h"
#include <QVariantMap>
namespace OCC {
/**
* @brief The Capabilities class represent the capabilities of an ownCloud
* server
* @ingroup libsync
*/
class OWNCLOUDSYNC_EXPORT Capabilities {
public:
Capabilities(const QVariantMap &capabilities);
bool publicLinkEnforcePassword() const;
bool publicLinkEnforceExpireDate() const;
int publicLinkExpireDateDays() const;
private:
QVariantMap _capabilities;
};
}
#endif //CAPABILITIES_H

View file

@ -46,6 +46,7 @@ static const char monoIconsC[] = "monoIcons";
static const char crashReporterC[] = "crashReporter";
static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications";
static const char skipUpdateCheckC[] = "skipUpdateCheck";
static const char updateCheckIntervalC[] = "updateCheckInterval";
static const char geometryC[] = "geometry";
static const char timeoutC[] = "timeout";
static const char transmissionChecksumC[] = "transmissionChecksum";
@ -62,8 +63,8 @@ static const char useDownloadLimitC[] = "BWLimit/useDownloadLimit";
static const char uploadLimitC[] = "BWLimit/uploadLimit";
static const char downloadLimitC[] = "BWLimit/downloadLimit";
static const char newSharedFolderSizeLimitC[] = "newSharedFolderSizeLimit";
static const char useNewSharedFolderSizeLimitC[] = "useNewSharedFolderSizeLimit";
static const char newBigFolderSizeLimitC[] = "newBigFolderSizeLimit";
static const char useNewBigFolderSizeLimitC[] = "useNewBigFolderSizeLimit";
static const char maxLogLinesC[] = "Logging/maxLogLines";
@ -356,7 +357,7 @@ void ConfigFile::setRemotePollInterval(int interval, const QString &connection )
if( connection.isEmpty() ) con = defaultConnection();
if( interval < 5000 ) {
qDebug() << "Remote Poll interval of " << interval << " is below fife seconds.";
qDebug() << "Remote Poll interval of " << interval << " is below five seconds.";
return;
}
QSettings settings(configFile(), QSettings::IniFormat);
@ -383,6 +384,24 @@ quint64 ConfigFile::forceSyncInterval(const QString& connection) const
return interval;
}
int ConfigFile::updateCheckInterval( const QString& connection ) const
{
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings(configFile(), QSettings::IniFormat);
settings.beginGroup( con );
int defaultInterval = 1000*60*60*10; // ten hours
int interval = settings.value( QLatin1String(updateCheckIntervalC), defaultInterval ).toInt();
int minInterval = 1000*60*5;
if( interval < minInterval) {
qDebug() << "Update check interval less than five minutes, setting " << minInterval;
interval = minInterval;
}
return interval;
}
bool ConfigFile::skipUpdateCheck( const QString& connection ) const
{
QString con( connection );
@ -552,17 +571,17 @@ void ConfigFile::setDownloadLimit(int kbytes)
setValue(downloadLimitC, kbytes);
}
QPair<bool, quint64> ConfigFile::newSharedFolderSizeLimit() const
QPair<bool, quint64> ConfigFile::newBigFolderSizeLimit() const
{
qint64 value = getValue(newSharedFolderSizeLimitC, QString(), 100).toLongLong();
bool use = value >= 0 && getValue(useNewSharedFolderSizeLimitC, QString(), true).toBool();
qint64 value = getValue(newBigFolderSizeLimitC, QString(), 500).toLongLong(); // Default to 500MB
bool use = value >= 0 && getValue(useNewBigFolderSizeLimitC, QString(), true).toBool();
return qMakePair(use, quint64(qMax<qint64>(0, value)));
}
void ConfigFile::setNewSharedFolderSizeLimit(bool isChecked, quint64 mbytes)
void ConfigFile::setNewBigFolderSizeLimit(bool isChecked, quint64 mbytes)
{
setValue(newSharedFolderSizeLimitC, mbytes);
setValue(useNewSharedFolderSizeLimitC, isChecked);
setValue(newBigFolderSizeLimitC, mbytes);
setValue(useNewBigFolderSizeLimitC, isChecked);
}
bool ConfigFile::monoIcons() const

View file

@ -54,9 +54,6 @@ public:
bool passwordStorageAllowed(const QString &connection = QString::null );
QString ownCloudVersion() const;
void setOwnCloudVersion( const QString& );
// max count of lines in the log window
int maxLogLines() const;
void setMaxLogLines(int);
@ -100,8 +97,8 @@ public:
void setUploadLimit(int kbytes);
void setDownloadLimit(int kbytes);
/** [checked, size in MB] **/
QPair<bool, quint64> newSharedFolderSizeLimit() const;
void setNewSharedFolderSizeLimit(bool isChecked, quint64 mbytes);
QPair<bool, quint64> newBigFolderSizeLimit() const;
void setNewBigFolderSizeLimit(bool isChecked, quint64 mbytes);
static bool setConfDir(const QString &value);
@ -119,13 +116,12 @@ public:
void saveGeometry(QWidget *w);
void restoreGeometry(QWidget *w);
// installer
// how often the check about new versions runs, default two hours
int updateCheckInterval( const QString& connection = QString() ) const;
bool skipUpdateCheck( const QString& connection = QString() ) const;
void setSkipUpdateCheck( bool, const QString& );
QString lastVersion() const;
void setLastVersion(const QString &version);
void saveGeometryHeader(QHeaderView *header);
void restoreGeometryHeader(QHeaderView *header);

View file

@ -284,8 +284,15 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
// NOT persisted into the new account.
} else {
// interactive password dialog starts here
QString hint;
if (job->error() != EntryNotFound) {
hint = tr("Reading from keychain failed with error: '%1'").arg(
job->errorString());
}
bool ok;
QString pwd = queryPassword(&ok);
QString pwd = queryPassword(&ok, hint);
_fetchJobInProgress = false;
if (ok) {
_password = pwd;

View file

@ -49,7 +49,7 @@ public:
void persist() Q_DECL_OVERRIDE;
QString user() const Q_DECL_OVERRIDE;
QString password() const;
virtual QString queryPassword(bool *ok) = 0;
virtual QString queryPassword(bool *ok, const QString& hint) = 0;
void invalidateToken() Q_DECL_OVERRIDE;
QString fetchUser();
virtual bool sslIsTrusted() { return false; }

View file

@ -62,14 +62,14 @@ int DiscoveryJob::isInSelectiveSyncBlackListCallback(void *data, const char *pat
return static_cast<DiscoveryJob*>(data)->isInSelectiveSyncBlackList(QString::fromUtf8(path));
}
bool DiscoveryJob::checkSelectiveSyncNewShare(const QString& path)
bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString& path)
{
// If this path or the parent is in the white list, then we do not block this file
if (findPathInList(_selectiveSyncWhiteList, path)) {
return false;
}
if (_newSharedFolderSizeLimit < 0) {
if (_newBigFolderSizeLimit < 0) {
// no limit, everything is allowed;
return false;
}
@ -83,10 +83,10 @@ bool DiscoveryJob::checkSelectiveSyncNewShare(const QString& path)
_vioWaitCondition.wait(&_vioMutex);
}
auto limit = _newSharedFolderSizeLimit;
auto limit = _newBigFolderSizeLimit;
if (result > limit) {
// we tell the UI there is a new folder
emit newSharedFolder(path);
emit newBigFolder(path);
return true;
} else {
// it is not too big, put it in the white list (so we will not do more query for the children)
@ -100,9 +100,9 @@ bool DiscoveryJob::checkSelectiveSyncNewShare(const QString& path)
}
}
int DiscoveryJob::checkSelectiveSyncNewShareCallback(void *data, const char *path)
int DiscoveryJob::checkSelectiveSyncNewFolderCallback(void *data, const char *path)
{
return static_cast<DiscoveryJob*>(data)->checkSelectiveSyncNewShare(QString::fromUtf8(path));
return static_cast<DiscoveryJob*>(data)->checkSelectiveSyncNewFolder(QString::fromUtf8(path));
}
@ -311,6 +311,9 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file,QMap
if (!file_stat->etag || strlen(file_stat->etag) == 0) {
qDebug() << "WARNING: etag of" << file_stat->name << "is" << file_stat->etag << " This must not happen.";
}
if( file.startsWith(QChar('.')) ) {
file_stat->flags = CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
//qDebug() << "!!!!" << file_stat << file_stat->name << file_stat->file_id << map.count();
_results.append(file_stat);
}
@ -591,7 +594,7 @@ void DiscoveryJob::start() {
_csync_ctx->callbacks.update_callback_userdata = this;
_csync_ctx->callbacks.update_callback = update_job_update_callback;
_csync_ctx->callbacks.checkSelectiveSyncBlackListHook = isInSelectiveSyncBlackListCallback;
_csync_ctx->callbacks.checkSelectiveSyncNewShareHook = checkSelectiveSyncNewShareCallback;
_csync_ctx->callbacks.checkSelectiveSyncNewFolderHook = checkSelectiveSyncNewFolderCallback;
_csync_ctx->callbacks.remote_opendir_hook = remote_vio_opendir_hook;
_csync_ctx->callbacks.remote_readdir_hook = remote_vio_readdir_hook;
@ -604,7 +607,7 @@ void DiscoveryJob::start() {
_lastUpdateProgressCallbackCall.invalidate();
int ret = csync_update(_csync_ctx);
_csync_ctx->callbacks.checkSelectiveSyncNewShareHook = 0;
_csync_ctx->callbacks.checkSelectiveSyncNewFolderHook = 0;
_csync_ctx->callbacks.checkSelectiveSyncBlackListHook = 0;
_csync_ctx->callbacks.update_callback = 0;
_csync_ctx->callbacks.update_callback_userdata = 0;

View file

@ -161,8 +161,8 @@ class DiscoveryJob : public QObject {
*/
bool isInSelectiveSyncBlackList(const QString &path) const;
static int isInSelectiveSyncBlackListCallback(void *, const char *);
bool checkSelectiveSyncNewShare(const QString &path);
static int checkSelectiveSyncNewShareCallback(void*, const char*);
bool checkSelectiveSyncNewFolder(const QString &path);
static int checkSelectiveSyncNewFolderCallback(void*, const char*);
// Just for progress
static void update_job_update_callback (bool local,
@ -182,7 +182,7 @@ class DiscoveryJob : public QObject {
public:
explicit DiscoveryJob(CSYNC *ctx, QObject* parent = 0)
: QObject(parent), _csync_ctx(ctx), _newSharedFolderSizeLimit(-1) {
: QObject(parent), _csync_ctx(ctx), _newBigFolderSizeLimit(-1) {
// We need to forward the log property as csync uses thread local
// and updates run in another thread
_log_callback = csync_get_log_callback();
@ -192,7 +192,7 @@ public:
QStringList _selectiveSyncBlackList;
QStringList _selectiveSyncWhiteList;
qint64 _newSharedFolderSizeLimit;
qint64 _newBigFolderSizeLimit;
Q_INVOKABLE void start();
signals:
void finished(int result);
@ -202,8 +202,8 @@ signals:
void doOpendirSignal(QString url, DiscoveryDirectoryResult*);
void doGetSizeSignal(const QString &path, qint64 *result);
// A new shared folder was discovered and was not synced because of the confirmation feature
void newSharedFolder(const QString &folder);
// A new folder was discovered and was not synced because of the confirmation feature
void newBigFolder(const QString &folder);
};
}

View file

@ -105,6 +105,9 @@ void FileSystem::setFileHidden(const QString& filename, bool hidden)
SetFileAttributesW((wchar_t*)fName.utf16(), dwAttrs & ~FILE_ATTRIBUTE_HIDDEN );
}
}
#else
Q_UNUSED(filename);
Q_UNUSED(hidden);
#endif
}

View file

@ -396,7 +396,8 @@ bool CheckServerJob::finished()
#if QT_VERSION > QT_VERSION_CHECK(5, 2, 0)
if (reply()->request().url().scheme() == QLatin1String("https")
&& reply()->sslConfiguration().sessionTicket().isEmpty()) {
&& reply()->sslConfiguration().sessionTicket().isEmpty()
&& reply()->error() == QNetworkReply::NoError) {
qDebug() << "No SSL session identifier / session ticket is used, this might impact sync performance negatively.";
}
#endif

View file

@ -286,6 +286,7 @@ void GETFileJob::slotReadyRead()
void GETFileJob::slotTimeout()
{
qDebug() << "Timeout" << reply()->request().url();
_errorString = tr("Connection Timeout");
_errorStatus = SyncFileItem::FatalError;
reply()->abort();
@ -402,7 +403,10 @@ void PropagateDownloadFileQNAM::slotGetFinished()
qDebug() << Q_FUNC_INFO << job->reply()->request().url() << "FINISHED WITH STATUS"
<< job->reply()->error()
<< (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->reply()->errorString());
<< (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->reply()->errorString())
<< _item->_httpErrorCode
<< _tmpFile.size() << _item->_size << job->resumeStart()
<< job->reply()->rawHeader("Content-Range") << job->reply()->rawHeader("Content-Length");
QNetworkReply::NetworkError err = job->reply()->error();
if (err != QNetworkReply::NoError) {
@ -486,6 +490,15 @@ void PropagateDownloadFileQNAM::slotGetFinished()
const QByteArray sizeHeader("Content-Length");
quint64 bodySize = job->reply()->rawHeader(sizeHeader).toULongLong();
if (!job->reply()->rawHeader(sizeHeader).isEmpty() && _tmpFile.size() > 0 && bodySize == 0) {
// Strange bug with broken webserver or webfirewall https://github.com/owncloud/client/issues/3373#issuecomment-122672322
// This happened when trying to resume a file. The Content-Range header was files, Content-Length was == 0
qDebug() << bodySize << _item->_size << _tmpFile.size() << job->resumeStart();
_tmpFile.remove();
done(SyncFileItem::NormalError, QLatin1String("Broken webserver returning empty content length for non-empty file on resume"));
return;
}
if(bodySize > 0 && bodySize != _tmpFile.size() - job->resumeStart() ) {
qDebug() << bodySize << _tmpFile.size() << job->resumeStart();
_propagator->_anotherSyncNeeded = true;

View file

@ -110,6 +110,7 @@ void PUTFileJob::start() {
}
void PUTFileJob::slotTimeout() {
qDebug() << "Timeout" << reply()->request().url();
_errorString = tr("Connection Timeout");
reply()->abort();
}
@ -453,6 +454,7 @@ void PropagateUploadFileQNAM::startNextChunk()
int sendingChunk = (_currentChunk + _startChunk) % _chunkCount;
// XOR with chunk size to make sure everything goes well if chunk size change between runs
uint transid = _transferId ^ chunkSize();
qDebug() << "Upload chunk" << sendingChunk << "of" << _chunkCount << "transferid(remote)=" << transid;
path += QString("-chunking-%1-%2-%3").arg(transid).arg(_chunkCount).arg(sendingChunk);
headers["OC-Chunked"] = "1";

View file

@ -70,7 +70,7 @@ SyncEngine::SyncEngine(AccountPtr account, CSYNC *ctx, const QString& localPath,
, _hasRemoveFile(false)
, _uploadLimit(0)
, _downloadLimit(0)
, _newSharedFolderSizeLimit(-1)
, _newBigFolderSizeLimit(-1)
, _anotherSyncNeeded(false)
{
qRegisterMetaType<SyncFileItem>("SyncFileItem");
@ -348,8 +348,24 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
if (file->remotePerm && file->remotePerm[0]) {
item->_remotePerm = QByteArray(file->remotePerm);
}
item->_should_update_metadata = item->_should_update_metadata || file->should_update_metadata;
/* The flag "serverHasIgnoredFiles" is true if item in question is a directory
* that has children which are ignored in sync, either because the files are
* matched by an ignore pattern, or because they are hidden.
*
* Only the information about the server side ignored files is stored to the
* database and thus written to the item here. For the local repository its
* generated by the walk through the real file tree by discovery phase.
*
* It needs to go to the sync journal becasue the stat information about remote
* files are often read from database rather than being pulled from remote.
*/
if( remote ) {
item->_serverHasIgnoredFiles = (file->has_ignored_files > 0);
}
// record the seen files to be able to clean the journal later
_seenFiles.insert(item->_file);
if (!renameTarget.isEmpty()) {
@ -376,6 +392,9 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
case CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME:
item->_errorString = tr("Filename is too long.");
break;
case CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN:
item->_errorString = tr("File is ignored because it's hidden.");
break;
case CYSNC_STATUS_FILE_LOCKED_OR_OPEN:
item->_errorString = QLatin1String("File locked"); // don't translate, internal use!
break;
@ -423,7 +442,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
item->_type = SyncFileItem::UnknownType;
}
SyncFileItem::Direction dir;
SyncFileItem::Direction dir = SyncFileItem::None;
int re = 0;
switch(file->instruction) {
@ -638,14 +657,14 @@ void SyncEngine::startSync()
discoveryJob->_selectiveSyncBlackList = selectiveSyncBlackList;
discoveryJob->_selectiveSyncWhiteList =
_journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList);
discoveryJob->_newSharedFolderSizeLimit = _newSharedFolderSizeLimit;
discoveryJob->_newBigFolderSizeLimit = _newBigFolderSizeLimit;
discoveryJob->moveToThread(&_thread);
connect(discoveryJob, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int)));
connect(discoveryJob, SIGNAL(folderDiscovered(bool,QString)),
this, SIGNAL(folderDiscovered(bool,QString)));
connect(discoveryJob, SIGNAL(newSharedFolder(QString)),
this, SIGNAL(newSharedFolder(QString)));
connect(discoveryJob, SIGNAL(newBigFolder(QString)),
this, SIGNAL(newBigFolder(QString)));
// This is used for the DiscoveryJob to be able to request the main thread/

View file

@ -66,10 +66,10 @@ public:
/* Abort the sync. Called from the main thread */
void abort();
/* Set the maximum size a shared folder can have without asking for confirmation
/* Set the maximum size a folder can have without asking for confirmation
* -1 means infinite
*/
void setNewSharedFolderSizeLimit(qint64 limit) { _newSharedFolderSizeLimit = limit; }
void setNewBigFolderSizeLimit(qint64 limit) { _newBigFolderSizeLimit = limit; }
Utility::StopWatch &stopWatch() { return _stopWatch; }
@ -116,8 +116,8 @@ signals:
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
// A new shared folder was discovered and was not synced because of the confirmation feature
void newSharedFolder(const QString &folder);
// A new folder was discovered and was not synced because of the confirmation feature
void newBigFolder(const QString &folder);
private slots:
void slotRootEtagReceived(QString);
@ -206,8 +206,8 @@ private:
int _uploadLimit;
int _downloadLimit;
/* maximum size a shared folder can have without asking for confirmation: -1 means infinite */
qint64 _newSharedFolderSizeLimit;
/* maximum size a folder can have without asking for confirmation: -1 means infinite */
qint64 _newBigFolderSizeLimit;
// hash containing the permissions on the remote directory
QHash<QString, QByteArray> _remotePerms;

View file

@ -64,7 +64,7 @@ public:
};
SyncFileItem() : _type(UnknownType), _direction(None), _isDirectory(false),
_hasBlacklistEntry(false), _status(NoStatus),
_serverHasIgnoredFiles(false), _hasBlacklistEntry(false), _status(NoStatus),
_isRestoration(false), _should_update_metadata(false),
_httpErrorCode(0), _requestDuration(0), _affectedItems(1),
_instruction(CSYNC_INSTRUCTION_NONE), _modtime(0), _size(0), _inode(0)
@ -130,6 +130,7 @@ public:
Type _type BITFIELD(3);
Direction _direction BITFIELD(2);
bool _isDirectory BITFIELD(1);
bool _serverHasIgnoredFiles BITFIELD(1);
/// Whether there's an entry in the blacklist table.
/// Note: that entry may have retries left, so this can be true

View file

@ -346,13 +346,13 @@ bool SyncJournalDb::checkConnect()
}
_getFileRecordQuery.reset(new SqlQuery(_db));
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize FROM "
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM "
"metadata WHERE phash=?1" );
_setFileRecordQuery.reset(new SqlQuery(_db) );
_setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize) "
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13);" );
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote) "
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14);" );
_getDownloadInfoQuery.reset(new SqlQuery(_db) );
_getDownloadInfoQuery->prepare( "SELECT tmpfile, etag, errorcount FROM "
@ -516,6 +516,16 @@ bool SyncJournalDb::updateMetadataTableStructure()
commitInternal("update database structure: add pathlen index");
}
if( columns.indexOf(QLatin1String("ignoredChildrenRemote")) == -1 ) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN ignoredChildrenRemote INT;");
if( !query.exec()) {
sqlFail("updateMetadataTableStructure: add ignoredChildrenRemote column", query);
re = false;
}
commitInternal("update database structure: add ignoredChildrenRemote col");
}
return re;
}
@ -630,6 +640,7 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
_setFileRecordQuery->bindValue(11, fileId );
_setFileRecordQuery->bindValue(12, remotePerm );
_setFileRecordQuery->bindValue(13, record._fileSize );
_setFileRecordQuery->bindValue(14, record._serverHasIgnoredFiles ? 1:0);
if( !_setFileRecordQuery->exec() ) {
qWarning() << "Error SQL statement setFileRecord: " << _setFileRecordQuery->lastQuery() << " :"
@ -640,7 +651,7 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
qDebug() << _setFileRecordQuery->lastQuery() << phash << plen << record._path << record._inode
<< record._mode
<< QString::number(Utility::qDateTimeToTime_t(record._modtime)) << QString::number(record._type)
<< record._etag << record._fileId << record._remotePerm << record._fileSize;
<< record._etag << record._fileId << record._remotePerm << record._fileSize << (record._serverHasIgnoredFiles ? 1:0);
_setFileRecordQuery->reset();
return true;
@ -719,6 +730,7 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord( const QString& filename )
rec._fileId = _getFileRecordQuery->baValue(8);
rec._remotePerm = _getFileRecordQuery->baValue(9);
rec._fileSize = _getFileRecordQuery->int64Value(10);
rec._serverHasIgnoredFiles = (_getFileRecordQuery->intValue(11) > 0);
} else {
QString err = _getFileRecordQuery->error();
qDebug() << "No journal entry found for " << filename;

View file

@ -28,14 +28,14 @@
namespace OCC {
SyncJournalFileRecord::SyncJournalFileRecord()
:_inode(0), _type(0), _fileSize(0), _mode(0)
:_inode(0), _type(0), _fileSize(0), _mode(0), _serverHasIgnoredFiles(false)
{
}
SyncJournalFileRecord::SyncJournalFileRecord(const SyncFileItem &item, const QString &localFileName)
: _path(item._file), _modtime(Utility::qDateTimeFromTime_t(item._modtime)),
_type(item._type), _etag(item._etag), _fileId(item._fileId), _fileSize(item._size),
_remotePerm(item._remotePerm), _mode(0)
_remotePerm(item._remotePerm), _mode(0), _serverHasIgnoredFiles(item._serverHasIgnoredFiles)
{
// use the "old" inode coming with the item for the case where the
// filesystem stat fails. That can happen if the the file was removed

View file

@ -37,15 +37,16 @@ public:
return !_path.isEmpty();
}
QString _path;
quint64 _inode;
QDateTime _modtime;
int _type;
QString _path;
quint64 _inode;
QDateTime _modtime;
int _type;
QByteArray _etag;
QByteArray _fileId;
qint64 _fileSize;
QByteArray _remotePerm;
int _mode;
int _mode;
bool _serverHasIgnoredFiles;
};
bool OWNCLOUDSYNC_EXPORT

View file

@ -11,11 +11,11 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "config.h"
#include "utility.h"
#include "version.h"
#include "config.h"
// Note: This file must compile without QtGui
#include <QCoreApplication>
@ -126,7 +126,7 @@ QString Utility::octetsToString( qint64 octets )
s = QCoreApplication::translate("Utility", "%L1 B");
}
return (value > 9.95) ? s.arg(qRound(value)) : s.arg(value, 0, 'f', 2);
return (value > 9.95) ? s.arg(qRound(value)) : s.arg(value, 0, 'g', 2);
}
// Qtified version of get_platforms() in csync_owncloud.c

View file

@ -18,6 +18,7 @@ list(APPEND FolderWatcher_SRC ../src/gui/folderwatcher_win.cpp)
ENDIF()
IF( APPLE )
list(APPEND FolderWatcher_SRC ../src/gui/folderwatcher_mac.cpp)
list(APPEND FolderWatcher_SRC ../src/gui/socketapisocket_mac.mm)
ENDIF()
owncloud_add_test(FolderWatcher "${FolderWatcher_SRC}")

View file

@ -14,7 +14,7 @@ macro(owncloud_add_test test_class additional_cpp)
qt_wrap_cpp(test${OWNCLOUD_TEST_CLASS_LOWERCASE}.h)
add_executable(${OWNCLOUD_TEST_CLASS}Test test${OWNCLOUD_TEST_CLASS_LOWERCASE}.cpp ${additional_cpp})
qt5_use_modules(${OWNCLOUD_TEST_CLASS}Test Test Sql Xml Network)
qt5_use_modules(${OWNCLOUD_TEST_CLASS}Test Test Sql Xml Network Gui Widgets)
target_link_libraries(${OWNCLOUD_TEST_CLASS}Test
updater

View file

@ -46,6 +46,11 @@ private slots:
QVERIFY(dir2.mkpath("ownCloud2"));
QVERIFY(dir2.mkpath("sub/free"));
QVERIFY(dir2.mkpath("free2/sub"));
{
QFile f(dir.path() + "/sub/file.txt");
f.open(QFile::WriteOnly);
f.write("hello");
}
FolderMan *folderman = FolderMan::instance();
QCOMPARE(folderman, &_fm);
@ -54,11 +59,15 @@ private slots:
// those should be allowed
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/free").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/free2/").isNull());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/free"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/free2/"), QString());
// Not an existing directory -> Ok
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/bliblablu"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/free/bliblablu"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/bliblablu/some/more"), QString());
// Not an existing directory -> Error
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/bliblablu").isNull());
// A file -> Error
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/file.txt").isNull());
// There are folders configured in those folders: -> ERROR
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1").isNull());
@ -85,6 +94,24 @@ private slots:
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link4").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3/folder").isNull());
// test some non existing sub path (error)
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/some/sub/path").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/ownCloud2/blublu").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/folder/g/h").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3/folder/neu_folder").isNull());
// Subfolder of links
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/link1/subfolder").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/link2/free/subfolder").isNull());
// Invalid paths
QVERIFY(!folderman->checkPathValidityForNewFolder("").isNull());
// Should not have the rights
QVERIFY(!folderman->checkPathValidityForNewFolder("/").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder("/usr/bin/somefolder").isNull());
#else
QSKIP("Test not supported with Qt4", SkipSingle);
#endif