зеркало из https://github.com/nextcloud/desktop.git
Merge branch '1.7'
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:
Коммит
50e718b1e7
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
163
src/cmd/cmd.cpp
163
src/cmd/cmd.cpp
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 ©)
|
|||
|
||||
_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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче