Conflicts:
	src/CMakeLists.txt
	src/cmd/cmd.cpp
	src/gui/folder.cpp
	src/gui/socketapi.cpp
	translations/mirall_ca.ts
	translations/mirall_cs.ts
	translations/mirall_de.ts
	translations/mirall_el.ts
	translations/mirall_en.ts
	translations/mirall_es.ts
	translations/mirall_es_AR.ts
	translations/mirall_et.ts
	translations/mirall_eu.ts
	translations/mirall_fa.ts
	translations/mirall_fi.ts
	translations/mirall_fr.ts
	translations/mirall_gl.ts
	translations/mirall_hu.ts
	translations/mirall_it.ts
	translations/mirall_ja.ts
	translations/mirall_nl.ts
	translations/mirall_pl.ts
	translations/mirall_pt.ts
	translations/mirall_pt_BR.ts
	translations/mirall_ru.ts
	translations/mirall_sk.ts
	translations/mirall_sl.ts
	translations/mirall_sv.ts
	translations/mirall_th.ts
	translations/mirall_tr.ts
	translations/mirall_uk.ts
	translations/mirall_zh_CN.ts
	translations/mirall_zh_TW.ts
This commit is contained in:
Olivier Goffart 2014-09-18 17:08:53 +02:00
Родитель c3d41f0d48 fc36e7eccf
Коммит 50e718b1e7
93 изменённых файлов: 16510 добавлений и 15241 удалений

Просмотреть файл

@ -160,4 +160,5 @@ if(BUILD_OWNCLOUD_OSX_BUNDLE)
configure_file(sync-exclude.lst ${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)
endif()

Просмотреть файл

@ -1,6 +1,7 @@
ChangeLog
=========
version 1.6.2 (release 2014-07-x )
version 1.6.2 (release 2014-07-28 )
* Limit the HTTP buffer size when downloading to limit memory consumption.
* Another small mem leak fixed in HTTP Credentials.
* Fix local file name clash detection for MacOSX.
* Limit maximum wait time to ten seconds in network limiting.

Просмотреть файл

@ -4,6 +4,8 @@
# http://s.sudre.free.fr/Software/Packages/about.html
#
[ "$#" -lt 2 ] && echo "Usage: create_mac_pkg.sh <CMAKE_INSTALL_DIR> <build dir> <installer sign identity>" && exit
# the path of installation must be given as parameter
if [ -z "$1" ]; then
echo "ERROR: Provide the path to CMAKE_INSTALL_DIR to this script as first parameter."
@ -15,13 +17,17 @@ if [ -z "$2" ]; then
exit 1
fi
install_path=$1
build_path=$2
install_path="$1"
build_path="$2"
identity="$3"
prjfile=$build_path/admin/osx/macosx.pkgproj
# The name of the installer package
installer="ownCloud-@MIRALL_VERSION_FULL@@MIRALL_VERSION_SUFFIX@"
installer_file="$installer.pkg"
installer_file_tar="$installer.pkg.tar"
installer_file_tar_bz2="$installer.pkg.tar.bz2"
installer_file_tbz="$installer.pkg.tbz"
# set the installer name to the copied prj config file
/usr/local/bin/packagesutil --file $prjfile set project name "$installer"
@ -39,9 +45,30 @@ else
exit 3
fi
# FIXME: Sign the finished package.
# See http://s.sudre.free.fr/Software/documentation/Packages/en/Project_Configuration.html#5
# certname=gdbsign
# productsign --cert $certname admin/$installer ./$installer
# Sign the finished package if desired.
if [ ! -z "$identity" ]; then
echo "Will try to sign the installer"
pushd $install_path
productsign --sign "$identity" "$installer_file" "$installer_file.new"
mv "$installer_file".new $installer_file
popd
else
echo "No certificate given, will not sign the pkg"
fi
# FIXME: OEMs?
# they will need to do their own signing..
# Sparkle wants a tbz, it cannot install raw pkg
cd $install_path
tar cf $installer_file_tar $installer_file
bzip2 -9 $installer_file_tar
mv $installer_file_tar_bz2 $installer_file_tbz
rc=$?
if [ $rc == 0 ]; then
echo "Successfully created $installer_file"
else
echo "Failed to create $installer_file"
exit 3
fi

33
admin/osx/sign_app.sh Executable file
Просмотреть файл

@ -0,0 +1,33 @@
#!/bin/sh -x
[ "$#" -lt 2 ] && echo "Usage: sign_app.sh <app> <identity>" && exit
src_app="$1"
identity="$2"
QT_FMWKS=`basename ${TMP_APP}/Contents/Frameworks`/Qt*
QT_FMWK_VERSION="5"
fix_frameworks() {
TMP_APP=$1
QT_FMWK_PATH=$2
QT_FMWKS=$3/Qt*.framework
echo "Patching Qt frameworks..."
for FMWK in $QT_FMWKS; do
FMWK_NAME=`basename -s .framework $FMWK`
FMWK=`basename $FMWK`
FMWK_PATH="${TMP_APP}/Contents/Frameworks/${FMWK}"
mkdir -p "${FMWK_PATH}/Versions/${QT_FMWK_VERSION}/Resources/"
cp -avf "${QT_FMWK_PATH}/${FMWK}/Contents/Info.plist" "${FMWK_PATH}/Versions/${QT_FMWK_VERSION}/Resources"
(cd "${FMWK_PATH}" && ln -sf "Versions/${QT_FMWK_VERSION}/Resources" "Resources")
perl -pi -e "s/${FMWK_NAME}_debug/${FMWK_NAME}/" "${FMWK_PATH}/Resources/Info.plist"
done
}
fix_frameworks "$src_app" `qmake -query QT_INSTALL_LIBS` "$src_app"/Contents/Frameworks
codesign -s "$identity" --force --verify --verbose --deep "$src_app"
# Just for our debug purposes:
spctl -a -t exec -vv $src_app
codesign -dv $src_app

Просмотреть файл

@ -94,6 +94,7 @@ enum csync_status_codes_e {
CSYNC_STATUS_ABORTED,
/* Codes for file individual status: */
CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK,
CSYNC_STATUS_INDIVIDUAL_IS_HARDLINK,
CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST,
CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS,
CYSNC_STATUS_FILE_LOCKED_OR_OPEN

Просмотреть файл

@ -40,7 +40,10 @@
#define CSYNC_LOG_CATEGORY_NAME "csync.exclude"
#include "csync_log.h"
static int _csync_exclude_add(c_strlist_t **inList, const char *string) {
#ifndef NDEBUG
static
#endif
int _csync_exclude_add(c_strlist_t **inList, const char *string) {
c_strlist_t *list;
if (*inList == NULL) {
@ -199,6 +202,20 @@ CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path
goto out;
}
#ifdef _WIN32
// Windows cannot sync files ending in spaces (#2176). It also cannot
// distinguish files ending in '.' from files without an ending,
// as '.' is a separator that is not stored internally, so let's
// not allow to sync those to avoid file loss/ambiguities (#416)
size_t blen = strlen(bname);
if (blen > 1 && (bname[blen-1]== ' ' || bname[blen-1]== '.' )) {
match = CSYNC_FILE_EXCLUDE_INVALID_CHAR;
SAFE_FREE(bname);
SAFE_FREE(dname);
goto out;
}
#endif
rc = csync_fnmatch(".owncloudsync.log*", bname, 0);
if (rc == 0) {
match = CSYNC_FILE_SILENTLY_EXCLUDED;

Просмотреть файл

@ -29,6 +29,11 @@ enum csync_exclude_type_e {
CSYNC_FILE_EXCLUDE_INVALID_CHAR
};
typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;
#ifdef NDEBUG
int _csync_exclude_add(c_strlist_t **inList, const char *string);
#endif
/**
* @brief Load exclude list
*

Просмотреть файл

@ -185,6 +185,7 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
if (type == CSYNC_FTW_TYPE_FILE ) {
if( fs->nlink > 1) {
st->instruction = CSYNC_INSTRUCTION_IGNORE;
st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_HARDLINK;
goto out;
}
@ -229,14 +230,16 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
if(tmp && tmp->phash == h ) { /* there is an entry in the database */
/* we have an update! */
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Database entry found, compare: %" PRId64 " <-> %" PRId64 ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64,
((int64_t) fs->mtime), ((int64_t) tmp->modtime), fs->etag, tmp->etag, (uint64_t) fs->inode, (uint64_t) tmp->inode);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Database entry found, compare: %" PRId64 " <-> %" PRId64 ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64 ", size: %" PRId64 " <-> %" PRId64,
((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);
if( !fs->etag) {
st->instruction = CSYNC_INSTRUCTION_EVAL;
goto out;
}
if((ctx->current == REMOTE_REPLICA && !c_streq(fs->etag, tmp->etag ))
|| (ctx->current == LOCAL_REPLICA && (fs->mtime != tmp->modtime
// zero size in statedb can happen during migration
|| (tmp->size != 0 && fs->size != tmp->size)
#if 0
|| fs->inode != tmp->inode
#endif
@ -432,11 +435,19 @@ int csync_walker(CSYNC *ctx, const char *file, const csync_vio_file_stat_t *fs,
switch (flag) {
case CSYNC_FTW_FLAG_FILE:
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s]", file, fs->file_id);
if (ctx->current == REMOTE_REPLICA) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=%" PRIu64 "]", file, fs->file_id, fs->size);
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [inode=%" PRIu64 " size=%" PRIu64 "]", file, fs->inode, fs->size);
}
type = CSYNC_FTW_TYPE_FILE;
break;
case CSYNC_FTW_FLAG_DIR: /* enter directory */
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "directory: %s [file_id=%s]", file, fs->file_id);
if (ctx->current == REMOTE_REPLICA) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "directory: %s [file_id=%s]", file, fs->file_id);
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "directory: %s [inode=%" PRIu64 "]", file, fs->inode);
}
type = CSYNC_FTW_TYPE_DIR;
break;
case CSYNC_FTW_FLAG_NSTAT: /* not statable file */

Просмотреть файл

@ -237,7 +237,7 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
/* printf("Index: %I64i\n", FileIndex.QuadPart); */
buf->inode = FileIndex.QuadPart;
buf->size = (fileInfo.nFileSizeHigh * (int64_t)(MAXDWORD+1)) + fileInfo.nFileSizeLow;
buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + fileInfo.nFileSizeLow;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
/* Get the file time with a win32 call rather than through stat. See

Просмотреть файл

@ -141,6 +141,38 @@ static void check_csync_excluded(void **state)
}
static void check_csync_pathes(void **state)
{
CSYNC *csync = *state;
int rc;
_csync_exclude_add( &(csync->excludes), "/exclude" );
/* Check toplevel dir, the pattern only works for toplevel dir. */
rc = csync_excluded(csync, "/exclude", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/foo/exclude", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* check for a file called exclude. Must still work */
rc = csync_excluded(csync, "/exclude", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/foo/exclude", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* Add an exclude for directories only: excl/ */
_csync_exclude_add( &(csync->excludes), "excl/" );
rc = csync_excluded(csync, "/excl", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "meep/excl", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/excl", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
}
int torture_run_tests(void)
{
@ -148,6 +180,7 @@ int torture_run_tests(void)
unit_test_setup_teardown(check_csync_exclude_add, setup, teardown),
unit_test_setup_teardown(check_csync_exclude_load, setup, teardown),
unit_test_setup_teardown(check_csync_excluded, setup_init, teardown),
unit_test_setup_teardown(check_csync_pathes, setup_init, teardown),
};
return run_tests(tests);

Просмотреть файл

@ -1,12 +1,7 @@
t1 - an integration test script for csync syncing to ownCloud.
Note: This test script uses perl HTTP::DAV. This package needs to
be in version 0.46 at least. Many distros deliver older versions.
A working version is part of the github checkout.
Note: This test script uses perl HTTP::DAV. This package needs to
be in version 0.46 at least. Many distros deliver older versions.
Update than.
be in version 0.47 at least. Many distros deliver older versions.
t1 uses a perl WebDAV client lib to sync to an existing instance of
ownCloud. For that, various files are copied around, synced and the
@ -20,10 +15,6 @@ First, configure the script. For that, create a file t1.cfg. There
is t1.cfg.in as an example. Yeah, this test script is not secure,
make sure to run it with a weak account and in a save environment.
Second, unpack the test file collection with
tar xf testfiles.tar.xz
in the directory where the tarball can be found.
To start the script, call ./t1.pl on the commandline. A lot of
output is generated. If the script does not fail, everything works.

Просмотреть файл

@ -1,8 +1,8 @@
user => "joe",
passwd => "secret",
url => "http://localhost/ocm/remote.php/webdav/",
ld_libpath => "/home/joe/owncloud/csync/csync-build/modules",
csync => "/home/joe/owncloud/csync/csync-build/client/csync",
ld_libpath => "/home/joe/owncloud/mirall-install/lib",
csync => "/home/joe/owncloud/mirall-install/bin/owncloudcmd",
ocs_url => "http://localhost/owncloud/ocs/v1.php/",
share_user => "jenny",
share_passwd => "also_secret"

Просмотреть файл

@ -176,8 +176,6 @@ system("sqlite3 " . localDir().'.csync_journal.db .dump');
#new directory should be uploaded
system("mv " . localDir().'readonlyDirectory_PERM_M_/subdir_PERM_CK_ ' . localDir().'normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_' );
# two syncs may be necessary for now: https://github.com/owncloud/mirall/issues/2038
csync();
csync();
system("sqlite3 " . localDir().'.csync_journal.db .dump');
assertCsyncJournalOk(localDir());
@ -209,8 +207,6 @@ system("mv " . localDir().'readonlyDirectory_PERM_M_/subdir_PERM_CK_ ' . localDi
#2. move a directory from read to read only (move the directory from previous step)
system("mv " . localDir().'normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_ ' . localDir().'readonlyDirectory_PERM_M_/moved_PERM_CK_' );
# two syncs may be necessary for now: https://github.com/owncloud/mirall/issues/2038
csync();
csync();
assertCsyncJournalOk(localDir());

Просмотреть файл

@ -1,12 +1,12 @@
The ownCloud Client reads a configuration file. You can locate this configuration files as follows:
- On Linux distributions:
On Linux distributions:
``$HOME/.local/share/data/ownCloud/owncloud.cfg``
- In Microsoft Windows systems:
On Microsoft Windows systems:
``%LOCALAPPDATA%\ownCloud\owncloud.cfg``
- In MAC OS X systems:
On MAC OS X systems:
``$HOME/Library/Application Support/ownCloud``

Просмотреть файл

@ -29,12 +29,24 @@ the server URL.
OPTIONS
=======
``--confdir`` `PATH`
Specifies the configuration directory where `csync.conf` is located.
``--user``, ``-u`` ``[user]``
Use ``user`` as the login name.
``--silent``
``--password``, ``-p`` ``[password]``
Use ``password`` as the password.
``-n``
Use ``netrc (5)`` for login.
``--non-interactive``
Do not prompt for questions.
``--silent``, ``--s``
Inhibits verbose log output.
``--trust``
Trust any SSL certificate, including invalid ones.
``--httpproxy http://[user@pass:]<server>:<port>``
Uses ``server`` as HTTP proxy.
@ -48,6 +60,8 @@ the command line would be::
$HOME/media/music \
https://server/owncloud/remote.php/webdav/Music
``owncloudcmd`` will enquire user name and password, unless they have
been specified on the command line or ``-n`` (see `netrc(5)`) has been passed.
Using the legacy scheme, it would be::

Просмотреть файл

@ -22,13 +22,25 @@ the server URL.
Other comand line switches supported by owncloudcmd include the following:
- ``--silent``
Supresses verbose log output.
``--user``, ``-u`` ``[user]``
Use ``user`` as the login name.
- ``--confdir`` `PATH`
Fetches or stores configuration in the specified configuration directory.
``--password``, ``-p`` ``[password]``
Use ``password`` as the password.
- ``--httpproxy http://[user@pass:]<server>:<port>``
``-n``
Use ``netrc (5)`` for login.
``--non-interactive``
Do not prompt for questions.
``--silent``, ``-s``
Inhibits verbose log output.
``--trust``
Trust any SSL certificate, including invalid ones.
``--httpproxy http://[user@pass:]<server>:<port>``
Uses the specified ``server`` as the HTTP proxy.
Credential Handling
@ -41,18 +53,19 @@ setting with the usual URL pattern. For example::
https://user:secret@192.168.178.2/remote.php/webdav
Example
~~~~~~~
To synchronize the ownCloud directory ``Music`` to the local directory
``media/music`, through a proxy listening on port ``8080``, and on a gateway
``media/music``, through a proxy listening on port ``8080``, and on a gateway
machine using IP address ``192.168.178.1``, the command line would be::
$ owncloudcmd --httpproxy http://192.168.178.1:8080 \
$HOME/media/music \
https://server/owncloud/remote.php/webdav/Music
``owncloudcmd`` will enquire user name and password, unless they have
been specified on the command line or ``-n`` has been passed.
Using the legacy scheme, the command line would be::

Просмотреть файл

@ -39,6 +39,9 @@
- (void)setIcons:(NSDictionary*)iconDictionary filterByFolder:(NSString*)filterFolder;
- (void)setResultForPath:(NSString*)path result:(NSString*)result;
- (void)clearFileNameCacheForPath:(NSString*)path;
- (void)reFetchFileNameCacheForPath:(NSString*)path;
- (void)repaintAllWindows;
- (void)loadIconResourcePath:(NSString*)path;
@end

Просмотреть файл

@ -32,19 +32,6 @@ static ContentManager* sharedInstance = nil;
_fileNamesCache = [[NSMutableDictionary alloc] init];
_fileIconsEnabled = TRUE;
_hasChangedContent = TRUE;
NSString *base = @"/Applications/owncloud.app/Contents/Resources/icons/";
_icnOk = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"ok.icns"]];
_icnSync = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"sync.icns"]];
_icnWarn = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"warning.icns"]];
_icnErr = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"error.icns"]];
_icnOkSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"ok_swm.icns"]];
_icnSyncSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"sync_swm.icns"]];
_icnWarnSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"warning_swm.icns"]];
_icnErrSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"error_swm.icns"]];
NSLog(@"Icon ok identifier: %d", [_icnOk intValue]);
}
return self;
@ -72,6 +59,22 @@ static ContentManager* sharedInstance = nil;
return sharedInstance;
}
- (void)loadIconResourcePath:(NSString*)path
{
NSString *base = path;
_icnOk = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"ok.icns"]];
_icnSync = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"sync.icns"]];
_icnWarn = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"warning.icns"]];
_icnErr = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"error.icns"]];
_icnOkSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"ok_swm.icns"]];
_icnSyncSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"sync_swm.icns"]];
_icnWarnSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"warning_swm.icns"]];
_icnErrSwm = [[IconCache sharedInstance] registerIcon:[base stringByAppendingString:@"error_swm.icns"]];
NSLog(@"Icon ok identifier: %d from %@", [_icnOk intValue], [base stringByAppendingString:@"ok.icns"]);
}
- (void)enableFileIcons:(BOOL)enable
{
_fileIconsEnabled = enable;
@ -81,6 +84,10 @@ static ContentManager* sharedInstance = nil;
- (void)setResultForPath:(NSString*)path result:(NSString*)result
{
if (_icnOk == nil) {
// no icon resource path registered yet
return;
}
NSNumber *res;
res = [NSNumber numberWithInt:0];
@ -131,6 +138,10 @@ static ContentManager* sharedInstance = nil;
return res;
}
NSString* normalizedPath = [path decomposedStringWithCanonicalMapping];
if (![[RequestManager sharedInstance] isRegisteredPath:normalizedPath]) {
return [NSNumber numberWithInt:0];
}
NSNumber* result = [_fileNamesCache objectForKey:normalizedPath];
// NSLog(@"XXXXXXX Asking for icon for path %@ = %d",path, [result intValue]);
@ -179,6 +190,20 @@ static ContentManager* sharedInstance = nil;
}
}
- (void)reFetchFileNameCacheForPath:(NSString*)path
{
NSLog(@"%@", NSStringFromSelector(_cmd));
for (id p in [_fileNamesCache keyEnumerator]) {
if ( path && [p hasPrefix:path] ) {
NSNumber *askState = [[RequestManager sharedInstance] askForIcon:p isDirectory:false]; // FIXME
//[_fileNamesCache setObject:askState forKey:p];
NSLog(@"%@ %@", NSStringFromSelector(_cmd), p);
}
}
}
- (void)removeAllIcons
{
[_fileNamesCache removeAllObjects];

Просмотреть файл

@ -34,7 +34,7 @@
NSNumber* imageIndex = [[ContentManager sharedInstance] iconByPath:[url path] isDirectory:isDir];
NSLog(@"1 The icon index is %d", [imageIndex intValue]);
//NSLog(@"1 The icon index is %d", [imageIndex intValue]);
if ([imageIndex intValue] > 0)
{
NSImage* image = [[IconCache sharedInstance] getIcon:imageIndex];
@ -145,7 +145,7 @@
}
NSNumber* imageIndex = [[ContentManager sharedInstance] iconByPath:[url path] isDirectory:isDir];
NSLog(@"3 The icon index is %d", [imageIndex intValue]);
//NSLog(@"3 The icon index is %d", [imageIndex intValue]);
if ([imageIndex intValue] > 0)
{

Просмотреть файл

@ -136,6 +136,7 @@ static RequestManager* sharedInstance = nil;
[contentman setResultForPath:[chunks objectAtIndex:2] result:[chunks objectAtIndex:1]];
} else if( [[chunks objectAtIndex:0] isEqualToString:@"UPDATE_VIEW"] ) {
NSString *path = [chunks objectAtIndex:1];
[contentman reFetchFileNameCacheForPath:path];
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_PATH"] ) {
NSNumber *one = [NSNumber numberWithInt:1];
NSString *path = [chunks objectAtIndex:1];
@ -148,6 +149,9 @@ static RequestManager* sharedInstance = nil;
[_registeredPathes removeObjectForKey:path];
[contentman repaintAllWindows];
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"ICON_PATH"] ) {
NSString *path = [chunks objectAtIndex:1];
[[ContentManager sharedInstance] loadIconResourcePath:path];
} else {
NSLog(@"Unknown command %@", [chunks objectAtIndex:0]);
}
@ -178,6 +182,10 @@ static RequestManager* sharedInstance = nil;
[self askOnSocket:path];
}
}
ContentManager *contentman = [ContentManager sharedInstance];
[contentman clearFileNameCacheForPath:nil];
[contentman repaintAllWindows];
// Read for the UPDATE_VIEW requests
NSData* stop = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
@ -202,6 +210,7 @@ static RequestManager* sharedInstance = nil;
// clear the caches in conent manager
ContentManager *contentman = [ContentManager sharedInstance];
[contentman clearFileNameCacheForPath:nil];
[contentman repaintAllWindows];
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(start) userInfo:nil repeats:NO];

Просмотреть файл

@ -7,7 +7,7 @@ import socket
from gi.repository import GObject, Nautilus
class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
nautilusVFSFile_table = {}
registered_paths = {}
remainder = ''
@ -17,81 +17,80 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
def __init__(self):
self.connectToOwnCloud
if not self.connected:
# try again in 5 seconds - attention, logic inverted!
GObject.timeout_add(5000, self.connectToOwnCloud)
def port(self):
return 34001 # Fixme, read from config file.
def connectToOwnCloud(self):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(("localhost", self.port()))
self.sock.settimeout(5)
self.connected = True
self.watch_id = GObject.io_add_watch(self.sock, GObject.IO_IN, self.handle_notify)
except:
print "Connect could not be established, try again later!"
self.sock.close()
return not self.connected
def sendCommand(self, cmd):
if self.connected:
try:
self.sock.send(cmd)
except:
print "Sending failed."
GObject.source_remove( self.watch_id )
self.connected = False
# try again in 5 seconds - attention, logic inverted!
GObject.timeout_add(5000, self.connectToOwnCloud)
def find_item_for_file( self, path ):
if path in self.nautilusVFSFile_table:
return self.nautilusVFSFile_table[path]
else:
return None
def port(self):
return 34001 # Fixme, read from config file.
def connectToOwnCloud(self):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(("localhost", self.port()))
self.sock.settimeout(5)
self.connected = True
self.watch_id = GObject.io_add_watch(self.sock, GObject.IO_IN, self.handle_notify)
except:
print("Connect could not be established, try again later!")
self.sock.close()
return not self.connected
def sendCommand(self, cmd):
if self.connected:
try:
self.sock.send(cmd)
except:
print("Sending failed.")
GObject.source_remove(self.watch_id)
self.connected = False
GObject.timeout_add(5000, self.connectToOwnCloud)
def find_item_for_file(self, path):
if path in self.nautilusVFSFile_table:
return self.nautilusVFSFile_table[path]
else:
return None
def askForOverlay(self, file):
if os.path.isdir(file):
folderStatus = self.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
if os.path.isfile(file):
fileStatus = self.sendCommand("RETRIEVE_FILE_STATUS:"+file+"\n");
def invalidate_items_underneath( self, path ):
update_items = []
for p in self.nautilusVFSFile_table:
if p == path or p.startswith( path ):
item = self.nautilusVFSFile_table[p]
update_items.append(item)
def invalidate_items_underneath(self, path):
update_items = []
for p in self.nautilusVFSFile_table:
if p == path or p.startswith(path):
item = self.nautilusVFSFile_table[p]
update_items.append(item)
for item in update_items:
item.invalidate_extension_info()
# self.update_file_info(item)
for item in update_items:
item.invalidate_extension_info()
# self.update_file_info(item)
# Handles a single line of server respoonse and sets the emblem
def handle_server_response(self, l):
Emblems = { 'OK' : 'oC_ok',
'SYNC' : 'oC_sync',
'NEW' : 'oC_sync',
'IGNORE' : 'oC_warn',
'ERROR' : 'oC_error',
'OK+SWM' : 'oC_ok_shared',
'SYNC+SWM' : 'oC_sync_shared',
'NEW+SWM' : 'oC_sync_shared',
'IGNORE+SWM': 'oC_warn_shared',
'ERROR+SWM' : 'oC_error_shared',
'NOP' : 'oC_error'
}
'SYNC' : 'oC_sync',
'NEW' : 'oC_sync',
'IGNORE' : 'oC_warn',
'ERROR' : 'oC_error',
'OK+SWM' : 'oC_ok_shared',
'SYNC+SWM' : 'oC_sync_shared',
'NEW+SWM' : 'oC_sync_shared',
'IGNORE+SWM': 'oC_warn_shared',
'ERROR+SWM' : 'oC_error_shared',
'NOP' : 'oC_error'
}
print "Server response: "+l
print("Server response: "+l)
parts = l.split(':')
if len(parts) > 0:
action = parts[0]
action = parts[0]
# file = parts[1]
# print "Action for " + file + ": "+parts[0]
# file = parts[1]
# print "Action for " + file + ": "+parts[0]
if action == 'STATUS':
emblem = Emblems[parts[1]]
if emblem:
@ -99,24 +98,24 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
if item:
item.add_emblem(emblem)
elif action == 'UPDATE_VIEW':
# Search all items underneath this path and invalidate them
if parts[1] in self.registered_paths:
self.invalidate_items_underneath( parts[1] )
elif action == 'UPDATE_VIEW':
# Search all items underneath this path and invalidate them
if parts[1] in self.registered_paths:
self.invalidate_items_underneath(parts[1])
elif action == 'REGISTER_PATH':
self.registered_paths[parts[1]] = 1
self.invalidate_items_underneath( parts[1] )
elif action == 'UNREGISTER_PATH':
del self.registered_paths[parts[1]]
self.invalidate_items_underneath( parts[1] )
elif action == 'REGISTER_PATH':
self.registered_paths[parts[1]] = 1
self.invalidate_items_underneath(parts[1])
elif action == 'UNREGISTER_PATH':
del self.registered_paths[parts[1]]
self.invalidate_items_underneath(parts[1])
# check if there are non pathes any more, if so, its usual
# that mirall went away. Try reconnect.
if not self.registered_paths:
self.sock.close()
self.connected = False
GObject.source_remove( self.watch_id )
# check if there are non pathes any more, if so, its usual
# that mirall went away. Try reconnect.
if not self.registered_paths:
self.sock.close()
self.connected = False
GObject.source_remove(self.watch_id)
GObject.timeout_add(5000, self.connectToOwnCloud)
else:
@ -126,26 +125,26 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
# notify is the raw answer from the socket
def handle_notify(self, source, condition):
data = source.recv(1024)
# prepend the remaining data from last call
if len(self.remainder) > 0:
data = self.remainder+data
self.remainder = ''
data = source.recv(1024)
# prepend the remaining data from last call
if len(self.remainder) > 0:
data = self.remainder+data
self.remainder = ''
if len(data) > 0:
# remember the remainder for next round
lastNL = data.rfind('\n');
if lastNL > -1 and lastNL < len(data):
self.remainder = data[lastNL+1:]
data = data[:lastNL]
if len(data) > 0:
# remember the remainder for next round
lastNL = data.rfind('\n');
if lastNL > -1 and lastNL < len(data):
self.remainder = data[lastNL+1:]
data = data[:lastNL]
for l in data.split('\n'):
self.handle_server_response( l )
for l in data.split('\n'):
self.handle_server_response(l)
else:
return False
return True # run again
return True # run again
def get_local_path(self, path):
return path.replace("file://", "")
@ -155,14 +154,14 @@ class ownCloudExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoP
filename = urllib.unquote(item.get_uri()[7:])
if item.is_directory():
filename += '/'
filename += '/'
for reg_path in self.registered_paths:
if filename.startswith(reg_path):
self.nautilusVFSFile_table[filename] = item
# item.add_string_attribute('share_state', "share state")
self.askForOverlay(filename)
break
else:
print "Not in scope:"+filename
for reg_path in self.registered_paths:
if filename.startswith(reg_path):
self.nautilusVFSFile_table[filename] = item
# item.add_string_attribute('share_state', "share state")
self.askForOverlay(filename)
break
else:
print("Not in scope:"+filename)

Просмотреть файл

@ -5,6 +5,7 @@ set(cmd_NAME ${APPLICATION_EXECUTABLE}cmd)
set(cmd_SRC
cmd.cpp
simplesslerrorhandler.cpp
netrcparser.cpp
)
include_directories(${CMAKE_SOURCE_DIR}/src/libsync
${CMAKE_BINARY_DIR}/src/libsync

Просмотреть файл

@ -33,15 +33,27 @@
#include "cmd.h"
#include "netrcparser.h"
#ifdef Q_OS_WIN32
#include <windows.h>
#else
#include <termios.h>
#endif
using namespace Mirall;
struct CmdOptions {
QString source_dir;
QString target_url;
QString config_directory;
QString user;
QString password;
QString proxy;
bool silent;
bool trustSSL;
bool useNetrc;
bool interactive;
QString exclude;
};
@ -49,21 +61,59 @@ struct CmdOptions {
// So we have to use a global variable
CmdOptions *opts = 0;
int getauth(const char* prompt, char* buf, size_t len, int a, int b, void *userdata)
class EchoDisabler
{
Q_UNUSED(a) Q_UNUSED(b) Q_UNUSED(userdata)
std::cout << "** Authentication required: \n" << prompt << std::endl;
std::string s;
if(opts && opts->trustSSL) {
s = "yes";
} else {
std::getline(std::cin, s);
public:
EchoDisabler()
{
#ifdef Q_OS_WIN
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hStdin, &mode);
SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));
#else
tcgetattr(STDIN_FILENO, &tios);
termios tios_new = tios;
tios_new.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &tios_new);
#endif
}
strncpy( buf, s.c_str(), len );
return 0;
~EchoDisabler()
{
#ifdef Q_OS_WIN
SetConsoleMode(hStdin, mode);
#else
tcsetattr(STDIN_FILENO, TCSANOW, &tios);
#endif
}
private:
#ifdef Q_OS_WIN
DWORD mode = 0;
#else
termios tios;
#endif
};
QString queryPassword(const QString &user)
{
EchoDisabler disabler;
std::cout << "Password for user " << qPrintable(user) << ": ";
std::string s;
std::getline(std::cin, s);
return QString::fromStdString(s);
}
class HttpCredentialsText : public HttpCredentials {
public:
HttpCredentialsText(const QString& user, const QString& password) : HttpCredentials(user, password) {}
QString queryPassword(bool *ok) {
if (ok) {
*ok = true;
}
return ::queryPassword(user());
}
};
void help()
{
const char* appName = APPLICATION_EXECUTABLE "cmd";
@ -75,12 +125,15 @@ void help()
std::cout << "Otherwise, the setting from a configured sync client will be used." << std::endl;
std::cout << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " --silent Don't be so verbose" << std::endl;
std::cout << " --confdir = configdir: Read config from there." << std::endl;
std::cout << " --httpproxy = proxy: Specify a http proxy to use." << std::endl;
std::cout << " --silent, -s Don't be so verbose" << std::endl;
std::cout << " --httpproxy [proxy] Specify a http proxy to use." << std::endl;
std::cout << " Proxy is http://server:port" << std::endl;
std::cout << " --trust Trust the SSL certification." << std::endl;
std::cout << " --exclude [file] exclude list file" << std::endl;
std::cout << " --user, -u [name] Use [name] as the login name" << std::endl;
std::cout << " --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 << "" << std::endl;
exit(1);
@ -100,7 +153,7 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
if(!options->target_url.endsWith("/")) {
options->target_url.append("/");
}
options->target_url.append("remote.php/webdav/");
options->target_url.append("remote.php/webdav");
}
if (options->target_url.startsWith("http"))
options->target_url.replace(0, 4, "owncloud");
@ -117,14 +170,20 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
while(it.hasNext()) {
const QString option = it.next();
if( option == "--confdir" && !it.peekNext().startsWith("-") ) {
options->config_directory = it.next();
} else if( option == "--httpproxy" && !it.peekNext().startsWith("-")) {
if( option == "--httpproxy" && !it.peekNext().startsWith("-")) {
options->proxy = it.next();
} else if( option == "--silent") {
} else if( option == "-s" || option == "--silent") {
options->silent = true;
} else if( option == "--trust") {
options->trustSSL = true;
} else if( option == "-n") {
options->useNetrc = true;
} else if( option == "--non-interactive") {
options->interactive = false;
} else if( (option == "-u" || option == "--user") && !it.peekNext().startsWith("-") ) {
options->user = it.next();
} else if( (option == "-p" || option == "--password") && !it.peekNext().startsWith("-") ) {
options->user = it.next();
} else if( option == "--exclude" && !it.peekNext().startsWith("-") ) {
options->exclude = it.next();
} else {
@ -143,12 +202,58 @@ int main(int argc, char **argv) {
CmdOptions options;
options.silent = false;
options.trustSSL = false;
options.useNetrc = false;
options.interactive = true;
ClientProxy clientProxy;
parseOptions( app.arguments(), &options );
QUrl url(options.target_url.toUtf8());
QUrl url = QUrl::fromUserInput(options.target_url);
// Fetch username and password. If empty, try to retrieve
// from URL and strip URL
QString user;
QString password;
if (options.useNetrc) {
NetrcParser parser;
if (parser.parse()) {
NetrcParser::LoginPair pair = parser.find(url.host());
user = pair.first;
password = pair.second;
}
} else {
user = options.user;
if (user.isEmpty()) {
user = url.userName();
}
password = options.password;
if (password.isEmpty()) {
password = url.password();
}
if (options.interactive) {
if (user.isEmpty()) {
std::cout << "Please enter user name: ";
std::string s;
std::getline(std::cin, s);
user = QString::fromStdString(s);
}
if (password.isEmpty()) {
password = queryPassword(user);
}
}
}
// ### ensure URL is free of credentials
if (url.userName().isEmpty()) {
url.setUserName(user);
}
if (url.password().isEmpty()) {
url.setPassword(password);
}
Account account;
// Find the folder and the original owncloud url
@ -159,14 +264,19 @@ int main(int argc, char **argv) {
SimpleSslErrorHandler *sslErrorHandler = new SimpleSslErrorHandler;
HttpCredentials *cred = new HttpCredentialsText(user, password);
account.setUrl(url);
account.setCredentials(new HttpCredentials(url.userName(), url.password()));
account.setCredentials(cred);
account.setSslErrorHandler(sslErrorHandler);
AccountManager::instance()->setAccount(&account);
restart_sync:
CSYNC *_csync_ctx;
if( csync_create( &_csync_ctx, options.source_dir.toUtf8(),
options.target_url.toUtf8()) < 0 ) {
url.toEncoded().constData()) < 0 ) {
qFatal("Unable to create csync-context!");
return EXIT_FAILURE;
}
@ -178,7 +288,7 @@ int main(int argc, char **argv) {
csync_set_log_level(options.silent ? 1 : 11);
opts = &options;
csync_set_auth_callback( _csync_ctx, getauth );
cred->syncContextPreInit(_csync_ctx);
if( csync_init( _csync_ctx ) < 0 ) {
qFatal("Could not initialize csync!");
@ -225,6 +335,8 @@ int main(int argc, char **argv) {
csync_add_exclude_list(_csync_ctx, options.exclude.toLocal8Bit());
}
cred->syncContextPreStart(_csync_ctx);
Cmd cmd;
SyncJournalDb db(options.source_dir);
SyncEngine engine(_csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
@ -240,6 +352,11 @@ int main(int argc, char **argv) {
ne_sock_exit();
if (engine.isAnotherSyncNeeded()) {
qDebug() << "Restarting Sync, because another sync is needed";
goto restart_sync;
}
return 0;
}

97
src/cmd/netrcparser.cpp Normal file
Просмотреть файл

@ -0,0 +1,97 @@
/*
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <QDir>
#include <QFile>
#include <QTextStream>
#include "netrcparser.h"
namespace Mirall {
namespace {
QString defaultKeyword = QLatin1String("default");
QString machineKeyword = QLatin1String("machine");
QString loginKeyword = QLatin1String("login");
QString passwordKeyword = QLatin1String("password");
}
NetrcParser::NetrcParser(const QString &fileName)
: _fileName(fileName)
{
if (_fileName.isEmpty()) {
_fileName = QDir::homePath()+QLatin1String("/.netrc");
}
}
void NetrcParser::tryAddEntryAndClear(QString& machine, LoginPair& pair, bool& isDefault) {
if (isDefault) {
_default = pair;
} else if (!machine.isEmpty() && !pair.first.isEmpty()){
_entries.insert(machine, pair);
}
pair = qMakePair(QString(), QString());
machine.clear();
isDefault = false;
}
bool NetrcParser::parse()
{
QFile netrc(_fileName);
if (!netrc.open(QIODevice::ReadOnly)) {
return false;
}
QTextStream ts(&netrc);
LoginPair pair;
QString machine;
bool isDefault = false;
while (!ts.atEnd()) {
QString next;
ts >> next;
if (next == defaultKeyword) {
tryAddEntryAndClear(machine, pair, isDefault);
isDefault = true;
}
if (next == machineKeyword) {
tryAddEntryAndClear(machine, pair, isDefault);
ts >> machine;
} else if (next == loginKeyword) {
ts >> pair.first;
} else if (next == passwordKeyword) {
ts >> pair.second;
} // ignore unsupported tokens
}
tryAddEntryAndClear(machine, pair, isDefault);
if (!_entries.isEmpty() || _default != qMakePair(QString(), QString())) {
return true;
} else {
return false;
}
}
NetrcParser::LoginPair NetrcParser::find(const QString &machine)
{
QHash<QString, LoginPair>::const_iterator it = _entries.find(machine);
if (it != _entries.end()) {
return *it;
} else {
return _default;
}
}
} // namespace Mirall

41
src/cmd/netrcparser.h Normal file
Просмотреть файл

@ -0,0 +1,41 @@
/*
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef NETRCPARSER_H
#define NETRCPARSER_H
#include <QHash>
#include <QPair>
namespace Mirall {
class NetrcParser
{
public:
typedef QPair<QString, QString> LoginPair;
NetrcParser(const QString &fileName = QString::null);
bool parse();
LoginPair find(const QString &machine);
private:
void tryAddEntryAndClear(QString &machine, LoginPair &pair, bool &isDefault);
QHash<QString, LoginPair> _entries;
LoginPair _default;
QString _fileName;
};
} // namespace Mirall
#endif // NETRCPARSER_H

Просмотреть файл

@ -383,7 +383,7 @@ void AccountSettings::slotResetCurrentFolder()
if( ret == QMessageBox::Yes ) {
FolderMan *folderMan = FolderMan::instance();
Folder *f = folderMan->folder(alias);
f->slotTerminateSync();
f->slotTerminateAndPauseSync();
f->wipe();
folderMan->slotScheduleAllFolders();
}
@ -499,7 +499,7 @@ void AccountSettings::slotEnableCurrentFolder()
// message box can return at any time while the thread keeps running,
// so better check again after the user has responded.
if ( f->isBusy() && terminate ) {
f->slotTerminateSync();
f->slotTerminateAndPauseSync();
}
f->setSyncPaused(!currentlyPaused); // toggle the pause setting
folderMan->slotSetFolderPaused( alias, !currentlyPaused );

Просмотреть файл

@ -27,6 +27,8 @@
#include "clientproxy.h"
#include "syncengine.h"
#include "syncrunfilelog.h"
#include "theme.h"
#include "creds/abstractcredentials.h"
@ -255,18 +257,27 @@ void Folder::slotPollTimerTimeout()
{
qDebug() << "* Polling" << alias() << "for changes. (time since last sync:" << (_timeSinceLastSync.elapsed() / 1000) << "s)";
if (_paused || AccountManager::instance()->account()->state() != Account::Connected) {
qDebug() << "Not syncing. :" << _paused << AccountManager::instance()->account()->state();
Account *account = AccountManager::instance()->account();
if (!account) {
qDebug() << Q_FUNC_INFO << "No valid account object";
return;
}
if (_paused || account->state() != Account::Connected) {
qDebug() << "Not syncing. :" << _paused << account->state();
return;
}
if (quint64(_timeSinceLastSync.elapsed()) > MirallConfigFile().forceSyncInterval() ||
_lastEtag.isNull() ||
!(_syncResult.status() == SyncResult::Success ||_syncResult.status() == SyncResult::Problem)) {
qDebug() << "** Force Sync now, state is " << _syncResult.statusString();
emit scheduleToSync(alias());
} else {
// do the ordinary etag chech for the root folder.
RequestEtagJob* job = new RequestEtagJob(AccountManager::instance()->account(), remotePath(), this);
// do the ordinary etag check for the root folder.
RequestEtagJob* job = new RequestEtagJob(account, remotePath(), this);
// check if the etag is different
QObject::connect(job, SIGNAL(etagRetreived(QString)), this, SLOT(etagRetreived(QString)));
QObject::connect(job, SIGNAL(networkError(QNetworkReply*)), this, SLOT(slotNetworkUnavailable()));
@ -488,12 +499,16 @@ void Folder::slotTerminateSync()
// Do not display an error message, user knows his own actions.
// _errors.append( tr("The CSync thread terminated.") );
// _csyncError = true;
FolderMan::instance()->slotSetFolderPaused(alias(), true);
setSyncState(SyncResult::SyncAbortRequested);
return;
}
}
void Folder::slotTerminateAndPauseSync()
{
slotTerminateSync();
FolderMan::instance()->slotSetFolderPaused(alias(), true);
}
// This removes the csync File database
// This is needed to provide a clean startup again in case another
// local folder is synced to the same ownCloud.
@ -521,20 +536,27 @@ void Folder::wipe()
}
}
void Folder::setIgnoredFiles()
bool Folder::setIgnoredFiles()
{
bool ok = false;
MirallConfigFile cfgFile;
csync_clear_exclude_list( _csync_ctx );
QString excludeList = cfgFile.excludeFile( MirallConfigFile::SystemScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added system ignore list to csync:" << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
if (csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() ) == 0) {
ok = true;
}
}
excludeList = cfgFile.excludeFile( MirallConfigFile::UserScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added user defined ignore list to csync:" << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
// reading the user exclude file is optional
}
return ok;
}
void Folder::setProxyDirty(bool value)
@ -557,7 +579,7 @@ void Folder::startSync(const QStringList &pathList)
if (!_csync_ctx) {
qDebug() << Q_FUNC_INFO << "init failed.";
// the error should already be set
QMetaObject::invokeMethod(this, "slotCSyncFinished", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection);
return;
}
_clientProxy.setCSyncProxy(AccountManager::instance()->account()->url(), _csync_ctx);
@ -578,9 +600,16 @@ void Folder::startSync(const QStringList &pathList)
_syncResult.setStatus( SyncResult::SyncPrepare );
emit syncStateChange();
qDebug() << "*** Start syncing - client version"
<< qPrintable(Theme::instance()->version());
if (! setIgnoredFiles())
{
slotSyncError(tr("Could not read system exclude file"));
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection);
return;
}
qDebug() << "*** Start syncing";
setIgnoredFiles();
_engine.reset(new SyncEngine( _csync_ctx, path(), remoteUrl().path(), _remotePath, &_journal));
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
@ -658,10 +687,13 @@ void Folder::slotSyncFinished()
bubbleUpSyncResult();
_engine.reset(0);
bool anotherSyncNeeded = false;
if (_engine) {
anotherSyncNeeded = _engine->isAnotherSyncNeeded();
_engine.reset(0);
}
// _watcher->setEventsEnabledDelayed(2000);
_pollTimer.start();
_timeSinceLastSync.restart();
if (_csyncError) {
@ -689,6 +721,16 @@ void Folder::slotSyncFinished()
// all come in.
QTimer::singleShot(200, this, SLOT(slotEmitFinishedDelayed() ));
if (!anotherSyncNeeded) {
_pollTimer.start();
_timeSinceLastSync.restart();
} else {
// Another sync is required. We will make sure that the poll timer occurs soon enough
// and we clear the etag to force a sync
_lastEtag.clear();
QTimer::singleShot(1000, this, SLOT(slotPollTimerTimeout() ));
}
}
void Folder::slotEmitFinishedDelayed()

Просмотреть файл

@ -138,6 +138,7 @@ public slots:
* terminate the current sync run
*/
void slotTerminateSync();
void slotTerminateAndPauseSync();
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
@ -177,7 +178,7 @@ private slots:
private:
bool init();
void setIgnoredFiles();
bool setIgnoredFiles();
void bubbleUpSyncResult();

Просмотреть файл

@ -38,6 +38,15 @@ namespace Mirall {
FolderMan* FolderMan::_instance = 0;
/**
* The minimum time between a sync being requested and it
* being executed in milliseconds.
*
* This delay must be larger than the minFileAgeForUpload in
* the propagator.
*/
static int msBetweenRequestAndSync = 2000;
FolderMan::FolderMan(QObject *parent) :
QObject(parent),
_syncEnabled( true )
@ -225,14 +234,6 @@ bool FolderMan::ensureJournalGone(const QString &localPath)
return true;
}
void FolderMan::terminateCurrentSync()
{
if( !_currentSyncFolder.isEmpty() ) {
qDebug() << "Terminating syncing on folder " << _currentSyncFolder;
terminateSyncProcess( _currentSyncFolder );
}
}
#define SLASH_TAG QLatin1String("__SLASH__")
#define BSLASH_TAG QLatin1String("__BSLASH__")
#define QMARK_TAG QLatin1String("__QMARK__")
@ -435,31 +436,33 @@ void FolderMan::slotScheduleAllFolders()
*/
void FolderMan::slotScheduleSync( const QString& alias )
{
if( alias.isEmpty() ) return;
if( _currentSyncFolder == alias ) {
qDebug() << "folder " << alias << " is currently syncing. NOT scheduling.";
if( alias.isEmpty() || ! _folderMap.contains(alias) ) {
qDebug() << "Not scheduling sync for empty or unknown folder" << alias;
return;
}
qDebug() << "Schedule folder " << alias << " to sync!";
if( ! _scheduleQueue.contains(alias ) && _folderMap.contains(alias) ) {
if( ! _scheduleQueue.contains(alias) ) {
Folder *f = _folderMap[alias];
if( f ) {
if( !f->syncPaused() ) {
f->prepareToSync();
} else {
qDebug() << "Folder is not enabled, not scheduled!";
_socketApi->slotUpdateFolderView(f->alias());
return;
}
if ( !f )
return;
if( !f->syncPaused() ) {
f->prepareToSync();
} else {
qDebug() << "Folder is not enabled, not scheduled!";
_socketApi->slotUpdateFolderView(f->alias());
return;
}
_scheduleQueue.enqueue(alias);
} else {
qDebug() << " II> Sync for folder " << alias << " already scheduled, do not enqueue!";
}
// wait a moment until the syncing starts
QTimer::singleShot(500, this, SLOT(slotScheduleFolderSync()));
// Look at the scheduleQueue in a bit to see if the sync is ready to start.
// The delay here is essential as the sync will not upload files that were
// changed too recently.
QTimer::singleShot(msBetweenRequestAndSync, this, SLOT(slotStartScheduledFolderSync()));
}
// only enable or disable foldermans will to schedule and do syncs.
@ -468,7 +471,7 @@ void FolderMan::setSyncEnabled( bool enabled )
{
if (!_syncEnabled && enabled && !_scheduleQueue.isEmpty()) {
// We have things in our queue that were waiting the the connection to go back on.
QTimer::singleShot(200, this, SLOT(slotScheduleFolderSync()));
QTimer::singleShot(200, this, SLOT(slotStartScheduledFolderSync()));
}
_syncEnabled = enabled;
// force a redraw in case the network connect status changed
@ -480,7 +483,7 @@ void FolderMan::setSyncEnabled( bool enabled )
* It is either called from the slot where folders enqueue themselves for
* syncing or after a folder sync was finished.
*/
void FolderMan::slotScheduleFolderSync()
void FolderMan::slotStartScheduledFolderSync()
{
if( !_currentSyncFolder.isEmpty() ) {
qDebug() << "Currently folder " << _currentSyncFolder << " is running, wait for finish!";
@ -492,21 +495,26 @@ void FolderMan::slotScheduleFolderSync()
return;
}
// Try to start the top scheduled sync.
qDebug() << "XX slotScheduleFolderSync: folderQueue size: " << _scheduleQueue.count();
if( ! _scheduleQueue.isEmpty() ) {
if( !_scheduleQueue.isEmpty() ) {
const QString alias = _scheduleQueue.dequeue();
if( _folderMap.contains( alias ) ) {
Folder *f = _folderMap[alias];
if( f && !f->syncPaused() ) {
_currentSyncFolder = alias;
if( !_folderMap.contains( alias ) ) {
qDebug() << "FolderMan: Not syncing queued folder" << alias << ": not in folder map anymore";
return;
}
f->startSync( QStringList() );
// Start syncing this folder!
Folder *f = _folderMap[alias];
if( f && !f->syncPaused() ) {
_currentSyncFolder = alias;
// reread the excludes of the socket api
// FIXME: the excludes need rework.
_socketApi->slotClearExcludesList();
_socketApi->slotReadExcludes();
}
f->startSync( QStringList() );
// reread the excludes of the socket api
// FIXME: the excludes need rework.
_socketApi->slotClearExcludesList();
_socketApi->slotReadExcludes();
}
}
}
@ -526,7 +534,7 @@ void FolderMan::slotFolderSyncFinished( const SyncResult& )
_currentSyncFolder.clear();
QTimer::singleShot(200, this, SLOT(slotScheduleFolderSync()));
QTimer::singleShot(200, this, SLOT(slotStartScheduledFolderSync()));
}
void FolderMan::addFolderDefinition(const QString& alias, const QString& sourceFolder,
@ -620,8 +628,11 @@ void FolderMan::removeFolder( const QString& alias )
}
}
QString FolderMan::getBackupName( const QString& fullPathName ) const
QString FolderMan::getBackupName( QString fullPathName ) const
{
if (fullPathName.endsWith("/"))
fullPathName.chop(1);
if( fullPathName.isEmpty() ) return QString::null;
QString newName = fullPathName + QLatin1String(".oC_bak");
@ -639,27 +650,41 @@ QString FolderMan::getBackupName( const QString& fullPathName ) const
bool FolderMan::startFromScratch( const QString& localFolder )
{
if( localFolder.isEmpty() ) return false;
if( localFolder.isEmpty() ) {
return false;
}
QFileInfo fi( localFolder );
if( fi.exists() && fi.isDir() ) {
QDir file = fi.dir();
QDir parentDir( fi.dir() );
QString folderName = fi.fileName();
// check if there are files in the directory.
if( file.count() == 0 ) {
// directory is existing, but its empty. Use it.
// Adjust for case where localFolder ends with a /
if ( fi.isDir() ) {
folderName = parentDir.dirName();
parentDir.cdUp();
}
if( fi.exists() ) {
// It exists, but is empty -> just reuse it.
if( fi.isDir() && fi.dir().count() == 0 ) {
qDebug() << "startFromScratch: Directory is empty!";
return true;
}
QString newName = getBackupName( fi.absoluteFilePath() );
if( file.rename( fi.absoluteFilePath(), newName )) {
if( file.mkdir( fi.absoluteFilePath() ) ) {
return true;
}
// Make a backup of the folder/file.
QString newName = getBackupName( parentDir.absoluteFilePath( folderName ) );
if( !parentDir.rename( fi.absoluteFilePath(), newName ) ) {
qDebug() << "startFromScratch: Could not rename" << fi.absoluteFilePath()
<< "to" << newName;
return false;
}
}
return false;
if( !parentDir.mkdir( fi.absoluteFilePath() ) ) {
qDebug() << "startFromScratch: Could not mkdir" << fi.absoluteFilePath();
return false;
}
return true;
}
void FolderMan::setDirtyProxy(bool value)

Просмотреть файл

@ -107,6 +107,11 @@ public slots:
void slotFolderSyncStarted();
void slotFolderSyncFinished( const SyncResult& );
/**
* Terminates the specified folder sync (or the current one).
*
* It does not switch the folder to paused state.
*/
void terminateSyncProcess( const QString& alias = QString::null );
/* unload and delete on folder object */
@ -129,13 +134,12 @@ public slots:
private slots:
// slot to take the next folder from queue and start syncing.
void slotScheduleFolderSync();
void slotStartScheduledFolderSync();
private:
// finds all folder configuration files
// and create the folders
void terminateCurrentSync();
QString getBackupName( const QString& ) const;
QString getBackupName( QString fullPathName ) const;
void registerFolderMonitor( Folder *folder );
QString unescapeAlias( const QString& ) const;
@ -149,10 +153,12 @@ private:
QSignalMapper *_folderWatcherSignalMapper;
QString _currentSyncFolder;
bool _syncEnabled;
QQueue<QString> _scheduleQueue;
QMap<QString, FolderWatcher*> _folderWatchers;
QPointer<SocketApi> _socketApi;
/** The aliases of folders that shall be synced. */
QQueue<QString> _scheduleQueue;
static FolderMan *_instance;
explicit FolderMan(QObject *parent = 0);
~FolderMan();

Просмотреть файл

@ -619,6 +619,12 @@ void ownCloudGui::raiseDialog( QWidget *raiseWidget )
e.xclient.data.l[2] = 0;
e.xclient.data.l[3] = 0l;
e.xclient.data.l[4] = 0l;
Display *display = QX11Info::display();
XSendEvent(display,
RootWindow(display, DefaultScreen(display)),
False, // propagate
SubstructureRedirectMask|SubstructureNotifyMask,
&e);
#endif
}
}

Просмотреть файл

@ -72,11 +72,8 @@ void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList
QTreeWidgetItem *item = findFirstChild(parent, pathTrail.first());
if (!item) {
item = new QTreeWidgetItem(parent);
if (parent->checkState(0) == Qt::Checked) {
item->setCheckState(0, Qt::Checked);
} else if (parent->checkState(0) == Qt::Unchecked) {
item->setCheckState(0, Qt::Unchecked);
} else {
if (parent->checkState(0) == Qt::Checked
|| parent->checkState(0) == Qt::PartiallyChecked) {
item->setCheckState(0, Qt::Checked);
foreach(const QString &str , _oldBlackList) {
if (str + "/" == path) {
@ -86,6 +83,8 @@ void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList
item->setCheckState(0, Qt::PartiallyChecked);
}
}
} else if (parent->checkState(0) == Qt::Unchecked) {
item->setCheckState(0, Qt::Unchecked);
}
item->setIcon(0, folderIcon);
item->setText(0, pathTrail.first());
@ -173,7 +172,7 @@ void SelectiveSyncTreeView::slotItemChanged(QTreeWidgetItem *item, int col)
parent->setCheckState(0, Qt::PartiallyChecked);
}
}
// also check all the childs
// also check all the children
for (int i = 0; i < item->childCount(); ++i) {
if (item->child(i)->checkState(0) != Qt::Checked) {
item->child(i)->setCheckState(0, Qt::Checked);
@ -183,27 +182,21 @@ void SelectiveSyncTreeView::slotItemChanged(QTreeWidgetItem *item, int col)
if (item->checkState(0) == Qt::Unchecked) {
QTreeWidgetItem *parent = item->parent();
if (parent && parent->checkState(0) != Qt::Unchecked) {
bool hasChecked = false;
for (int i = 0; i < parent->childCount(); ++i) {
if (parent->child(i)->checkState(0) != Qt::Unchecked) {
hasChecked = true;
break;
}
}
if (!hasChecked) {
parent->setCheckState(0, Qt::Unchecked);
} else if (parent->checkState(0) == Qt::Checked) {
parent->setCheckState(0, Qt::PartiallyChecked);
}
if (parent && parent->checkState(0) == Qt::Checked) {
parent->setCheckState(0, Qt::PartiallyChecked);
}
// Uncheck all the childs
// Uncheck all the children
for (int i = 0; i < item->childCount(); ++i) {
if (item->child(i)->checkState(0) != Qt::Unchecked) {
item->child(i)->setCheckState(0, Qt::Unchecked);
}
}
// Can't uncheck the root.
if (!parent) {
item->setCheckState(0, Qt::PartiallyChecked);
}
}
if (item->checkState(0) == Qt::PartiallyChecked) {
@ -291,7 +284,7 @@ void SelectiveSyncDialog::accept()
FolderMan *folderMan = FolderMan::instance();
if (_folder->isBusy()) {
_folder->slotTerminateSync();
_folder->slotTerminateAndPauseSync();
}
folderMan->slotScheduleSync(_folder->alias());
}

Просмотреть файл

@ -46,6 +46,13 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent) :
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_ui->setupUi(this);
// People perceive this as a Window, so also make Ctrl+W work
QAction *closeWindowAction = new QAction(this);
closeWindowAction->setShortcut(QKeySequence("Ctrl+W"));
connect(closeWindowAction, SIGNAL(triggered()), SLOT(accept()));
addAction(closeWindowAction);
setObjectName("Settings"); // required as group for saveGeometry call
setWindowTitle(tr("%1").arg(Theme::instance()->appNameGUI()));

Просмотреть файл

@ -30,8 +30,13 @@ SettingsDialogMac::SettingsDialogMac(ownCloudGui *gui, QWidget *parent)
// Emulate dialog behavior: Escape means close
QAction *closeDialogAction = new QAction(this);
closeDialogAction->setShortcut(QKeySequence(Qt::Key_Escape));
connect(closeDialogAction, SIGNAL(triggered()), SLOT(close()));
addAction(closeDialogAction);
// People perceive this as a Window, so also make Ctrl+W work
QAction *closeWindowAction = new QAction(this);
closeWindowAction->setShortcut(QKeySequence(Qt::Key_Escape));
closeWindowAction->setShortcut(QKeySequence("Ctrl+W"));
connect(closeWindowAction, SIGNAL(triggered()), SLOT(close()));
addAction(closeWindowAction);

Просмотреть файл

@ -22,6 +22,7 @@
#include "theme.h"
#include "syncjournalfilerecord.h"
#include "syncfileitem.h"
#include "filesystem.h"
#include "version.h"
#include <QDebug>
@ -165,7 +166,7 @@ SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strli
if( type == CSYNC_FTW_TYPE_DIR ) {
// compute recursive status of the directory
status = recursiveFolderStatus( folder, fileName, excludes );
} else if(fi.lastModified() != rec._modtime ) {
} else if( FileSystem::getModTime(fi.absoluteFilePath()) != Utility::qDateTimeToTime_t(rec._modtime) ) {
// file was locally modified.
status.set(SyncFileStatus::STATUS_EVAL);
} else {
@ -245,6 +246,17 @@ void SocketApi::slotNewConnection()
_listeners.append(socket);
#ifdef Q_OS_MAC
// We want to tell our location so it can load the icons
// e.g. "/Users/guruz/woboq/owncloud/client/buildmirall/owncloud.app/Contents/MacOS/"
QString iconPath = qApp->applicationDirPath() + "/../Resources/icons/";
if (!QDir(iconPath).exists()) {
DEBUG << "Icon path " << iconPath << " does not exist, did you forget make install?";
}
broadcastMessage(QLatin1String("ICON_PATH"), iconPath );
#endif
foreach( QString alias, FolderMan::instance()->map().keys() ) {
slotRegisterPath(alias);
}

Просмотреть файл

@ -87,9 +87,9 @@ SparkleUpdater::SparkleUpdater(const QString& appCastUrl)
[NSString stringWithUTF8String: appCastUrl.toUtf8().data()]];
[d->updater setFeedURL: url];
// requires a more recent version
// NSString *userAgent = [NSString stringWithUTF8String: Utility::userAgentString().data()];
// [d->updater setUserAgentString: userAgent];
// Sparkle 1.8 required
NSString *userAgent = [NSString stringWithUTF8String: Utility::userAgentString().data()];
[d->updater setUserAgentString: userAgent];
}
SparkleUpdater::~SparkleUpdater()

Просмотреть файл

@ -148,7 +148,7 @@ void OwncloudHttpCredsPage::setErrorString(const QString& err)
AbstractCredentials* OwncloudHttpCredsPage::getCredentials() const
{
return new HttpCredentials(_ui.leUsername->text(), _ui.lePassword->text());
return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text());
}
void OwncloudHttpCredsPage::setConfigExists(bool config)

Просмотреть файл

@ -16,6 +16,7 @@
#include <QDir>
#include <QFileDialog>
#include <QUrl>
#include <QTimer>
#include <QPushButton>
#include <QMessageBox>
@ -143,9 +144,12 @@ void OwncloudSetupPage::initializePage()
if (Theme::instance()->overrideServerUrl().isEmpty()) {
_ui.leUrl->setFocus();
} else {
setVisible(false);
setCommitPage(true);
validatePage();
setVisible(false);
// because the wizard will call show on us right after this call, we need to hide in the
// next event loop iteration.
QTimer::singleShot(0, this, SLOT(hide()));
}
}

Просмотреть файл

@ -171,7 +171,6 @@ void OwncloudWizard::slotCurrentPageChanged( int id )
if( id == WizardCommon::Page_ServerSetup ) {
emit clearPendingRequests();
_setupPage->initializePage();
}
if( id == WizardCommon::Page_Result ) {

Просмотреть файл

@ -176,7 +176,7 @@ else()
install(TARGETS ${synclib_NAME} DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
if (SPARKLE_FOUND)
install(DIRECTORY "${SPARKLE_LIBRARY}"
DESTINATION "${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks")
DESTINATION "${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks" USE_SOURCE_PERMISSIONS)
endif (SPARKLE_FOUND)
endif()

Просмотреть файл

@ -39,6 +39,7 @@ static const char urlC[] = "url";
static const char authTypeC[] = "authType";
static const char userC[] = "user";
static const char httpUserC[] = "http_user";
static const char caCertsKeyC[] = "CaCertificates";
AccountManager *AccountManager::_instance = 0;
@ -102,15 +103,15 @@ void Account::save()
}
settings->sync();
// ### TODO port away from MirallConfigFile
MirallConfigFile cfg;
// Save accepted certificates.
settings->beginGroup(QLatin1String("General"));
qDebug() << "Saving " << approvedCerts().count() << " unknown certs.";
QByteArray certs;
Q_FOREACH( const QSslCertificate& cert, approvedCerts() ) {
certs += cert.toPem() + '\n';
}
if (!certs.isEmpty()) {
cfg.setCaCerts( certs );
settings->setValue( QLatin1String(caCertsKeyC), certs );
}
}
@ -141,13 +142,15 @@ Account* Account::restore()
// Check the theme url to see if it is the same url that the oC config was for
QString overrideUrl = Theme::instance()->overrideServerUrl();
if( !overrideUrl.isEmpty() ) {
if (overrideUrl.endsWith('/')) { overrideUrl.chop(1); }
QString oCUrl = oCSettings->value(QLatin1String(urlC)).toString();
if (oCUrl.endsWith('/')) { oCUrl.chop(1); }
// in case the urls are equal reset the settings object to read from
// the ownCloud settings object
qDebug() << "Migrate oC config if " << oCUrl << " == " << overrideUrl << ":"
<< (QUrl(oCUrl) == QUrl(overrideUrl) ? "Yes" : "No");
if( QUrl(oCUrl) == QUrl(overrideUrl) ) {
<< (oCUrl == overrideUrl ? "Yes" : "No");
if( oCUrl == overrideUrl ) {
migratedCreds = true;
settings.reset( oCSettings );
} else {
@ -174,7 +177,7 @@ Account* Account::restore()
// now the cert, it is in the general group
settings->beginGroup(QLatin1String("General"));
acc->setApprovedCerts(QSslCertificate::fromData(settings->value(QLatin1String("CaCertificates")).toByteArray()));
acc->setApprovedCerts(QSslCertificate::fromData(settings->value(caCertsKeyC).toByteArray()));
acc->setMigrated(migratedCreds);
return acc;
}

Просмотреть файл

@ -85,6 +85,7 @@ void ConnectionValidator::checkConnection()
checkJob->setIgnoreCredentialFailure(true);
connect(checkJob, SIGNAL(instanceFound(QUrl,QVariantMap)), SLOT(slotStatusFound(QUrl,QVariantMap)));
connect(checkJob, SIGNAL(networkError(QNetworkReply*)), SLOT(slotNoStatusFound(QNetworkReply*)));
connect(checkJob, SIGNAL(timeout(QUrl)), SLOT(slotStatusTimeout(QUrl)));
checkJob->start();
} else {
_errors << tr("No ownCloud account configured");
@ -127,9 +128,19 @@ void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
_errors.append( reply->errorString() );
_networkError = (reply->error() != QNetworkReply::NoError);
emit connectionResult( StatusNotFound );
}
void ConnectionValidator::slotStatusTimeout(const QUrl &url)
{
_account->setState(Account::Disconnected);
_errors.append(tr("Unable to connect to %1").arg(url.toString()));
_errors.append(tr("timeout"));
_networkError = true;
emit connectionResult( StatusNotFound );
}
void ConnectionValidator::slotCheckAuthentication()
{
AbstractCredentials *creds = _account->credentials();

Просмотреть файл

@ -60,6 +60,7 @@ public slots:
protected slots:
void slotStatusFound(const QUrl&url, const QVariantMap &info);
void slotNoStatusFound(QNetworkReply *reply);
void slotStatusTimeout(const QUrl& url);
void slotCheckAuthentication();
void slotAuthFailed(QNetworkReply *reply);

Просмотреть файл

@ -36,7 +36,7 @@ AbstractCredentials* create(const QString& type)
// empty string might happen for old version of configuration
if (type == "http" || type == "") {
return new HttpCredentials;
return new HttpCredentialsGui;
} else if (type == "dummy") {
return new DummyCredentials;
} else if (type == "shibboleth") {

Просмотреть файл

@ -37,9 +37,6 @@ using namespace QKeychain;
namespace Mirall
{
namespace
{
int getauth(const char *prompt,
char *buf,
size_t len,
@ -74,10 +71,10 @@ int getauth(const char *prompt,
return re;
}
namespace
{
const char userC[] = "user";
const char authenticationFailedC[] = "owncloud-authentication-failed";
} // ns
class HttpCredentialsAccessManager : public MirallAccessManager {
@ -299,19 +296,6 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
}
}
QString HttpCredentials::queryPassword(bool *ok)
{
if (ok) {
QString str = QInputDialog::getText(0, tr("Enter Password"),
tr("Please enter %1 password for user '%2':")
.arg(Theme::instance()->appNameGUI(), _user),
QLineEdit::Password, QString(), ok);
return str;
} else {
return QString();
}
}
void HttpCredentials::invalidateToken(Account *account)
{
_password = QString();
@ -380,4 +364,17 @@ void HttpCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* a
reply->close();
}
QString HttpCredentialsGui::queryPassword(bool *ok)
{
if (ok) {
QString str = QInputDialog::getText(0, tr("Enter Password"),
tr("Please enter %1 password for user '%2':")
.arg(Theme::instance()->appNameGUI(), _user),
QLineEdit::Password, QString(), ok);
return str;
} else {
return QString();
}
}
} // ns Mirall

Просмотреть файл

@ -49,7 +49,7 @@ public:
void persist(Account *account) Q_DECL_OVERRIDE;
QString user() const Q_DECL_OVERRIDE;
QString password() const;
QString queryPassword(bool *ok);
virtual QString queryPassword(bool *ok) = 0;
void invalidateToken(Account *account) Q_DECL_OVERRIDE;
QString fetchUser(Account *account);
@ -58,14 +58,23 @@ private Q_SLOTS:
void slotReadJobDone(QKeychain::Job*);
void slotWriteJobDone(QKeychain::Job*);
private:
protected:
QString _user;
QString _password;
private:
bool _ready;
bool _fetchJobInProgress; //True if the keychain job is in progress or the input dialog visible
bool _readPwdFromDeprecatedPlace;
};
class OWNCLOUDSYNC_EXPORT HttpCredentialsGui : public HttpCredentials {
public:
HttpCredentialsGui() : HttpCredentials() {}
HttpCredentialsGui(const QString& user, const QString& password) : HttpCredentials(user, password) {}
QString queryPassword(bool *ok) Q_DECL_OVERRIDE;
};
} // ns Mirall
#endif

Просмотреть файл

@ -385,7 +385,7 @@ void ShibbolethCredentials::showLoginWindow(Account* account)
QList<QNetworkCookie> ShibbolethCredentials::accountCookies(Account *account)
{
return account->networkAccessManager()->cookieJar()->cookiesForUrl(account->url());
return account->networkAccessManager()->cookieJar()->cookiesForUrl(account->davUrl());
}
QNetworkCookie ShibbolethCredentials::findShibCookie(Account *account, QList<QNetworkCookie> cookies)

Просмотреть файл

@ -12,7 +12,10 @@
*/
#include "filesystem.h"
#include "utility.h"
#include <QFile>
#include <QFileInfo>
#include <QDebug>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
@ -24,11 +27,16 @@
#include <winbase.h>
#endif
// We use some internals of csync:
extern "C" int c_utimes(const char *, const struct timeval *);
extern "C" void csync_win32_set_file_hidden( const char *file, bool h );
extern "C" {
#include "vio/csync_vio_handle.h"
#include "vio/csync_vio_file_stat.h"
#include "vio/csync_vio_local.h"
}
namespace Mirall {
bool FileSystem::fileEquals(const QString& fn1, const QString& fn2)
@ -69,6 +77,25 @@ void FileSystem::setFileHidden(const QString& filename, bool hidden)
return csync_win32_set_file_hidden(filename.toUtf8().constData(), hidden);
}
time_t FileSystem::getModTime(const QString &filename)
{
csync_vio_file_stat_t* stat = csync_vio_file_stat_new();
qint64 result = -1;
if (csync_vio_local_stat(filename.toUtf8().data(), stat) != -1
&& (stat->fields & CSYNC_VIO_FILE_STAT_FIELDS_MTIME))
{
result = stat->mtime;
}
else
{
qDebug() << "Could not get modification time for" << filename
<< "with csync, using QFileInfo";
result = Utility::qDateTimeToTime_t(QFileInfo(filename).lastModified());
}
csync_vio_file_stat_destroy(stat);
return result;
}
void FileSystem::setModTime(const QString& filename, time_t modTime)
{
struct timeval times[2];

Просмотреть файл

@ -32,6 +32,14 @@ bool fileEquals(const QString &fn1, const QString &fn2);
/** Mark the file as hidden (only has effects on windows) */
void OWNCLOUDSYNC_EXPORT setFileHidden(const QString& filename, bool hidden);
/** Get the mtime for a filepath.
*
* Use this over QFileInfo::lastModified() to avoid timezone related bugs. See
* owncloud/core#9781 for details.
*/
time_t OWNCLOUDSYNC_EXPORT getModTime(const QString &filename);
void setModTime(const QString &filename, time_t modTime);
/**

Просмотреть файл

@ -232,6 +232,14 @@ QString MirallConfigFile::excludeFile(Scope scope) const
#endif
#ifdef Q_OS_UNIX
fi.setFile( QString( SYSCONFDIR "/%1").arg(Theme::instance()->appName()), exclFile );
if ( ! fi.exists() ) {
// Prefer to return the preferred path! Only use the fallback location
// if the other path does not exist and the fallback is valid.
QFileInfo nextToBinary( QCoreApplication::applicationDirPath(), exclFile );
if (nextToBinary.exists()) {
fi = nextToBinary;
}
}
#endif
#ifdef Q_OS_MAC
// exec path is inside the bundle

Просмотреть файл

@ -193,6 +193,13 @@ void AbstractNetworkJob::start()
qDebug() << "!!!" << metaObject()->className() << "created for" << account()->url() << "querying" << path();
}
void AbstractNetworkJob::slotTimeout()
{
qDebug() << this << "Timeout" ;
reply()->abort();
}
/*********************************************************************************************/
RequestEtagJob::RequestEtagJob(Account *account, const QString &path, QObject *parent)
@ -359,6 +366,7 @@ void CheckServerJob::slotTimeout()
qDebug() << "TIMEOUT" << Q_FUNC_INFO;
if (reply()->isRunning())
emit timeout(reply()->url());
deleteLater();
}
QString CheckServerJob::version(const QVariantMap &info)

Просмотреть файл

@ -96,7 +96,7 @@ protected:
private slots:
void slotFinished();
virtual void slotTimeout() {}
virtual void slotTimeout();
private:
QNetworkReply* addTimer(QNetworkReply *reply);

Просмотреть файл

@ -84,13 +84,14 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
// do not blacklist in case of soft error or fatal error.
break;
case SyncFileItem::NormalError:
if (_item._httpErrorCode == 0 // Do not blacklist local errors. (#1985)
#ifdef OWNCLOUD_5XX_NO_BLACKLIST
if (_item._httpErrorCode / 100 == 5) {
// In this configuration, never blacklist error 5xx
qDebug() << "Do not blacklist error " << _item._httpErrorCode;
|| _item._httpErrorCode / 100 == 5 // In this configuration, never blacklist error 5xx
#endif
) {
qDebug() << "This error is not blacklisted " << _item._httpErrorCode;
break;
}
#endif
_propagator->_journal->updateBlacklistEntry( record );
break;
case SyncFileItem::Success:
@ -150,6 +151,7 @@ bool PropagateItemJob::checkForProblemsWithShared(int httpStatusCode, const QStr
// Also remove the inodes and fileid from the db so no further renames are tried for
// this item.
_propagator->_journal->avoidRenamesOnNextSync(_item._file);
_propagator->_anotherSyncNeeded = true;
}
if( newJob ) {
newJob->setRestoreJobMsg(msg);
@ -399,6 +401,11 @@ bool OwncloudPropagator::localFileNameClash( const QString& relFile )
return re;
}
QString OwncloudPropagator::getFilePath(const QString& tmp_file_name) const
{
return _localDir + tmp_file_name;
}
// ================================================================================
void PropagateDirectory::start()

Просмотреть файл

@ -206,6 +206,7 @@ public:
, _journal(progressDb)
, _finishedEmited(false)
, _activeJobs(0)
, _anotherSyncNeeded(false)
{ }
void start(const SyncFileItemVector &_syncedItems);
@ -218,8 +219,12 @@ public:
/* The number of currently active jobs */
int _activeJobs;
/** We detected that another sync is required after this one */
bool _anotherSyncNeeded;
bool isInSharedDirectory(const QString& file);
bool localFileNameClash(const QString& relfile);
QString getFilePath(const QString& tmp_file_name) const;
void abort() {
_abortRequested.fetchAndStoreOrdered(true);

Просмотреть файл

@ -50,7 +50,7 @@ QString ownCloudTheme::about() const
"Olivier Goffart, Markus Götz and others.<br/>"
"Based on Mirall by Duncan Mac-Vicar P.</small></p>"
"<p>Copyright ownCloud, Inc.</p>"
"<p>Licensed under the GNU Public License (GPL) Version 2.0<br/>"
"<p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>"
"ownCloud and the ownCloud Logo are registered trademarks of ownCloud, "
"Inc. in the United States, other countries, or both</p>"
)
@ -114,7 +114,7 @@ QPixmap ownCloudTheme::wizardHeaderLogo() const
QString ownCloudTheme::appName() const
{
return QLatin1String("owncloud");
return QLatin1String("ownCloud");
}
QString ownCloudTheme::appNameGUI() const

Просмотреть файл

@ -62,7 +62,7 @@ void PropagateUploadFileLegacy::start()
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
return;
QFile file(_propagator->_localDir + _item._file);
QFile file(_propagator->getFilePath(_item._file));
if (!file.open(QIODevice::ReadOnly)) {
done(SyncFileItem::NormalError, file.errorString());
return;
@ -189,7 +189,7 @@ void PropagateUploadFileLegacy::start()
return;
}
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, _propagator->_localDir + _item._file));
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, _propagator->getFilePath(_item._file)));
// Remove from the progress database:
_propagator->_journal->setUploadInfo(_item._file, SyncJournalDb::UploadInfo());
_propagator->_journal->commit("upload file start");
@ -498,7 +498,7 @@ void PropagateDownloadFileLegacy::start()
if (progressInfo._valid) {
// if the etag has changed meanwhile, remove the already downloaded part.
if (progressInfo._etag != _item._etag) {
QFile::remove(_propagator->_localDir + progressInfo._tmpfile);
QFile::remove(_propagator->getFilePath(progressInfo._tmpfile));
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
} else {
tmpFileName = progressInfo._tmpfile;
@ -516,7 +516,7 @@ void PropagateDownloadFileLegacy::start()
tmpFileName += ".~" + QString::number(uint(qrand()), 16);
}
QFile tmpFile(_propagator->_localDir + tmpFileName);
QFile tmpFile(_propagator->getFilePath(tmpFileName));
_file = &tmpFile;
if (!tmpFile.open(QIODevice::Append | QIODevice::Unbuffered)) {
done(SyncFileItem::NormalError, tmpFile.errorString());
@ -610,7 +610,7 @@ void PropagateDownloadFileLegacy::start()
tmpFile.close();
tmpFile.flush();
QString fn = _propagator->_localDir + _item._file;
QString fn = _propagator->getFilePath(_item._file);
bool isConflict = _item._instruction == CSYNC_INSTRUCTION_CONFLICT

Просмотреть файл

@ -27,6 +27,18 @@
namespace Mirall {
/**
* The mtime of a file must be at least this many milliseconds in
* the past for an upload to be started. Otherwise the propagator will
* assume it's still being changed and skip it.
*
* This value must be smaller than the msBetweenRequestAndSync in
* the folder manager.
*
* Two seconds has shown to be a good value in tests.
*/
static int minFileAgeForUpload = 2000;
static qint64 chunkSize() {
static uint chunkSize;
if (!chunkSize) {
@ -97,14 +109,29 @@ void PropagateUploadFileQNAM::start()
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
return;
_file = new QFile(_propagator->_localDir + _item._file, this);
_file = new QFile(_propagator->getFilePath(_item._file), this);
if (!_file->open(QIODevice::ReadOnly)) {
done(SyncFileItem::NormalError, _file->errorString());
delete _file;
return;
}
// Update the mtime and size, it might have changed since discovery.
_item._modtime = FileSystem::getModTime(_file->fileName());
quint64 fileSize = _file->size();
_item._size = fileSize;
// But skip the file if the mtime is too close to 'now'!
// That usually indicates a file that is still being changed
// or not yet fully copied to the destination.
QDateTime modtime = Utility::qDateTimeFromTime_t(_item._modtime);
if (modtime.msecsTo(QDateTime::currentDateTime()) < minFileAgeForUpload) {
_propagator->_anotherSyncNeeded = true;
done(SyncFileItem::SoftError, tr("Local file changed during sync."));
delete _file;
return;
}
_chunkCount = std::ceil(fileSize/double(chunkSize()));
_startChunk = 0;
_transferId = qrand() ^ _item._modtime ^ (_item._size << 16);
@ -198,20 +225,6 @@ void PropagateUploadFileQNAM::startNextChunk()
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
return;
/*
* // If the source file has changed during upload, it is detected and the
* // variable _previousFileSize is set accordingly. The propagator waits a
* // couple of seconds and retries.
* if(_previousFileSize > 0) {
* qDebug() << "File size changed underway: " << trans->stat_size - _previousFileSize;
* // Report the change of the overall transmission size to the propagator
* _propagator->overallTransmissionSizeChanged(qint64(trans->stat_size - _previousFileSize));
* // update the item's values to the current from trans. hbf_splitlist does a stat
* _item._size = trans->stat_size;
* _item._modtime = trans->modtime;
*
*/
quint64 fileSize = _item._size;
QMap<QByteArray, QByteArray> headers;
headers["OC-Total-Length"] = QByteArray::number(fileSize);
@ -307,16 +320,22 @@ void PropagateUploadFileQNAM::slotPutFinished()
|| job->reply()->hasRawHeader("OC-ETag");
if (!finished) {
QFileInfo fi(_propagator->_localDir + _item._file);
QFileInfo fi(_propagator->getFilePath(_item._file));
if( !fi.exists() ) {
_propagator->_activeJobs--;
done(SyncFileItem::SoftError, tr("The local file was removed during sync."));
return;
}
if (Utility::qDateTimeToTime_t(fi.lastModified()) != _item._modtime) {
qDebug() << "The local file has changed during upload:" << _item._modtime << "!=" << Utility::qDateTimeToTime_t(fi.lastModified()) << fi.lastModified();
const time_t new_mtime = FileSystem::getModTime(fi.absoluteFilePath());
const quint64 new_size = static_cast<quint64>(fi.size());
if (new_mtime != _item._modtime || new_size != _item._size) {
qDebug() << "The local file has changed during upload:"
<< "mtime: " << _item._modtime << "<->" << new_mtime
<< ", size: " << _item._size << "<->" << new_size
<< ", QFileInfo: " << Utility::qDateTimeToTime_t(fi.lastModified()) << fi.lastModified();
_propagator->_activeJobs--;
_propagator->_anotherSyncNeeded = true;
done(SyncFileItem::SoftError, tr("Local file changed during sync."));
// FIXME: the legacy code was retrying for a few seconds.
// and also checking that after the last chunk, and removed the file in case of INSTRUCTION_NEW
@ -381,7 +400,7 @@ void PropagateUploadFileQNAM::finalize(const SyncFileItem &copy)
_item._requestDuration = _duration.elapsed();
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, _propagator->_localDir + _item._file));
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, _propagator->getFilePath(_item._file)));
// Remove from the progress database:
_propagator->_journal->setUploadInfo(_item._file, SyncJournalDb::UploadInfo());
_propagator->_journal->commit("upload file start");
@ -569,7 +588,7 @@ void PropagateDownloadFileQNAM::start()
if (progressInfo._valid) {
// if the etag has changed meanwhile, remove the already downloaded part.
if (progressInfo._etag != _item._etag) {
QFile::remove(_propagator->_localDir + progressInfo._tmpfile);
QFile::remove(_propagator->getFilePath(progressInfo._tmpfile));
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
} else {
tmpFileName = progressInfo._tmpfile;
@ -587,7 +606,7 @@ void PropagateDownloadFileQNAM::start()
tmpFileName += ".~" + QString::number(uint(qrand()), 16);
}
_tmpFile.setFileName(_propagator->_localDir + tmpFileName);
_tmpFile.setFileName(_propagator->getFilePath(tmpFileName));
if (!_tmpFile.open(QIODevice::Append | QIODevice::Unbuffered)) {
done(SyncFileItem::NormalError, _tmpFile.errorString());
return;
@ -711,7 +730,7 @@ QString makeConflictFileName(const QString &fn, const QDateTime &dt)
void PropagateDownloadFileQNAM::downloadFinished()
{
QString fn = _propagator->_localDir + _item._file;
QString fn = _propagator->getFilePath(_item._file);
bool isConflict = _item._instruction == CSYNC_INSTRUCTION_CONFLICT
@ -740,7 +759,10 @@ void PropagateDownloadFileQNAM::downloadFinished()
return;
}
// Maybe we downloaded a newer version of the file than we thought we would...
// Get up to date information for the journal.
FileSystem::setModTime(fn, _item._modtime);
_item._size = existingFile.size();
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, fn));
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());

Просмотреть файл

@ -59,6 +59,7 @@ SyncEngine::SyncEngine(CSYNC *ctx, const QString& localPath, const QString& remo
, _hasRemoveFile(false)
, _uploadLimit(0)
, _downloadLimit(0)
, _anotherSyncNeeded(false)
{
qRegisterMetaType<SyncFileItem>("SyncFileItem");
qRegisterMetaType<SyncFileItem::Status>("SyncFileItem::Status");
@ -244,6 +245,57 @@ bool SyncEngine::checkBlacklisting( SyncFileItem *item )
return re;
}
void SyncEngine::deleteStaleDownloadInfos()
{
// Find all downloadinfo paths that we want to preserve.
QSet<QString> download_file_paths;
foreach(const SyncFileItem& it, _syncedItems) {
if (it._direction == SyncFileItem::Down
&& it._type == SyncFileItem::File)
{
download_file_paths.insert(it._file);
}
}
// Delete from journal and from filesystem.
const QVector<SyncJournalDb::DownloadInfo> deleted_infos =
_journal->getAndDeleteStaleDownloadInfos(download_file_paths);
foreach (const SyncJournalDb::DownloadInfo & deleted_info, deleted_infos) {
const QString tmppath = _propagator->getFilePath(deleted_info._tmpfile);
qDebug() << "Deleting stale temporary file: " << tmppath;
QFile::remove(tmppath);
}
}
void SyncEngine::deleteStaleUploadInfos()
{
// Find all blacklisted paths that we want to preserve.
QSet<QString> upload_file_paths;
foreach(const SyncFileItem& it, _syncedItems) {
if (it._direction == SyncFileItem::Up
&& it._type == SyncFileItem::File)
{
upload_file_paths.insert(it._file);
}
}
// Delete from journal.
_journal->deleteStaleUploadInfos(upload_file_paths);
}
void SyncEngine::deleteStaleBlacklistEntries()
{
// Find all blacklisted paths that we want to preserve.
QSet<QString> blacklist_file_paths;
foreach(const SyncFileItem& it, _syncedItems) {
if (it._status == SyncFileItem::FileIgnored)
blacklist_file_paths.insert(it._file);
}
// Delete from journal.
_journal->deleteStaleBlacklistEntries(blacklist_file_paths);
}
int SyncEngine::treewalkLocal( TREE_WALK_FILE* file, void *data )
{
return static_cast<SyncEngine*>(data)->treewalkFile( file, false );
@ -302,6 +354,9 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
case CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK:
item._errorString = tr("Symbolic links are not supported in syncing.");
break;
case CSYNC_STATUS_INDIVIDUAL_IS_HARDLINK:
item._errorString = tr("Hard links are not supported in syncing.");
break;
case CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST:
item._errorString = tr("File is listed on the ignore list.");
break;
@ -535,7 +590,7 @@ void SyncEngine::startSync()
qDebug() << "#### Discovery start #################################################### >>";
DiscoveryJob *job = new DiscoveryJob(_csync_ctx);
job->_selectiveSyncBlackList = _selectiveSyncWhiteList;
job->_selectiveSyncBlackList = _selectiveSyncBlackList;
job->moveToThread(&_thread);
connect(job, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int)));
connect(job, SIGNAL(folderDiscovered(bool,QString)),
@ -647,6 +702,11 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
// apply the network limits to the propagator
setNetworkLimits(_uploadLimit, _downloadLimit);
deleteStaleDownloadInfos();
deleteStaleUploadInfos();
deleteStaleBlacklistEntries();
_journal->commit("post stale entry removal");
_propagator->start(_syncedItems);
}
@ -706,6 +766,8 @@ void SyncEngine::slotJobCompleted(const SyncFileItem &item)
void SyncEngine::slotFinished()
{
_anotherSyncNeeded = _anotherSyncNeeded || _propagator->_anotherSyncNeeded;
// emit the treewalk results.
if( ! _journal->postSyncCleanup( _seenFiles ) ) {
qDebug() << "Cleaning of synced ";
@ -917,6 +979,7 @@ void SyncEngine::checkForPermission()
// At this point we would need to go back to the propagate phase on both remote to take
// the decision.
_journal->avoidRenamesOnNextSync(it->_file);
_anotherSyncNeeded = true;
if (it->_isDirectory) {

Просмотреть файл

@ -62,7 +62,10 @@ public:
Utility::StopWatch &stopWatch() { return _stopWatch; }
void setSelectiveSyncBlackList(const QStringList &list)
{ _selectiveSyncWhiteList = list; }
{ _selectiveSyncBlackList = list; }
/* Return true if we detected that another sync is needed to complete the sync */
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
signals:
void csyncError( const QString& );
@ -107,6 +110,16 @@ private:
int treewalkFile( TREE_WALK_FILE*, bool );
bool checkBlacklisting( SyncFileItem *item );
// Cleans up unnecessary downloadinfo entries in the journal as well
// as their temporary files.
void deleteStaleDownloadInfos();
// Removes stale uploadinfos from the journal.
void deleteStaleUploadInfos();
// Removes stale blacklist entries from the journal.
void deleteStaleBlacklistEntries();
// cleanup and emit the finished signal
void finalize();
@ -149,7 +162,9 @@ private:
// hash containing the permissions on the remote directory
QHash<QString, QByteArray> _remotePerms;
QStringList _selectiveSyncWhiteList;
QStringList _selectiveSyncBlackList;
bool _anotherSyncNeeded;
};
}

Просмотреть файл

@ -80,8 +80,9 @@ void SyncJournalDb::commitTransaction()
bool SyncJournalDb::sqlFail( const QString& log, const QSqlQuery& query )
{
commitTransaction();
qWarning() << "Error" << log << query.lastError().text();
qWarning() << "SQL Error" << log << query.lastError().text();
Q_ASSERT(!"SQL ERROR");
_db.close();
return false;
}
@ -227,6 +228,9 @@ bool SyncJournalDb::checkConnect()
commitInternal("checkConnect");
bool rc = updateDatabaseStructure();
if( !rc ) {
qDebug() << "WARN: Failed to update the database structure!";
}
_getFileRecordQuery.reset(new QSqlQuery(_db));
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm FROM "
@ -310,20 +314,19 @@ bool SyncJournalDb::updateDatabaseStructure()
if( !checkConnect() ) {
return false;
}
if( columns.indexOf(QLatin1String("fileid")) == -1 ) {
if( columns.indexOf(QLatin1String("fileid")) == -1 ) {
QSqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN fileid VARCHAR(128);");
re = query.exec();
if(!re) {
qDebug() << Q_FUNC_INFO << "SQL Error " << query.lastError().text();
if( !query.exec() ) {
sqlFail("updateDatabaseStructure: Add column fileid", query);
re = false;
}
query.prepare("CREATE INDEX metadata_file_id ON metadata(fileid);");
re = re && query.exec();
if(!re) {
qDebug() << Q_FUNC_INFO << "SQL Error " << query.lastError().text();
if( ! query.exec() ) {
sqlFail("updateDatabaseStructure: create index fileid", query);
re = false;
}
commitInternal("update database structure: add fileid col");
}
@ -331,9 +334,9 @@ bool SyncJournalDb::updateDatabaseStructure()
QSqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN remotePerm VARCHAR(128);");
re = re && query.exec();
if(!re) {
qDebug() << Q_FUNC_INFO << "SQL Error " << query.lastError().text();
if( !query.exec()) {
sqlFail("updateDatabaseStructure: add column remotePerm", query);
re = false;
}
commitInternal("update database structure (remotePerm");
}
@ -341,10 +344,9 @@ bool SyncJournalDb::updateDatabaseStructure()
if( 1 ) {
QSqlQuery query(_db);
query.prepare("CREATE INDEX IF NOT EXISTS metadata_inode ON metadata(inode);");
re = re && query.exec();
if(!re) {
qDebug() << Q_FUNC_INFO << "SQL Error " << query.lastError().text();
if( !query.exec()) {
sqlFail("updateDatabaseStructure: create index inode", query);
re = false;
}
commitInternal("update database structure: add inode index");
@ -353,10 +355,9 @@ bool SyncJournalDb::updateDatabaseStructure()
if( 1 ) {
QSqlQuery query(_db);
query.prepare("CREATE INDEX IF NOT EXISTS metadata_pathlen ON metadata(pathlen);");
re = re && query.exec();
if(!re) {
qDebug() << Q_FUNC_INFO << "SQL Error " << query.lastError().text();
if( !query.exec()) {
sqlFail("updateDatabaseStructure: create index pathlen", query);
re = false;
}
commitInternal("update database structure: add pathlen index");
@ -608,6 +609,32 @@ int SyncJournalDb::getFileRecordCount()
return 0;
}
static void toDownloadInfo(const QSqlQuery & query, SyncJournalDb::DownloadInfo * res)
{
bool ok = true;
res->_tmpfile = query.value(0).toString();
res->_etag = query.value(1).toByteArray();
res->_errorCount = query.value(2).toInt(&ok);
res->_valid = ok;
}
static bool deleteBatch(QSqlQuery & query, const QStringList & entries, const QString & name)
{
if (entries.isEmpty())
return true;
qDebug() << "Removing stale " << qPrintable(name) << " entries: " << entries.join(", ");
query.bindValue(0, entries);
if (!query.execBatch()) {
QString err = query.lastError().text();
qDebug() << "Error removing stale " << qPrintable(name) << " entries: "
<< query.lastQuery() << ", Error:" << err;
return false;
}
query.finish();
return true;
}
SyncJournalDb::DownloadInfo SyncJournalDb::getDownloadInfo(const QString& file)
{
QMutexLocker locker(&_mutex);
@ -624,11 +651,7 @@ SyncJournalDb::DownloadInfo SyncJournalDb::getDownloadInfo(const QString& file)
}
if( _getDownloadInfoQuery->next() ) {
bool ok = true;
res._tmpfile = _getDownloadInfoQuery->value(0).toString();
res._etag = _getDownloadInfoQuery->value(1).toByteArray();
res._errorCount = _getDownloadInfoQuery->value(2).toInt(&ok);
res._valid = ok;
toDownloadInfo(*_getDownloadInfoQuery, &res);
}
_getDownloadInfoQuery->finish();
}
@ -669,6 +692,44 @@ void SyncJournalDb::setDownloadInfo(const QString& file, const SyncJournalDb::Do
}
}
QVector<SyncJournalDb::DownloadInfo> SyncJournalDb::getAndDeleteStaleDownloadInfos(const QSet<QString>& keep)
{
QVector<SyncJournalDb::DownloadInfo> empty_result;
QMutexLocker locker(&_mutex);
if (!checkConnect()) {
return empty_result;
}
QSqlQuery query(_db);
// The selected values *must* match the ones expected by toDownloadInfo().
query.prepare("SELECT tmpfile, etag, errorcount, path FROM downloadinfo");
if (!query.exec()) {
QString err = query.lastError().text();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
return empty_result;
}
QStringList superfluousPaths;
QVector<SyncJournalDb::DownloadInfo> deleted_entries;
while (query.next()) {
const QString file = query.value(3).toString(); // path
if (!keep.contains(file)) {
superfluousPaths.append(file);
DownloadInfo info;
toDownloadInfo(query, &info);
deleted_entries.append(info);
}
}
if (!deleteBatch(*_deleteDownloadInfoQuery, superfluousPaths, "downloadinfo"))
return empty_result;
return deleted_entries;
}
SyncJournalDb::UploadInfo SyncJournalDb::getUploadInfo(const QString& file)
{
QMutexLocker locker(&_mutex);
@ -734,6 +795,35 @@ void SyncJournalDb::setUploadInfo(const QString& file, const SyncJournalDb::Uplo
}
}
bool SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
{
QMutexLocker locker(&_mutex);
if (!checkConnect()) {
return false;
}
QSqlQuery query(_db);
query.prepare("SELECT path FROM uploadinfo");
if (!query.exec()) {
QString err = query.lastError().text();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
return false;
}
QStringList superfluousPaths;
while (query.next()) {
const QString file = query.value(0).toString();
if (!keep.contains(file)) {
superfluousPaths.append(file);
}
}
return deleteBatch(*_deleteUploadInfoQuery, superfluousPaths, "uploadinfo");
}
SyncJournalBlacklistRecord SyncJournalDb::blacklistEntry( const QString& file )
{
QMutexLocker locker(&_mutex);
@ -764,6 +854,37 @@ SyncJournalBlacklistRecord SyncJournalDb::blacklistEntry( const QString& file )
return entry;
}
bool SyncJournalDb::deleteStaleBlacklistEntries(const QSet<QString> &keep)
{
QMutexLocker locker(&_mutex);
if (!checkConnect()) {
return false;
}
QSqlQuery query(_db);
query.prepare("SELECT path FROM blacklist");
if (!query.exec()) {
QString err = query.lastError().text();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
return false;
}
QStringList superfluousPaths;
while (query.next()) {
const QString file = query.value(0).toString();
if (!keep.contains(file)) {
superfluousPaths.append(file);
}
}
QSqlQuery delQuery(_db);
delQuery.prepare("DELETE FROM blacklist WHERE path = ?");
return deleteBatch(delQuery, superfluousPaths, "blacklist");
}
int SyncJournalDb::blackListEntryCount()
{
int re = 0;

Просмотреть файл

@ -68,9 +68,15 @@ public:
DownloadInfo getDownloadInfo(const QString &file);
void setDownloadInfo(const QString &file, const DownloadInfo &i);
QVector<DownloadInfo> getAndDeleteStaleDownloadInfos(const QSet<QString>& keep);
UploadInfo getUploadInfo(const QString &file);
void setUploadInfo(const QString &file, const UploadInfo &i);
bool deleteStaleUploadInfos(const QSet<QString>& keep);
SyncJournalBlacklistRecord blacklistEntry( const QString& );
bool deleteStaleBlacklistEntries(const QSet<QString>& keep);
void avoidRenamesOnNextSync(const QString &path);
/**

Просмотреть файл

@ -184,8 +184,7 @@ qint64 Utility::freeDiskSpace(const QString &path, bool *ok)
#elif defined(Q_OS_WIN)
ULARGE_INTEGER freeBytes;
freeBytes.QuadPart = 0L;
QString drive = QDir().absoluteFilePath(path).left(2);
if( !GetDiskFreeSpaceEx( reinterpret_cast<const wchar_t *>(drive.utf16()), &freeBytes, NULL, NULL ) ) {
if( !GetDiskFreeSpaceEx( reinterpret_cast<const wchar_t *>(path.utf16()), &freeBytes, NULL, NULL ) ) {
if (ok) *ok = false;
}
return freeBytes.QuadPart;

Просмотреть файл

@ -26,5 +26,6 @@ if( UNIX AND NOT APPLE )
endif(UNIX AND NOT APPLE)
owncloud_add_test(CSyncSqlite "")
owncloud_add_test(NetrcParser ../src/owncloudcmd/netrcparser.cpp)

76
test/testnetrcparser.h Normal file
Просмотреть файл

@ -0,0 +1,76 @@
/*
* This software is in the public domain, furnished "as is", without technical
* support, and with no warranty, express or implied, as to its usefulness for
* any purpose.
* */
#ifndef MIRALL_INOTIFYWATCHER_H
#define MIRALL_INOTIFYWATCHER_H
#include <QtTest>
#include "owncloudcmd/netrcparser.h"
using namespace Mirall;
namespace {
const char testfileC[] = "netrctest";
const char testfileWithDefaultC[] = "netrctestDefault";
const char testfileEmptyC[] = "netrctestEmpty";
}
class TestNetrcParser : public QObject
{
Q_OBJECT
private slots:
void initTestCase() {
QFile netrc(testfileC);
QVERIFY(netrc.open(QIODevice::WriteOnly));
netrc.write("machine foo login bar password baz\n");
netrc.write("machine broken login bar2 dontbelonghere password baz2 extratokens dontcare andanother\n");
netrc.write("machine\nfunnysplit\tlogin bar3 password baz3\n");
QFile netrcWithDefault(testfileWithDefaultC);
QVERIFY(netrcWithDefault.open(QIODevice::WriteOnly));
netrcWithDefault.write("machine foo login bar password baz\n");
netrcWithDefault.write("default login user password pass\n");
QFile netrcEmpty(testfileEmptyC);
QVERIFY(netrcEmpty.open(QIODevice::WriteOnly));
}
void cleanupTestCase() {
QVERIFY(QFile::remove(testfileC));
QVERIFY(QFile::remove(testfileWithDefaultC));
QVERIFY(QFile::remove(testfileEmptyC));
}
void testValidNetrc() {
NetrcParser parser(testfileC);
QVERIFY(parser.parse());
QCOMPARE(parser.find("foo"), qMakePair(QString("bar"), QString("baz")));
QCOMPARE(parser.find("broken"), qMakePair(QString("bar2"), QString("baz2")));
QCOMPARE(parser.find("funnysplit"), qMakePair(QString("bar3"), QString("baz3")));
}
void testEmptyNetrc() {
NetrcParser parser(testfileEmptyC);
QVERIFY(!parser.parse());
QCOMPARE(parser.find("foo"), qMakePair(QString(), QString()));
}
void testValidNetrcWithDefault() {
NetrcParser parser(testfileWithDefaultC);
QVERIFY(parser.parse());
QCOMPARE(parser.find("foo"), qMakePair(QString("bar"), QString("baz")));
QCOMPARE(parser.find("dontknow"), qMakePair(QString("user"), QString("pass")));
}
void testInvalidNetrc() {
NetrcParser parser("/invalid");
QVERIFY(!parser.parse());
}
};
#endif

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу