mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-25 14:36:01 +03:00
Merge remote-tracking branch 'origin/master' into new_nsis_modules
This commit is contained in:
commit
b8d21bdd51
91 changed files with 1663 additions and 384 deletions
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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@"
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
2
src/3rdparty/qtmacgoodies
vendored
2
src/3rdparty/qtmacgoodies
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 8828099fd05b191cedf4d52f62f2405de6471415
|
||||
Subproject commit 044580c32837edba05a055aabca27245939454eb
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
/**
|
||||
|
|
|
@ -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)];
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ class GeneralSettings : public QWidget
|
|||
public:
|
||||
explicit GeneralSettings(QWidget *parent = 0);
|
||||
~GeneralSettings();
|
||||
QSize sizeHint() const;
|
||||
|
||||
private slots:
|
||||
void saveMiscSettings();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -36,6 +36,8 @@ public:
|
|||
explicit IgnoreListEditor(QWidget *parent = 0);
|
||||
~IgnoreListEditor();
|
||||
|
||||
bool ignoreHiddenFiles();
|
||||
|
||||
private slots:
|
||||
void slotItemSelectionChanged();
|
||||
void slotRemoveCurrentItem();
|
||||
|
|
|
@ -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/>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -34,6 +34,7 @@ class NetworkSettings : public QWidget
|
|||
public:
|
||||
explicit NetworkSettings(QWidget *parent = 0);
|
||||
~NetworkSettings();
|
||||
QSize sizeHint() const;
|
||||
|
||||
private slots:
|
||||
void saveProxySettings();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
53
src/gui/proxyauthdialog.cpp
Normal file
53
src/gui/proxyauthdialog.cpp
Normal 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
52
src/gui/proxyauthdialog.h
Normal 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
115
src/gui/proxyauthdialog.ui
Normal 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>
|
263
src/gui/proxyauthhandler.cpp
Normal file
263
src/gui/proxyauthhandler.cpp
Normal 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
112
src/gui/proxyauthhandler.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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å 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
virtual void checkForUpdate() = 0;
|
||||
virtual void backgroundCheckForUpdate() = 0;
|
||||
|
||||
int downloadState() const;
|
||||
|
||||
virtual bool handleStartup() = 0;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -35,6 +35,7 @@ endif()
|
|||
set(libsync_SRCS
|
||||
account.cpp
|
||||
bandwidthmanager.cpp
|
||||
capabilities.cpp
|
||||
clientproxy.cpp
|
||||
connectionvalidator.cpp
|
||||
cookiejar.cpp
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
41
src/libsync/capabilities.cpp
Normal file
41
src/libsync/capabilities.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
44
src/libsync/capabilities.h
Normal file
44
src/libsync/capabilities.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue