Comparar commits
51 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 0249a68420 | |||
| aa6f041c36 | |||
| dce3f8c4f6 | |||
| 084146756b | |||
| e1f5a49c21 | |||
| 143320341e | |||
| e286bb1b64 | |||
| d433f0e08e | |||
| 107149d601 | |||
| fa9b36f829 | |||
| 6775292d63 | |||
| 6835429a28 | |||
| 22135f9f57 | |||
| 0fd06a225c | |||
| 473dcb0947 | |||
| e306f4611c | |||
| c6f4f44619 | |||
| 0865c63745 | |||
| b81c3a6a67 | |||
| cc56e5639d | |||
| 3ce5b358ae | |||
| e968284618 | |||
| c7723179d8 | |||
| 1872f3f94a | |||
| 3b7887ca35 | |||
| d781e63fab | |||
| d1237cdda3 | |||
| 030e3a5d4a | |||
| 4fa4a6a5ed | |||
| 23fb07240b | |||
| 3912dba33a | |||
| d8c479ab1e | |||
| bec67455c1 | |||
| ba6a37a601 | |||
| 03f5091e73 | |||
| 6b6ff08821 | |||
| 35a0ee4893 | |||
| 3519391119 | |||
| 775a1c9ad8 | |||
| ac95844ebd | |||
| c5b90d9507 | |||
| d631f2e070 | |||
| 5bae2ed5ef | |||
| 5af2a657ca | |||
| 71231026a0 | |||
| dfca67b63a | |||
| 7dc6a3b89f | |||
| f985111b62 | |||
| e8d734b1c2 | |||
| a38864fcb4 | |||
| 47bab51474 |
+4
-1
@@ -58,6 +58,9 @@ if( UNIX AND NOT APPLE )
|
||||
endif()
|
||||
####
|
||||
|
||||
# Enable Q_ASSERT etc. in all builds
|
||||
add_definitions( -DQT_FORCE_ASSERTS )
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(DefineInstallationPaths)
|
||||
include(GenerateExportHeader)
|
||||
@@ -69,7 +72,7 @@ get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
# if we cannot get it from git, directly try .tag (packages)
|
||||
# this will work if the tar balls have been properly created
|
||||
# via git-archive.
|
||||
if (${GIT_SHA1} STREQUAL "GITDIR-NOTFOUND")
|
||||
if ("${GIT_SHA1}" STREQUAL "GITDIR-NOTFOUND")
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.tag sha1_candidate)
|
||||
string(REPLACE "\n" "" sha1_candidate ${sha1_candidate})
|
||||
if (NOT ${sha1_candidate} STREQUAL "$Format:%H$")
|
||||
|
||||
@@ -44,11 +44,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct csync_client_certs_s {
|
||||
char *certificatePath;
|
||||
char *certificatePasswd;
|
||||
};
|
||||
|
||||
enum csync_status_codes_e {
|
||||
CSYNC_STATUS_OK = 0,
|
||||
|
||||
|
||||
@@ -103,9 +103,6 @@ struct csync_s {
|
||||
|
||||
} callbacks;
|
||||
c_strlist_t *excludes;
|
||||
|
||||
// needed for SSL client certificate support
|
||||
struct csync_client_certs_s *clientCerts;
|
||||
|
||||
struct {
|
||||
char *file;
|
||||
|
||||
+1
-9
@@ -68,17 +68,9 @@ To set up your build environment for development using HomeBrew_:
|
||||
|
||||
1. Install Xcode
|
||||
2. Install Xcode command line tools::
|
||||
<<<<<<< HEAD
|
||||
xcode-select --install
|
||||
|
||||
3. Install homebrew::
|
||||
=======
|
||||
|
||||
xcode-select --install
|
||||
|
||||
3. Install homebrew::
|
||||
|
||||
>>>>>>> ca9ec4625391ae23940b3a62aaa0afe89f3d98e8
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
|
||||
4. Add the ownCloud repository using the following command::
|
||||
@@ -91,7 +83,7 @@ To set up your build environment for development using HomeBrew_:
|
||||
|
||||
3. Add Qt from brew to the path::
|
||||
|
||||
export PATH=/usr/local/Cellar/qt5/5.x.y/bin/qmake
|
||||
export PATH=/usr/local/Cellar/qt5/5.x.y/bin:$PATH
|
||||
|
||||
Where ``x.z`` is the current version of Qt 5 that brew has installed
|
||||
on your machine.
|
||||
|
||||
@@ -65,7 +65,7 @@ If no client is configured, or if you choose to use a different user to synchron
|
||||
you can specify the user
|
||||
password setting with the usual URL pattern. For example::
|
||||
|
||||
$ owncloudcmd / https://carla:secret@server/owncloud/remote.php/webdav/
|
||||
$ owncloudcmd /home/user/my_sync_folder https://carla:secret@server/owncloud/remote.php/webdav/
|
||||
|
||||
To synchronize the ownCloud directory ``Music`` to the local directory
|
||||
``media/music``, through a proxy listening on port ``8080``, and on a gateway
|
||||
|
||||
@@ -553,6 +553,69 @@ X-GNOME-Autostart-Delay=3
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
GenericName[oc]=Dorsièr de Sincronizacion
|
||||
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file p12topem.cpp
|
||||
* \brief Static library to convert p12 to pem
|
||||
* \author Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
* \version 1.0.0
|
||||
* \date 09 January 2014
|
||||
*/
|
||||
|
||||
#include "p12topem.h"
|
||||
|
||||
/**
|
||||
* \fn string x509ToString (BIO)
|
||||
* \brief Return string from BIO SSL
|
||||
* \param BIO o PEM_write_BIO_...
|
||||
* \return string PEM
|
||||
*/
|
||||
string x509ToString(BIO *o) {
|
||||
BUF_MEM *bptr;
|
||||
BIO_get_mem_ptr(o, &bptr);
|
||||
int len = bptr->length;
|
||||
void* data = calloc(len+10, sizeof(char));
|
||||
BIO_read(o, data, len);
|
||||
string ret = std::string(static_cast<char*>(data));
|
||||
free(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn resultP12ToPem p12ToPem (string, string)
|
||||
* \brief Convert P12 to PEM
|
||||
* \param string p12File Path to P12 file
|
||||
* \param string p12Passwd Password to open P12 file
|
||||
* \return result (bool ReturnCode, Int ErrorCode, String Comment, String PrivateKey, String Certificate)
|
||||
*/
|
||||
resultP12ToPem p12ToPem(string p12File, string p12Passwd) {
|
||||
FILE *fp;
|
||||
PKCS12 *p12 = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
X509 *cert = NULL;
|
||||
STACK_OF(X509) *ca = NULL;
|
||||
|
||||
BIO *o = BIO_new(BIO_s_mem());
|
||||
|
||||
string privateKey = "";
|
||||
string certificate = "";
|
||||
|
||||
resultP12ToPem ret;
|
||||
ret.ReturnCode = false;
|
||||
ret.ErrorCode = 0;
|
||||
ret.Comment = "";
|
||||
ret.PrivateKey = "";
|
||||
ret.Certificate = "";
|
||||
|
||||
SSLeay_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
if(!(fp = fopen(p12File.c_str(), "rb"))) {
|
||||
ret.ErrorCode = 1;
|
||||
ret.Comment = strerror(errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
p12 = d2i_PKCS12_fp(fp, &p12);
|
||||
fclose (fp);
|
||||
|
||||
if (!p12) {
|
||||
ret.ErrorCode = 2;
|
||||
ret.Comment = "Unable to open PKCS#12 file";
|
||||
return ret;
|
||||
}
|
||||
if (!PKCS12_parse(p12, p12Passwd.c_str(), &pkey, &cert, &ca)) {
|
||||
ret.ErrorCode = 3;
|
||||
ret.Comment = "Unable to parse PKCS#12 file (wrong password ?)";
|
||||
return ret;
|
||||
}
|
||||
PKCS12_free(p12);
|
||||
|
||||
if (!(pkey && cert)) {
|
||||
ret.ErrorCode = 4;
|
||||
ret.Comment = "Certificate and/or key file doesn't exists";
|
||||
} else {
|
||||
PEM_write_bio_PrivateKey(o, pkey, 0, 0, 0, NULL, 0);
|
||||
privateKey = x509ToString(o);
|
||||
|
||||
PEM_write_bio_X509(o, cert);
|
||||
certificate = x509ToString(o);
|
||||
|
||||
BIO_free(o);
|
||||
|
||||
ret.ReturnCode = true;
|
||||
ret.ErrorCode = 0;
|
||||
ret.Comment = "All is fine";
|
||||
ret.PrivateKey = privateKey;
|
||||
ret.Certificate = certificate;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef P12TOPEM_H
|
||||
#define P12TOPEM_H
|
||||
|
||||
/**
|
||||
* \file p12topem.h
|
||||
* \brief Static library to convert p12 to pem
|
||||
* \author Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
* \version 1.0.0
|
||||
* \date 09 January 2014
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pkcs12.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* \struct resultP12ToPem p12topem.h
|
||||
*/
|
||||
struct resultP12ToPem {
|
||||
bool ReturnCode;
|
||||
int ErrorCode;
|
||||
string Comment;
|
||||
string PrivateKey;
|
||||
string Certificate;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return string from BIO SSL
|
||||
* \param BIO o PEM_write_BIO_...
|
||||
* \return string PEM
|
||||
*/
|
||||
string x509ToString(BIO *o);
|
||||
|
||||
/**
|
||||
* \brief Convert P12 to PEM
|
||||
* \param string p12File Path to P12 file
|
||||
* \param string p12Passwd Password to open P12 file
|
||||
* \return result (bool ReturnCode, Int ErrorCode, String Comment, String PrivateKey, String Certificate)
|
||||
*/
|
||||
resultP12ToPem p12ToPem(string p12File, string p12Passwd);
|
||||
|
||||
#endif /* P12TOPEM_H */
|
||||
|
||||
+2
-2
@@ -121,7 +121,7 @@ QString queryPassword(const QString &user)
|
||||
class HttpCredentialsText : public HttpCredentials {
|
||||
public:
|
||||
HttpCredentialsText(const QString& user, const QString& password)
|
||||
: HttpCredentials(user, password, "", ""), // FIXME: not working with client certs yet (qknight)
|
||||
: HttpCredentials(user, password), // FIXME: not working with client certs yet (qknight)
|
||||
_sslTrusted(false)
|
||||
{}
|
||||
|
||||
@@ -507,7 +507,7 @@ restart_sync:
|
||||
|
||||
app.exec();
|
||||
|
||||
if (engine.isAnotherSyncNeeded()) {
|
||||
if (engine.isAnotherSyncNeeded() != NoFollowUpSync) {
|
||||
if (restartCount < options.restartTimes) {
|
||||
restartCount++;
|
||||
qDebug() << "Restarting Sync, because another sync is needed" << restartCount;
|
||||
|
||||
@@ -151,7 +151,6 @@ set(3rdparty_SRC
|
||||
../3rdparty/qtsingleapplication/qtlocalpeer.cpp
|
||||
../3rdparty/qtsingleapplication/qtsingleapplication.cpp
|
||||
../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
|
||||
../3rdparty/certificates/p12topem.cpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
||||
@@ -214,6 +214,17 @@ AccountPtr AccountManager::loadAccountHelper(QSettings& settings)
|
||||
auto acc = createAccount();
|
||||
|
||||
QString authType = settings.value(QLatin1String(authTypeC)).toString();
|
||||
|
||||
// There was an account-type saving bug when 'skip folder config' was used
|
||||
// See #5408. This attempts to fix up the "dummy" authType
|
||||
if (authType == QLatin1String("dummy")) {
|
||||
if (settings.contains(QLatin1String("http_user"))) {
|
||||
authType = "http";
|
||||
} else if (settings.contains(QLatin1String("shibboleth_shib_user"))) {
|
||||
authType = "shibboleth";
|
||||
}
|
||||
}
|
||||
|
||||
QString overrideUrl = Theme::instance()->overrideServerUrl();
|
||||
QString forceAuth = Theme::instance()->forceConfigAuthType();
|
||||
if(!forceAuth.isEmpty() && !overrideUrl.isEmpty() ) {
|
||||
@@ -237,7 +248,7 @@ AccountPtr AccountManager::loadAccountHelper(QSettings& settings)
|
||||
|
||||
acc->setCredentials(CredentialsFactory::create(authType));
|
||||
|
||||
// now the cert, it is in the general group
|
||||
// now the server cert, it is in the general group
|
||||
settings.beginGroup(QLatin1String("General"));
|
||||
acc->setApprovedCerts(QSslCertificate::fromData(settings.value(caCertsKeyC).toByteArray()));
|
||||
settings.endGroup();
|
||||
|
||||
@@ -214,8 +214,10 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||
}
|
||||
|
||||
tv->setCurrentIndex(index);
|
||||
QString alias = _model->data( index, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
bool folderPaused = _model->data( index, FolderStatusDelegate::FolderSyncPaused).toBool();
|
||||
bool folderConnected = _model->data( index, FolderStatusDelegate::FolderAccountConnected ).toBool();
|
||||
auto folderMan = FolderMan::instance();
|
||||
|
||||
QMenu *menu = new QMenu(tv);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
@@ -231,6 +233,10 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||
|
||||
if (!folderPaused) {
|
||||
ac = menu->addAction(tr("Force sync now"));
|
||||
if (folderMan->currentSyncFolder() == folderMan->folder(alias)) {
|
||||
ac->setText(tr("Restart sync"));
|
||||
}
|
||||
ac->setEnabled(folderConnected);
|
||||
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotForceSyncCurrentFolder()));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>462</width>
|
||||
<height>186</height>
|
||||
<height>188</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -32,7 +32,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelCertificateFile">
|
||||
<property name="text">
|
||||
<string>Certificate :</string>
|
||||
<string>Certificate & Key (pkcs12) :</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -347,14 +347,6 @@ void Application::slotownCloudWizardDone( int res )
|
||||
}
|
||||
}
|
||||
|
||||
static void csyncLogCatcher(int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
void Application::setupLogging()
|
||||
{
|
||||
// might be called from second instance
|
||||
@@ -370,10 +362,6 @@ void Application::setupLogging()
|
||||
.arg(property("ui_lang").toString())
|
||||
.arg(_theme->version())
|
||||
.arg(Utility::platformName());
|
||||
|
||||
// Setup CSYNC logging to forward to our own logger
|
||||
csync_set_log_callback( csyncLogCatcher );
|
||||
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
|
||||
}
|
||||
|
||||
void Application::slotUseMonoIconsChanged(bool)
|
||||
|
||||
@@ -27,7 +27,7 @@ class HttpCredentialsGui : public HttpCredentials {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HttpCredentialsGui() : HttpCredentials() {}
|
||||
HttpCredentialsGui(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd) : HttpCredentials(user, password, certificatePath, certificatePasswd) {}
|
||||
HttpCredentialsGui(const QString& user, const QString& password, const QSslCertificate& certificate, const QSslKey& key) : HttpCredentials(user, password, certificate, key) {}
|
||||
void askFromUser() Q_DECL_OVERRIDE;
|
||||
Q_INVOKABLE void askFromUserAsync();
|
||||
|
||||
|
||||
@@ -65,9 +65,6 @@ Folder::Folder(const FolderDefinition& definition,
|
||||
, _fileLog(new SyncRunFileLog)
|
||||
, _saveBackwardsCompatible(false)
|
||||
{
|
||||
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
|
||||
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
|
||||
|
||||
qsrand(QTime::currentTime().msec());
|
||||
_timeSinceLastSyncStart.start();
|
||||
_timeSinceLastSyncDone.start();
|
||||
@@ -826,7 +823,7 @@ void Folder::slotSyncFinished(bool success)
|
||||
_fileLog->finish();
|
||||
bubbleUpSyncResult();
|
||||
|
||||
bool anotherSyncNeeded = _engine->isAnotherSyncNeeded();
|
||||
auto anotherSyncNeeded = _engine->isAnotherSyncNeeded();
|
||||
|
||||
if (_csyncError) {
|
||||
_syncResult.setStatus(SyncResult::Error);
|
||||
@@ -877,7 +874,7 @@ void Folder::slotSyncFinished(bool success)
|
||||
_timeSinceLastSyncDone.restart();
|
||||
|
||||
// Increment the follow-up sync counter if necessary.
|
||||
if (anotherSyncNeeded) {
|
||||
if (anotherSyncNeeded == ImmediateFollowUp) {
|
||||
_consecutiveFollowUpSyncs++;
|
||||
qDebug() << "another sync was requested by the finished sync, this has"
|
||||
<< "happened" << _consecutiveFollowUpSyncs << "times";
|
||||
@@ -886,7 +883,7 @@ void Folder::slotSyncFinished(bool success)
|
||||
}
|
||||
|
||||
// Maybe force a follow-up sync to take place, but only a couple of times.
|
||||
if (anotherSyncNeeded && _consecutiveFollowUpSyncs <= 3)
|
||||
if (anotherSyncNeeded == ImmediateFollowUp && _consecutiveFollowUpSyncs <= 3)
|
||||
{
|
||||
// Sometimes another sync is requested because a local file is still
|
||||
// changing, so wait at least a small amount of time before syncing
|
||||
|
||||
+17
-10
@@ -148,6 +148,7 @@ int FolderMan::unloadAndDeleteAllFolders()
|
||||
void FolderMan::registerFolderMonitor( Folder *folder )
|
||||
{
|
||||
if( !folder ) return;
|
||||
if( !QDir(folder->path()).exists() ) return;
|
||||
|
||||
if( !_folderWatchers.contains(folder->alias() ) ) {
|
||||
FolderWatcher *fw = new FolderWatcher(folder->path(), folder);
|
||||
@@ -730,6 +731,10 @@ void FolderMan::slotStartScheduledFolderSync()
|
||||
|
||||
// Start syncing this folder!
|
||||
if( folder ) {
|
||||
// Safe to call several times, and necessary to try again if
|
||||
// the folder path didn't exist previously.
|
||||
registerFolderMonitor(folder);
|
||||
|
||||
_currentSyncFolder = folder;
|
||||
folder->startSync( QStringList() );
|
||||
}
|
||||
@@ -833,18 +838,20 @@ void FolderMan::slotScheduleFolderByTime()
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retry a couple of times after failure
|
||||
bool syncAgainAfterFail = f->consecutiveFailingSyncs() > 0 && f->consecutiveFailingSyncs() < 3;
|
||||
qint64 syncAgainAfterFailDelay = 10 * 1000; // 10s for the first retry-after-fail
|
||||
// Retry a couple of times after failure; or regularly if requested
|
||||
bool syncAgain =
|
||||
(f->consecutiveFailingSyncs() > 0 && f->consecutiveFailingSyncs() < 3)
|
||||
|| f->syncEngine().isAnotherSyncNeeded() == DelayedFollowUp;
|
||||
qint64 syncAgainDelay = 10 * 1000; // 10s for the first retry-after-fail
|
||||
if (f->consecutiveFailingSyncs() > 1)
|
||||
syncAgainAfterFailDelay = 60 * 1000; // 60s for each further attempt
|
||||
if (syncAgainAfterFail
|
||||
&& msecsSinceSync > syncAgainAfterFailDelay) {
|
||||
syncAgainDelay = 60 * 1000; // 60s for each further attempt
|
||||
if (syncAgain
|
||||
&& msecsSinceSync > syncAgainDelay) {
|
||||
qDebug() << "** scheduling folder" << f->alias()
|
||||
<< "because the last"
|
||||
<< f->consecutiveFailingSyncs() << "syncs failed, last status:"
|
||||
<< f->syncResult().statusString()
|
||||
<< "time since last sync:" << msecsSinceSync;
|
||||
<< ", the last" << f->consecutiveFailingSyncs() << "syncs failed"
|
||||
<< ", anotherSyncNeeded" << f->syncEngine().isAnotherSyncNeeded()
|
||||
<< ", last status:" << f->syncResult().statusString()
|
||||
<< ", time since last sync:" << msecsSinceSync;
|
||||
|
||||
scheduleFolder(f);
|
||||
continue;
|
||||
|
||||
@@ -527,10 +527,8 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
|
||||
|
||||
if (!info || info->_fetched || info->_fetching)
|
||||
return;
|
||||
|
||||
info->_hasError = false;
|
||||
info->resetSubs(this, parent);
|
||||
info->_fetching = true;
|
||||
info->_fetchingLabel = false;
|
||||
QString path = info->_folder->remotePath();
|
||||
if (info->_path != QLatin1String("/")) {
|
||||
if (!path.endsWith(QLatin1Char('/'))) {
|
||||
@@ -698,17 +696,16 @@ void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
|
||||
qDebug() << r->errorString();
|
||||
parentInfo->_lastErrorString = r->errorString();
|
||||
|
||||
parentInfo->resetSubs(this, idx);
|
||||
|
||||
if (r->error() == QNetworkReply::ContentNotFoundError) {
|
||||
parentInfo->_fetched = true;
|
||||
} else {
|
||||
if (!parentInfo->hasLabel()) {
|
||||
beginInsertRows(idx, 0, 0);
|
||||
endInsertRows();
|
||||
}
|
||||
Q_ASSERT(!parentInfo->hasLabel());
|
||||
beginInsertRows(idx, 0, 0);
|
||||
parentInfo->_hasError = true;
|
||||
endInsertRows();
|
||||
}
|
||||
parentInfo->_fetching = false;
|
||||
parentInfo->_fetchingLabel = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -482,9 +482,8 @@ void FolderWizardRemotePath::showWarn( const QString& msg ) const
|
||||
FolderWizardSelectiveSync::FolderWizardSelectiveSync(const AccountPtr& account)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
_treeView = new SelectiveSyncTreeView(account, this);
|
||||
layout->addWidget(new QLabel(tr("Choose What to Sync: You can optionally deselect remote subfolders you do not wish to synchronize.")));
|
||||
layout->addWidget(_treeView);
|
||||
_selectiveSync = new SelectiveSyncWidget(account, this);
|
||||
layout->addWidget(_selectiveSync);
|
||||
}
|
||||
|
||||
FolderWizardSelectiveSync::~FolderWizardSelectiveSync()
|
||||
@@ -501,13 +500,17 @@ void FolderWizardSelectiveSync::initializePage()
|
||||
QString alias = QFileInfo(targetPath).fileName();
|
||||
if (alias.isEmpty())
|
||||
alias = Theme::instance()->appName();
|
||||
_treeView->setFolderInfo(targetPath, alias);
|
||||
QStringList initialBlacklist;
|
||||
if (Theme::instance()->wizardSelectiveSyncDefaultNothing()) {
|
||||
initialBlacklist = QStringList("/");
|
||||
}
|
||||
_selectiveSync->setFolderInfo(targetPath, alias, initialBlacklist);
|
||||
QWizardPage::initializePage();
|
||||
}
|
||||
|
||||
bool FolderWizardSelectiveSync::validatePage()
|
||||
{
|
||||
wizard()->setProperty("selectiveSyncBlackList", QVariant(_treeView->createBlackList()));
|
||||
wizard()->setProperty("selectiveSyncBlackList", QVariant(_selectiveSync->createBlackList()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -517,7 +520,7 @@ void FolderWizardSelectiveSync::cleanupPage()
|
||||
QString alias = QFileInfo(targetPath).fileName();
|
||||
if (alias.isEmpty())
|
||||
alias = Theme::instance()->appName();
|
||||
_treeView->setFolderInfo(targetPath, alias);
|
||||
_selectiveSync->setFolderInfo(targetPath, alias);
|
||||
QWizardPage::cleanupPage();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class SelectiveSyncTreeView;
|
||||
class SelectiveSyncWidget;
|
||||
|
||||
class ownCloudInfo;
|
||||
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
virtual void cleanupPage() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
SelectiveSyncTreeView *_treeView;
|
||||
SelectiveSyncWidget *_selectiveSync;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -110,8 +110,9 @@ LogBrowser::LogBrowser(QWidget *parent) :
|
||||
|
||||
setModal(false);
|
||||
|
||||
Logger::instance()->setLogWindowActivated(true);
|
||||
// Direct connection for log coming from this thread, and queued for the one in a different thread
|
||||
connect(Logger::instance(), SIGNAL(newLog(QString)),this,SLOT(slotNewLog(QString)), Qt::AutoConnection);
|
||||
connect(Logger::instance(), SIGNAL(logWindowLog(QString)),this,SLOT(slotNewLog(QString)), Qt::AutoConnection);
|
||||
|
||||
QAction *showLogWindow = new QAction(this);
|
||||
showLogWindow->setShortcut(QKeySequence("F12"));
|
||||
|
||||
@@ -207,10 +207,15 @@ void OwncloudSetupWizard::slotNoOwnCloudFoundAuth(QNetworkReply *reply)
|
||||
QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
|
||||
|
||||
// Do this early because reply might be deleted in message box event loop
|
||||
QString msg = tr("Failed to connect to %1 at %2:<br/>%3")
|
||||
QString msg;
|
||||
if (!_ocWizard->account()->url().isValid()) {
|
||||
msg = tr("Invalid URL");
|
||||
} else {
|
||||
msg = tr("Failed to connect to %1 at %2:<br/>%3")
|
||||
.arg(Theme::instance()->appNameGUI(),
|
||||
reply->url().toString(),
|
||||
reply->errorString());
|
||||
}
|
||||
bool isDowngradeAdvised = checkDowngradeAdvised(reply);
|
||||
|
||||
// If a client cert is needed, nginx sends:
|
||||
@@ -553,6 +558,13 @@ void OwncloudSetupWizard::slotSkipFolderConfiguration()
|
||||
AccountState *OwncloudSetupWizard::applyAccountChanges()
|
||||
{
|
||||
AccountPtr newAccount = _ocWizard->account();
|
||||
|
||||
// Detach the account that is going to be saved from the
|
||||
// wizard to ensure it doesn't accidentally get modified
|
||||
// later (such as from running cleanup such as
|
||||
// AbstractCredentialsWizardPage::cleanupPage())
|
||||
_ocWizard->setAccount(AccountManager::createAccount());
|
||||
|
||||
auto manager = AccountManager::instance();
|
||||
|
||||
auto newState = manager->addAccount(newAccount);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "networkjobs.h"
|
||||
#include "theme.h"
|
||||
#include "folderman.h"
|
||||
#include "configfile.h"
|
||||
#include <QDialogButtonBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QTreeWidget>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <QScopedValueRollback>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -54,32 +56,48 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
SelectiveSyncTreeView::SelectiveSyncTreeView(AccountPtr account, QWidget* parent)
|
||||
: QTreeWidget(parent), _inserting(false), _account(account)
|
||||
SelectiveSyncWidget::SelectiveSyncWidget(AccountPtr account, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, _account(account)
|
||||
, _inserting(false)
|
||||
, _folderTree(new QTreeWidget(this))
|
||||
{
|
||||
_loading = new QLabel(tr("Loading ..."), this);
|
||||
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
||||
setSortingEnabled(true);
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
setColumnCount(2);
|
||||
_loading = new QLabel(tr("Loading ..."), _folderTree);
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto header = new QLabel(this);
|
||||
header->setText(tr("Deselect remote folders you do not wish to synchronize."));
|
||||
header->setWordWrap(true);
|
||||
layout->addWidget(header);
|
||||
|
||||
layout->addWidget(_folderTree);
|
||||
|
||||
connect(_folderTree, SIGNAL(itemExpanded(QTreeWidgetItem*)),
|
||||
SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
connect(_folderTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
||||
_folderTree->setSortingEnabled(true);
|
||||
_folderTree->sortByColumn(0, Qt::AscendingOrder);
|
||||
_folderTree->setColumnCount(2);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
header()->setSectionResizeMode(0, QHeaderView::QHeaderView::ResizeToContents);
|
||||
header()->setSectionResizeMode(1, QHeaderView::QHeaderView::ResizeToContents);
|
||||
_folderTree->header()->setSectionResizeMode(0, QHeaderView::QHeaderView::ResizeToContents);
|
||||
_folderTree->header()->setSectionResizeMode(1, QHeaderView::QHeaderView::ResizeToContents);
|
||||
#else
|
||||
header()->resizeSection(0, sizeHint().width()/2);
|
||||
_folderTree->header()->resizeSection(0, sizeHint().width()/2);
|
||||
#endif
|
||||
header()->setStretchLastSection(true);
|
||||
headerItem()->setText(0, tr("Name"));
|
||||
headerItem()->setText(1, tr("Size"));
|
||||
_folderTree->header()->setStretchLastSection(true);
|
||||
_folderTree->headerItem()->setText(0, tr("Name"));
|
||||
_folderTree->headerItem()->setText(1, tr("Size"));
|
||||
}
|
||||
|
||||
QSize SelectiveSyncTreeView::sizeHint() const
|
||||
QSize SelectiveSyncWidget::sizeHint() const
|
||||
{
|
||||
return QTreeView::sizeHint().expandedTo(QSize(400, 400));
|
||||
return QWidget::sizeHint().expandedTo(QSize(600, 600));
|
||||
}
|
||||
|
||||
void SelectiveSyncTreeView::refreshFolders()
|
||||
void SelectiveSyncWidget::refreshFolders()
|
||||
{
|
||||
LsColJob *job = new LsColJob(_account, _folderPath, this);
|
||||
job->setProperties(QList<QByteArray>() << "resourcetype" << "http://owncloud.org/ns:size");
|
||||
@@ -88,12 +106,12 @@ void SelectiveSyncTreeView::refreshFolders()
|
||||
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
|
||||
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
|
||||
job->start();
|
||||
clear();
|
||||
_folderTree->clear();
|
||||
_loading->show();
|
||||
_loading->move(10,header()->height() + 10);
|
||||
_loading->move(10, _folderTree->header()->height() + 10);
|
||||
}
|
||||
|
||||
void SelectiveSyncTreeView::setFolderInfo(const QString& folderPath, const QString& rootName, const QStringList& oldBlackList)
|
||||
void SelectiveSyncWidget::setFolderInfo(const QString& folderPath, const QString& rootName, const QStringList& oldBlackList)
|
||||
{
|
||||
_folderPath = folderPath;
|
||||
if (_folderPath.startsWith(QLatin1Char('/'))) {
|
||||
@@ -116,7 +134,7 @@ static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& t
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size)
|
||||
void SelectiveSyncWidget::recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
@@ -159,13 +177,13 @@ void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList
|
||||
}
|
||||
}
|
||||
|
||||
void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
|
||||
void SelectiveSyncWidget::slotUpdateDirectories(QStringList list)
|
||||
{
|
||||
auto job = qobject_cast<LsColJob *>(sender());
|
||||
QScopedValueRollback<bool> isInserting(_inserting);
|
||||
_inserting = true;
|
||||
|
||||
SelectiveSyncTreeViewItem *root = static_cast<SelectiveSyncTreeViewItem*>(topLevelItem(0));
|
||||
SelectiveSyncTreeViewItem *root = static_cast<SelectiveSyncTreeViewItem*>(_folderTree->topLevelItem(0));
|
||||
|
||||
QUrl url = _account->davUrl();
|
||||
QString pathToRemove = url.path();
|
||||
@@ -206,15 +224,11 @@ void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
root = new SelectiveSyncTreeViewItem(this);
|
||||
root = new SelectiveSyncTreeViewItem(_folderTree);
|
||||
root->setText(0, _rootName);
|
||||
root->setIcon(0, Theme::instance()->applicationIcon());
|
||||
root->setData(0, Qt::UserRole, QString());
|
||||
if (_oldBlackList.isEmpty()) {
|
||||
root->setCheckState(0, Qt::Checked);
|
||||
} else {
|
||||
root->setCheckState(0, Qt::PartiallyChecked);
|
||||
}
|
||||
root->setCheckState(0, Qt::Checked);
|
||||
qint64 size = job ? job->_sizes.value(pathToRemove, -1) : -1;
|
||||
if (size >= 0) {
|
||||
root->setText(1, Utility::octetsToString(size));
|
||||
@@ -236,10 +250,19 @@ void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
|
||||
recursiveInsert(root, paths, path, size);
|
||||
}
|
||||
|
||||
// Root is partially checked if any children are not checked
|
||||
for (int i = 0; i < root->childCount(); ++i) {
|
||||
const auto child = root->child(i);
|
||||
if (child->checkState(0) != Qt::Checked) {
|
||||
root->setCheckState(0, Qt::PartiallyChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
root->setExpanded(true);
|
||||
}
|
||||
|
||||
void SelectiveSyncTreeView::slotLscolFinishedWithError(QNetworkReply *r)
|
||||
void SelectiveSyncWidget::slotLscolFinishedWithError(QNetworkReply *r)
|
||||
{
|
||||
if (r->error() == QNetworkReply::ContentNotFoundError) {
|
||||
_loading->setText(tr("No subfolders currently on the server."));
|
||||
@@ -249,7 +272,7 @@ void SelectiveSyncTreeView::slotLscolFinishedWithError(QNetworkReply *r)
|
||||
_loading->resize(_loading->sizeHint()); // because it's not in a layout
|
||||
}
|
||||
|
||||
void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item)
|
||||
void SelectiveSyncWidget::slotItemExpanded(QTreeWidgetItem *item)
|
||||
{
|
||||
QString dir = item->data(0, Qt::UserRole).toString();
|
||||
if (dir.isEmpty()) return;
|
||||
@@ -264,7 +287,7 @@ void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item)
|
||||
job->start();
|
||||
}
|
||||
|
||||
void SelectiveSyncTreeView::slotItemChanged(QTreeWidgetItem *item, int col)
|
||||
void SelectiveSyncWidget::slotItemChanged(QTreeWidgetItem *item, int col)
|
||||
{
|
||||
if (col != 0 || _inserting)
|
||||
return;
|
||||
@@ -322,10 +345,10 @@ void SelectiveSyncTreeView::slotItemChanged(QTreeWidgetItem *item, int col)
|
||||
}
|
||||
}
|
||||
|
||||
QStringList SelectiveSyncTreeView::createBlackList(QTreeWidgetItem* root) const
|
||||
QStringList SelectiveSyncWidget::createBlackList(QTreeWidgetItem* root) const
|
||||
{
|
||||
if (!root) {
|
||||
root = topLevelItem(0);
|
||||
root = _folderTree->topLevelItem(0);
|
||||
}
|
||||
if (!root) return QStringList();
|
||||
|
||||
@@ -354,15 +377,15 @@ QStringList SelectiveSyncTreeView::createBlackList(QTreeWidgetItem* root) const
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList SelectiveSyncTreeView::oldBlackList() const
|
||||
QStringList SelectiveSyncWidget::oldBlackList() const
|
||||
{
|
||||
return _oldBlackList;
|
||||
}
|
||||
|
||||
qint64 SelectiveSyncTreeView::estimatedSize(QTreeWidgetItem* root)
|
||||
qint64 SelectiveSyncWidget::estimatedSize(QTreeWidgetItem* root)
|
||||
{
|
||||
if (!root) {
|
||||
root = topLevelItem(0);
|
||||
root = _folderTree->topLevelItem(0);
|
||||
}
|
||||
if (!root) return -1;
|
||||
|
||||
@@ -396,10 +419,10 @@ SelectiveSyncDialog::SelectiveSyncDialog(AccountPtr account, Folder* folder, QWi
|
||||
_okButton(0) // defined in init()
|
||||
{
|
||||
bool ok;
|
||||
init(account, tr("Unchecked folders will be <b>removed</b> from your local file system and will not be synchronized to this computer anymore"));
|
||||
init(account);
|
||||
QStringList selectiveSyncList = _folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok);
|
||||
if( ok ) {
|
||||
_treeView->setFolderInfo(_folder->remotePath(), _folder->alias(),selectiveSyncList);
|
||||
_selectiveSync->setFolderInfo(_folder->remotePath(), _folder->alias(),selectiveSyncList);
|
||||
} else {
|
||||
_okButton->setEnabled(false);
|
||||
}
|
||||
@@ -411,22 +434,16 @@ SelectiveSyncDialog::SelectiveSyncDialog(AccountPtr account, const QString &fold
|
||||
const QStringList& blacklist, QWidget* parent, Qt::WindowFlags f)
|
||||
: QDialog(parent, f), _folder(0)
|
||||
{
|
||||
init(account,
|
||||
Theme::instance()->wizardSelectiveSyncDefaultNothing() ?
|
||||
tr("Choose What to Sync: Select remote subfolders you wish to synchronize.") :
|
||||
tr("Choose What to Sync: Deselect remote subfolders you do not wish to synchronize."));
|
||||
_treeView->setFolderInfo(folder, folder, blacklist);
|
||||
init(account);
|
||||
_selectiveSync->setFolderInfo(folder, folder, blacklist);
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::init(const AccountPtr &account, const QString &labelText)
|
||||
void SelectiveSyncDialog::init(const AccountPtr &account)
|
||||
{
|
||||
setWindowTitle(tr("Choose What to Sync"));
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
_treeView = new SelectiveSyncTreeView(account, this);
|
||||
auto label = new QLabel(labelText);
|
||||
label->setWordWrap(true);
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(_treeView);
|
||||
_selectiveSync = new SelectiveSyncWidget(account, this);
|
||||
layout->addWidget(_selectiveSync);
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal);
|
||||
_okButton = buttonBox->addButton(QDialogButtonBox::Ok);
|
||||
connect(_okButton, SIGNAL(clicked()), this, SLOT(accept()));
|
||||
@@ -444,7 +461,7 @@ void SelectiveSyncDialog::accept()
|
||||
if( ! ok ) {
|
||||
return;
|
||||
}
|
||||
QStringList blackList = _treeView->createBlackList();
|
||||
QStringList blackList = _selectiveSync->createBlackList();
|
||||
_folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
|
||||
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
@@ -467,19 +484,18 @@ void SelectiveSyncDialog::accept()
|
||||
|
||||
QStringList SelectiveSyncDialog::createBlackList() const
|
||||
{
|
||||
return _treeView->createBlackList();
|
||||
return _selectiveSync->createBlackList();
|
||||
}
|
||||
|
||||
QStringList SelectiveSyncDialog::oldBlackList() const
|
||||
{
|
||||
return _treeView->oldBlackList();
|
||||
return _selectiveSync->oldBlackList();
|
||||
}
|
||||
|
||||
qint64 SelectiveSyncDialog::estimatedSize()
|
||||
{
|
||||
return _treeView->estimatedSize();
|
||||
return _selectiveSync->estimatedSize();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,40 +26,50 @@ namespace OCC {
|
||||
class Folder;
|
||||
|
||||
/**
|
||||
* @brief The SelectiveSyncTreeView class
|
||||
* @brief The SelectiveSyncWidget contains a folder tree with labels
|
||||
* @ingroup gui
|
||||
*/
|
||||
class SelectiveSyncTreeView : public QTreeWidget {
|
||||
class SelectiveSyncWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SelectiveSyncTreeView(AccountPtr account, QWidget* parent = 0);
|
||||
explicit SelectiveSyncWidget(AccountPtr account, QWidget* parent = 0);
|
||||
|
||||
/// Returns a list of blacklisted paths, each including the trailing /
|
||||
QStringList createBlackList(QTreeWidgetItem* root = 0) const;
|
||||
|
||||
/** Returns the oldBlackList passed into setFolderInfo(), except that
|
||||
* a "/" entry is expanded to all top-level folder names.
|
||||
*/
|
||||
QStringList oldBlackList() const;
|
||||
|
||||
// Estimates the total size of checked items (recursively)
|
||||
qint64 estimatedSize(QTreeWidgetItem *root = 0);
|
||||
void refreshFolders();
|
||||
|
||||
// oldBlackList is a list of excluded paths, each including a trailing /
|
||||
void setFolderInfo(const QString &folderPath, const QString &rootName,
|
||||
const QStringList &oldBlackList = QStringList());
|
||||
|
||||
QSize sizeHint() const Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void slotUpdateDirectories(QStringList);
|
||||
void slotItemExpanded(QTreeWidgetItem *);
|
||||
void slotItemChanged(QTreeWidgetItem*,int);
|
||||
void slotLscolFinishedWithError(QNetworkReply*);
|
||||
private:
|
||||
void refreshFolders();
|
||||
void recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size);
|
||||
|
||||
AccountPtr _account;
|
||||
|
||||
QString _folderPath;
|
||||
QString _rootName;
|
||||
QStringList _oldBlackList;
|
||||
|
||||
bool _inserting; // set to true when we are inserting new items on the list
|
||||
AccountPtr _account;
|
||||
QLabel *_loading;
|
||||
|
||||
QTreeWidget *_folderTree;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -85,9 +95,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void init(const AccountPtr &account, const QString &label);
|
||||
void init(const AccountPtr &account);
|
||||
|
||||
SelectiveSyncTreeView *_treeView;
|
||||
SelectiveSyncWidget *_selectiveSync;
|
||||
|
||||
Folder *_folder;
|
||||
QPushButton *_okButton;
|
||||
|
||||
@@ -29,8 +29,11 @@ OwncloudConnectionMethodDialog::OwncloudConnectionMethodDialog(QWidget *parent)
|
||||
connect(ui->btnClientSideTLS, SIGNAL(clicked(bool)), this, SLOT(returnClientSideTLS()));
|
||||
connect(ui->btnBack, SIGNAL(clicked(bool)), this, SLOT(returnBack()));
|
||||
|
||||
// DM: TLS Client Cert GUI support disabled for now
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 0)
|
||||
// We support only from Qt 5.4.x because of https://doc.qt.io/qt-5/qsslcertificate.html#importPkcs12
|
||||
ui->btnClientSideTLS->hide();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OwncloudConnectionMethodDialog::setUrl(const QUrl &url)
|
||||
|
||||
@@ -192,7 +192,7 @@ void OwncloudHttpCredsPage::setErrorString(const QString& err)
|
||||
|
||||
AbstractCredentials* OwncloudHttpCredsPage::getCredentials() const
|
||||
{
|
||||
return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text(), _ocWizard->ownCloudCertificatePath, _ocWizard->ownCloudCertificatePasswd);
|
||||
return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text(), _ocWizard->_clientSslCertificate, _ocWizard->_clientSslKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
#include <QMessageBox>
|
||||
#include <QSsl>
|
||||
#include <QSslCertificate>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
#include "QProgressIndicator.h"
|
||||
|
||||
#include "wizard/owncloudwizardcommon.h"
|
||||
#include "wizard/owncloudsetuppage.h"
|
||||
#include "wizard/owncloudconnectionmethoddialog.h"
|
||||
#include "../3rdparty/certificates/p12topem.h"
|
||||
#include "theme.h"
|
||||
#include "account.h"
|
||||
|
||||
@@ -71,7 +71,6 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent)
|
||||
connect(_ui.leUrl, SIGNAL(editingFinished()), SLOT(slotUrlEditFinished()));
|
||||
|
||||
addCertDial = new AddCertificateDialog(this);
|
||||
connect(_ocWizard,SIGNAL(needCertificate()),this,SLOT(slotAskSSLClientCertificate()));
|
||||
}
|
||||
|
||||
void OwncloudSetupPage::setServerUrl( const QString& newUrl )
|
||||
@@ -139,7 +138,7 @@ void OwncloudSetupPage::slotUrlChanged(const QString& url)
|
||||
void OwncloudSetupPage::slotUrlEditFinished()
|
||||
{
|
||||
QString url = _ui.leUrl->fullText();
|
||||
if (QUrl(url).isRelative()) {
|
||||
if (QUrl(url).isRelative() && !url.isEmpty()) {
|
||||
// no scheme defined, set one
|
||||
url.prepend("https://");
|
||||
}
|
||||
@@ -269,7 +268,10 @@ void OwncloudSetupPage::setErrorString( const QString& err, bool retryHTTPonly )
|
||||
}
|
||||
break;
|
||||
case OwncloudConnectionMethodDialog::Client_Side_TLS:
|
||||
slotAskSSLClientCertificate();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
|
||||
addCertDial->show();
|
||||
connect(addCertDial, SIGNAL(accepted()),this,SLOT(slotCertificateAccepted()));
|
||||
#endif
|
||||
break;
|
||||
case OwncloudConnectionMethodDialog::Closed:
|
||||
case OwncloudConnectionMethodDialog::Back:
|
||||
@@ -302,12 +304,6 @@ void OwncloudSetupPage::stopSpinner()
|
||||
_progressIndi->stopAnimation();
|
||||
}
|
||||
|
||||
void OwncloudSetupPage::slotAskSSLClientCertificate()
|
||||
{
|
||||
addCertDial->show();
|
||||
connect(addCertDial, SIGNAL(accepted()),this,SLOT(slotCertificateAccepted()));
|
||||
}
|
||||
|
||||
QString subjectInfoHelper(const QSslCertificate& cert, const QByteArray &qa)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||
@@ -320,36 +316,40 @@ QString subjectInfoHelper(const QSslCertificate& cert, const QByteArray &qa)
|
||||
//called during the validation of the client certificate.
|
||||
void OwncloudSetupPage::slotCertificateAccepted()
|
||||
{
|
||||
QSslCertificate sslCertificate;
|
||||
|
||||
resultP12ToPem certif = p12ToPem(addCertDial->getCertificatePath().toStdString() , addCertDial->getCertificatePasswd().toStdString());
|
||||
if(certif.ReturnCode){
|
||||
QString s = QString::fromStdString(certif.Certificate);
|
||||
QByteArray ba = s.toLocal8Bit();
|
||||
|
||||
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(ba, QSsl::Pem);
|
||||
sslCertificate = sslCertificateList.takeAt(0);
|
||||
|
||||
_ocWizard->ownCloudCertificate = ba;
|
||||
_ocWizard->ownCloudPrivateKey = certif.PrivateKey.c_str();
|
||||
_ocWizard->ownCloudCertificatePath = addCertDial->getCertificatePath();
|
||||
_ocWizard->ownCloudCertificatePasswd = addCertDial->getCertificatePasswd();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
|
||||
QList<QSslCertificate> clientCaCertificates;
|
||||
QFile certFile(addCertDial->getCertificatePath());
|
||||
certFile.open(QFile::ReadOnly);
|
||||
if(QSslCertificate::importPkcs12(&certFile,
|
||||
&_ocWizard->_clientSslKey, &_ocWizard->_clientSslCertificate,
|
||||
&clientCaCertificates,
|
||||
addCertDial->getCertificatePasswd().toLocal8Bit())){
|
||||
AccountPtr acc = _ocWizard->account();
|
||||
acc->setCertificate(_ocWizard->ownCloudCertificate, _ocWizard->ownCloudPrivateKey);
|
||||
addCertDial->reinit();
|
||||
|
||||
// to re-create the session ticket because we added a key/cert
|
||||
acc->setSslConfiguration(QSslConfiguration());
|
||||
QSslConfiguration sslConfiguration = acc->getOrCreateSslConfig();
|
||||
|
||||
// We're stuffing the certificate into the configuration form here. Later the
|
||||
// cert will come via the HttpCredentials
|
||||
sslConfiguration.setLocalCertificate(_ocWizard->_clientSslCertificate);
|
||||
sslConfiguration.setPrivateKey(_ocWizard->_clientSslKey);
|
||||
acc->setSslConfiguration(sslConfiguration);
|
||||
|
||||
// Make sure TCP connections get re-established
|
||||
acc->networkAccessManager()->clearAccessCache();
|
||||
|
||||
addCertDial->reinit(); // FIXME: Why not just have this only created on use?
|
||||
validatePage();
|
||||
} else {
|
||||
QString message;
|
||||
message = certif.Comment.c_str();
|
||||
addCertDial->showErrorMessage(message);
|
||||
addCertDial->showErrorMessage("Could not load certificate");
|
||||
addCertDial->show();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
OwncloudSetupPage::~OwncloudSetupPage()
|
||||
{
|
||||
delete addCertDial;
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -59,7 +59,6 @@ public slots:
|
||||
void setErrorString( const QString&, bool retryHTTPonly );
|
||||
void startSpinner();
|
||||
void stopSpinner();
|
||||
void slotAskSSLClientCertificate();
|
||||
void slotCertificateAccepted();
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -224,11 +224,4 @@ AbstractCredentials* OwncloudWizard::getCredentials() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
// outputs the signal needed to authenticate a certificate
|
||||
void OwncloudWizard::raiseCertificatePopup()
|
||||
{
|
||||
emit needCertificate();
|
||||
}
|
||||
|
||||
|
||||
} // end namespace
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#define MIRALL_OWNCLOUD_WIZARD_H
|
||||
|
||||
#include <QWizard>
|
||||
#include <QSslKey>
|
||||
#include <QSslCertificate>
|
||||
|
||||
#include "wizard/owncloudwizardcommon.h"
|
||||
#include "accountfwd.h"
|
||||
@@ -63,11 +65,10 @@ public:
|
||||
void displayError( const QString&, bool retryHTTPonly);
|
||||
AbstractCredentials* getCredentials() const;
|
||||
|
||||
void raiseCertificatePopup();
|
||||
QByteArray ownCloudCertificate;
|
||||
QString ownCloudPrivateKey;
|
||||
QString ownCloudCertificatePath;
|
||||
QString ownCloudCertificatePasswd;
|
||||
// FIXME: Can those be local variables?
|
||||
// Set from the OwncloudSetupPage, later used from OwncloudHttpCredsPage
|
||||
QSslKey _clientSslKey;
|
||||
QSslCertificate _clientSslCertificate;
|
||||
|
||||
public slots:
|
||||
void setAuthType(WizardCommon::AuthType type);
|
||||
|
||||
@@ -70,7 +70,6 @@ set(libsync_SRCS
|
||||
creds/abstractcredentials.cpp
|
||||
creds/credentialscommon.cpp
|
||||
../3rdparty/qjson/json.cpp
|
||||
../3rdparty/certificates/p12topem.cpp
|
||||
)
|
||||
|
||||
if(TOKEN_AUTH_ONLY)
|
||||
|
||||
+11
-33
@@ -18,7 +18,6 @@
|
||||
#include "configfile.h"
|
||||
#include "accessmanager.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "../3rdparty/certificates/p12topem.h"
|
||||
#include "capabilities.h"
|
||||
#include "theme.h"
|
||||
|
||||
@@ -93,7 +92,7 @@ void Account::setDavUser(const QString &newDavUser)
|
||||
|
||||
QString Account::displayName() const
|
||||
{
|
||||
QString dn = QString("%1@%2").arg(davUser(), _url.host());
|
||||
QString dn = QString("%1@%2").arg(_credentials->user(), _url.host());
|
||||
int port = url().port();
|
||||
if (port > 0 && port != 80 && port != 443) {
|
||||
dn.append(QLatin1Char(':'));
|
||||
@@ -149,10 +148,14 @@ QUrl Account::davUrl() const
|
||||
return Utility::concatUrlPath(url(), davPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* clear all cookies. (Session cookies or not)
|
||||
*/
|
||||
void Account::clearCookieJar()
|
||||
{
|
||||
Q_ASSERT(qobject_cast<CookieJar*>(_am->cookieJar()));
|
||||
static_cast<CookieJar*>(_am->cookieJar())->clearSessionCookies();
|
||||
static_cast<CookieJar*>(_am->cookieJar())->setAllCookies(QList<QNetworkCookie>());
|
||||
emit wantsAccountSaved(this);
|
||||
}
|
||||
|
||||
/*! This shares our official cookie jar (containing all the tasty
|
||||
@@ -242,12 +245,6 @@ QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNet
|
||||
return _am->sendCustomRequest(req, verb, data);
|
||||
}
|
||||
|
||||
void Account::setCertificate(const QByteArray certficate, const QString privateKey)
|
||||
{
|
||||
_pemCertificate=certficate;
|
||||
_pemPrivateKey=privateKey;
|
||||
}
|
||||
|
||||
void Account::setSslConfiguration(const QSslConfiguration &config)
|
||||
{
|
||||
_sslConfiguration = config;
|
||||
@@ -264,31 +261,7 @@ QSslConfiguration Account::getOrCreateSslConfig()
|
||||
// if setting the client certificate fails, you will probably get an error similar to this:
|
||||
// "An internal error number 1060 happened. SSL handshake failed, client certificate was requested: SSL error: sslv3 alert handshake failure"
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
QSslCertificate sslClientCertificate;
|
||||
|
||||
ConfigFile cfgFile;
|
||||
if(!cfgFile.certificatePath().isEmpty() && !cfgFile.certificatePasswd().isEmpty()) {
|
||||
resultP12ToPem certif = p12ToPem(cfgFile.certificatePath().toStdString(), cfgFile.certificatePasswd().toStdString());
|
||||
QString s = QString::fromStdString(certif.Certificate);
|
||||
QByteArray ba = s.toLocal8Bit();
|
||||
this->setCertificate(ba, QString::fromStdString(certif.PrivateKey));
|
||||
}
|
||||
if((!_pemCertificate.isEmpty())&&(!_pemPrivateKey.isEmpty())) {
|
||||
// Read certificates
|
||||
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(_pemCertificate, QSsl::Pem);
|
||||
if(sslCertificateList.length() != 0) {
|
||||
sslClientCertificate = sslCertificateList.takeAt(0);
|
||||
}
|
||||
// Read key from file
|
||||
QSslKey privateKey(_pemPrivateKey.toLocal8Bit(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey , "");
|
||||
|
||||
// SSL configuration
|
||||
sslConfig.setCaCertificates(QSslSocket::systemCaCertificates());
|
||||
sslConfig.setLocalCertificate(sslClientCertificate);
|
||||
sslConfig.setPrivateKey(privateKey);
|
||||
qDebug() << "Added SSL client certificate to the query";
|
||||
}
|
||||
|
||||
#if QT_VERSION > QT_VERSION_CHECK(5, 2, 0)
|
||||
// Try hard to re-use session for different requests
|
||||
sslConfig.setSslOption(QSsl::SslOptionDisableSessionTickets, false);
|
||||
@@ -379,8 +352,11 @@ void Account::slotHandleSslErrors(QNetworkReply *reply , QList<QSslError> errors
|
||||
// Keep a ref here on our stackframe to make sure that it doesn't get deleted before
|
||||
// handleErrors returns.
|
||||
QSharedPointer<QNetworkAccessManager> qnamLock = _am;
|
||||
QPointer<QObject> guard = reply;
|
||||
|
||||
if (_sslErrorHandler->handleErrors(errors, reply->sslConfiguration(), &approvedCerts, sharedFromThis())) {
|
||||
if (!guard) return;
|
||||
|
||||
QSslSocket::addDefaultCaCertificates(approvedCerts);
|
||||
addApprovedCerts(approvedCerts);
|
||||
emit wantsAccountSaved(this);
|
||||
@@ -392,6 +368,8 @@ void Account::slotHandleSslErrors(QNetworkReply *reply , QList<QSslError> errors
|
||||
// certificate changes.
|
||||
reply->ignoreSslErrors(errors);
|
||||
} else {
|
||||
if (!guard) return;
|
||||
|
||||
// Mark all involved certificates as rejected, so we don't ask the user again.
|
||||
foreach (const QSslError &error, errors) {
|
||||
if (!_rejectedCertificates.contains(error.certificate())) {
|
||||
|
||||
@@ -224,8 +224,7 @@ private:
|
||||
QList<QSslCertificate> _rejectedCertificates;
|
||||
|
||||
static QString _configFileName;
|
||||
QByteArray _pemCertificate;
|
||||
QString _pemPrivateKey;
|
||||
|
||||
QString _davPath; // defaults to value from theme, might be overwritten in brandings
|
||||
friend class AccountManager;
|
||||
};
|
||||
|
||||
@@ -116,12 +116,9 @@ bool Capabilities::chunkingNg() const
|
||||
return _capabilities["dav"].toMap()["chunking"].toByteArray() >= "1.0";
|
||||
}
|
||||
|
||||
quint64 Capabilities::requestMaxDurationDC() const
|
||||
bool Capabilities::chunkingParallelUploadDisabled() const
|
||||
{
|
||||
QByteArray requestMaxDurationDC = _capabilities["dav"].toMap()["max_single_upload_request_duration_msec"].toByteArray();
|
||||
if (!requestMaxDurationDC.isEmpty())
|
||||
return requestMaxDurationDC.toLongLong();
|
||||
return 0;
|
||||
return _capabilities["dav"].toMap()["chunkingParallelUploadDisabled"].toBool();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ public:
|
||||
int sharePublicLinkExpireDateDays() const;
|
||||
bool shareResharing() const;
|
||||
bool chunkingNg() const;
|
||||
quint64 requestMaxDurationDC() const;
|
||||
|
||||
/// disable parallel upload in chunking
|
||||
bool chunkingParallelUploadDisabled() const;
|
||||
|
||||
/// returns true if the capabilities report notifications
|
||||
bool notificationsAvailable() const;
|
||||
|
||||
@@ -52,7 +52,6 @@ static const char updateCheckIntervalC[] = "updateCheckInterval";
|
||||
static const char geometryC[] = "geometry";
|
||||
static const char timeoutC[] = "timeout";
|
||||
static const char chunkSizeC[] = "chunkSize";
|
||||
static const char maxChunkSizeC[] = "maxChunkSizeC";
|
||||
|
||||
static const char proxyHostC[] = "Proxy/host";
|
||||
static const char proxyTypeC[] = "Proxy/type";
|
||||
@@ -129,18 +128,6 @@ quint64 ConfigFile::chunkSize() const
|
||||
return settings.value(QLatin1String(chunkSizeC), 10*1000*1000).toLongLong(); // default to 10 MB
|
||||
}
|
||||
|
||||
quint64 ConfigFile::maxChunkSize() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
return settings.value(QLatin1String(maxChunkSizeC), 50*1000*1000).toLongLong(); // default to 50 MB
|
||||
}
|
||||
|
||||
quint64 ConfigFile::minChunkSize() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
return settings.value(QLatin1String(maxChunkSizeC), 1000*1000).toLongLong(); // default to 1 MB
|
||||
}
|
||||
|
||||
void ConfigFile::setOptionalDesktopNotifications(bool show)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
|
||||
@@ -113,8 +113,6 @@ public:
|
||||
|
||||
int timeout() const;
|
||||
quint64 chunkSize() const;
|
||||
quint64 maxChunkSize() const;
|
||||
quint64 minChunkSize() const;
|
||||
|
||||
void saveGeometry(QWidget *w);
|
||||
void restoreGeometry(QWidget *w);
|
||||
|
||||
@@ -144,7 +144,12 @@ void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &inf
|
||||
// status.php could not be loaded (network or server issue!).
|
||||
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << reply->error() << reply->errorString();
|
||||
qDebug() << Q_FUNC_INFO << reply->error() << reply->errorString() << reply->peek(1024);
|
||||
if (reply && !_account->credentials()->ready()) {
|
||||
// This could be needed for SSL client certificates
|
||||
// We need to load them from keychain and try
|
||||
reportResult( CredentialsMissingOrWrong );
|
||||
} else
|
||||
if( reply && ! _account->credentials()->stillValid(reply)) {
|
||||
_errors.append(tr("Authentication error: Either username or password are wrong."));
|
||||
} else {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QSettings>
|
||||
#include <QSslKey>
|
||||
|
||||
#include <keychain.h>
|
||||
|
||||
@@ -36,8 +37,8 @@ namespace OCC
|
||||
namespace
|
||||
{
|
||||
const char userC[] = "user";
|
||||
const char certifPathC[] = "certificatePath";
|
||||
const char certifPasswdC[] = "certificatePasswd";
|
||||
const char clientCertificatePEMC[] = "_clientCertificatePEM";
|
||||
const char clientKeyPEMC[] = "_clientKeyPEM";
|
||||
const char authenticationFailedC[] = "owncloud-authentication-failed";
|
||||
} // ns
|
||||
|
||||
@@ -50,24 +51,47 @@ protected:
|
||||
QByteArray credHash = QByteArray(_cred->user().toUtf8()+":"+_cred->password().toUtf8()).toBase64();
|
||||
QNetworkRequest req(request);
|
||||
req.setRawHeader(QByteArray("Authorization"), QByteArray("Basic ") + credHash);
|
||||
//qDebug() << "Request for " << req.url() << "with authorization" << QByteArray::fromBase64(credHash);
|
||||
//qDebug() << "Request for " << req.url() << "with authorization"
|
||||
// << QByteArray::fromBase64(credHash)
|
||||
// << _cred->_clientSslKey << _cred->_clientSslCertificate
|
||||
// << _cred->_clientSslKey.isNull() << _cred->_clientSslCertificate.isNull();
|
||||
|
||||
if (!_cred->_clientSslKey.isNull() && !_cred->_clientSslCertificate.isNull()) {
|
||||
// SSL configuration
|
||||
QSslConfiguration sslConfiguration = req.sslConfiguration();
|
||||
sslConfiguration.setLocalCertificate(_cred->_clientSslCertificate);
|
||||
sslConfiguration.setPrivateKey(_cred->_clientSslKey);
|
||||
req.setSslConfiguration(sslConfiguration);
|
||||
}
|
||||
|
||||
|
||||
return AccessManager::createRequest(op, req, outgoingData);
|
||||
}
|
||||
private:
|
||||
const HttpCredentials *_cred;
|
||||
};
|
||||
|
||||
|
||||
static void addSettingsToJob(Account *account, QKeychain::Job *job)
|
||||
{
|
||||
Q_UNUSED(account);
|
||||
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
|
||||
settings->setParent(job); // make the job parent to make setting deleted properly
|
||||
job->setSettings(settings.release());
|
||||
}
|
||||
|
||||
HttpCredentials::HttpCredentials()
|
||||
: _ready(false)
|
||||
{
|
||||
}
|
||||
|
||||
HttpCredentials::HttpCredentials(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd)
|
||||
// From wizard
|
||||
HttpCredentials::HttpCredentials(const QString& user, const QString& password, const QSslCertificate& certificate, const QSslKey& key)
|
||||
: _user(user),
|
||||
_password(password),
|
||||
_ready(true),
|
||||
_certificatePath(certificatePath),
|
||||
_certificatePasswd(certificatePasswd)
|
||||
_clientSslKey(key),
|
||||
_clientSslCertificate(certificate)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -86,16 +110,6 @@ QString HttpCredentials::password() const
|
||||
return _password;
|
||||
}
|
||||
|
||||
QString HttpCredentials::certificatePath() const
|
||||
{
|
||||
return _certificatePath;
|
||||
}
|
||||
|
||||
QString HttpCredentials::certificatePasswd() const
|
||||
{
|
||||
return _certificatePasswd;
|
||||
}
|
||||
|
||||
void HttpCredentials::setAccount(Account* account)
|
||||
{
|
||||
AbstractCredentials::setAccount(account);
|
||||
@@ -129,35 +143,83 @@ void HttpCredentials::fetchFromKeychain()
|
||||
{
|
||||
// User must be fetched from config file
|
||||
fetchUser();
|
||||
_certificatePath = _account->credentialSetting(QLatin1String(certifPathC)).toString();
|
||||
_certificatePasswd = _account->credentialSetting(QLatin1String(certifPasswdC)).toString();
|
||||
|
||||
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
|
||||
const QString kck = keychainKey(_account->url().toString(), _user );
|
||||
|
||||
QString key = QString::fromLatin1( "%1/data" ).arg( kck );
|
||||
if( settings && settings->contains(key) ) {
|
||||
// Clean the password from the config file if it is in there.
|
||||
// we do not want a security problem.
|
||||
settings->remove(key);
|
||||
key = QString::fromLatin1( "%1/type" ).arg( kck );
|
||||
settings->remove(key);
|
||||
settings->sync();
|
||||
}
|
||||
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
// Read client cert from keychain
|
||||
const QString kck = keychainKey(_account->url().toString(), _user + clientCertificatePEMC);
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
settings->setParent(job); // make the job parent to make setting deleted properly
|
||||
job->setSettings(settings.release());
|
||||
|
||||
addSettingsToJob(_account, job);
|
||||
job->setInsecureFallback(false);
|
||||
job->setKey(kck);
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
|
||||
qDebug() << "-------- ----->" << _clientSslCertificate << _clientSslKey;
|
||||
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadClientCertPEMJobDone(QKeychain::Job*)));
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpCredentials::slotReadClientCertPEMJobDone(QKeychain::Job* incoming)
|
||||
{
|
||||
// Store PEM in memory
|
||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(incoming);
|
||||
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
||||
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
|
||||
if(sslCertificateList.length() >= 1) {
|
||||
_clientSslCertificate = sslCertificateList.at(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Load key too
|
||||
const QString kck = keychainKey(_account->url().toString(), _user + clientKeyPEMC);
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
addSettingsToJob(_account, job);
|
||||
job->setInsecureFallback(false);
|
||||
job->setKey(kck);
|
||||
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadClientKeyPEMJobDone(QKeychain::Job*)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job* incoming)
|
||||
{
|
||||
// Store key in memory
|
||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(incoming);
|
||||
|
||||
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
||||
QByteArray clientKeyPEM = readJob->binaryData();
|
||||
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
|
||||
// load whatever we have. So we try until it works.
|
||||
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Rsa);
|
||||
if (_clientSslKey.isNull()) {
|
||||
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Dsa);
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
|
||||
// ec keys are Qt 5.5
|
||||
if (_clientSslKey.isNull()) {
|
||||
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Ec);
|
||||
}
|
||||
#endif
|
||||
if (_clientSslKey.isNull()) {
|
||||
qDebug() << "Warning: Could not load SSL key into Qt!";
|
||||
}
|
||||
}
|
||||
|
||||
// Now fetch the actual server password
|
||||
const QString kck = keychainKey(_account->url().toString(), _user );
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
addSettingsToJob(_account, job);
|
||||
job->setInsecureFallback(false);
|
||||
job->setKey(kck);
|
||||
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
|
||||
bool HttpCredentials::stillValid(QNetworkReply *reply)
|
||||
{
|
||||
return ((reply->error() != QNetworkReply::AuthenticationRequiredError)
|
||||
@@ -166,10 +228,10 @@ bool HttpCredentials::stillValid(QNetworkReply *reply)
|
||||
|| !reply->property(authenticationFailedC).toBool()));
|
||||
}
|
||||
|
||||
void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
|
||||
void HttpCredentials::slotReadJobDone(QKeychain::Job *incomingJob)
|
||||
{
|
||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(job);
|
||||
_password = readJob->textData();
|
||||
QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob*>(incomingJob);
|
||||
_password = job->textData();
|
||||
|
||||
if( _user.isEmpty()) {
|
||||
qDebug() << "Strange: User is empty!";
|
||||
@@ -178,7 +240,6 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
|
||||
QKeychain::Error error = job->error();
|
||||
|
||||
if( !_password.isEmpty() && error == NoError ) {
|
||||
|
||||
// All cool, the keychain did not come back with error.
|
||||
// Still, the password can be empty which indicates a problem and
|
||||
// the password dialog has to be opened.
|
||||
@@ -214,9 +275,7 @@ void HttpCredentials::invalidateToken()
|
||||
}
|
||||
|
||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
|
||||
settings->setParent(job); // make the job parent to make setting deleted properly
|
||||
job->setSettings(settings.release());
|
||||
addSettingsToJob(_account, job);
|
||||
job->setInsecureFallback(true);
|
||||
job->setKey(kck);
|
||||
job->start();
|
||||
@@ -261,14 +320,37 @@ void HttpCredentials::persist()
|
||||
// We never connected or fetched the user, there is nothing to save.
|
||||
return;
|
||||
}
|
||||
_account->setCredentialSetting(QLatin1String(userC), _user);
|
||||
_account->setCredentialSetting(QLatin1String(certifPathC), _certificatePath);
|
||||
_account->setCredentialSetting(QLatin1String(certifPasswdC), _certificatePasswd);
|
||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
|
||||
settings->setParent(job); // make the job parent to make setting deleted properly
|
||||
job->setSettings(settings.release());
|
||||
|
||||
_account->setCredentialSetting(QLatin1String(userC), _user);
|
||||
|
||||
// write cert
|
||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||
addSettingsToJob(_account, job);
|
||||
job->setInsecureFallback(false);
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteClientCertPEMJobDone(QKeychain::Job*)));
|
||||
job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC));
|
||||
job->setBinaryData(_clientSslCertificate.toPem());
|
||||
job->start();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotWriteClientCertPEMJobDone(Job *incomingJob)
|
||||
{
|
||||
Q_UNUSED(incomingJob);
|
||||
// write ssl key
|
||||
WritePasswordJob* job = new WritePasswordJob(Theme::instance()->appName());
|
||||
addSettingsToJob(_account, job);
|
||||
job->setInsecureFallback(false);
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteClientKeyPEMJobDone(QKeychain::Job*)));
|
||||
job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC));
|
||||
job->setBinaryData(_clientSslKey.toPem());
|
||||
job->start();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotWriteClientKeyPEMJobDone(Job *incomingJob)
|
||||
{
|
||||
Q_UNUSED(incomingJob);
|
||||
WritePasswordJob* job = new WritePasswordJob(Theme::instance()->appName());
|
||||
addSettingsToJob(_account, job);
|
||||
job->setInsecureFallback(false);
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteJobDone(QKeychain::Job*)));
|
||||
job->setKey(keychainKey(_account->url().toString(), _user));
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#define MIRALL_CREDS_HTTP_CREDENTIALS_H
|
||||
|
||||
#include <QMap>
|
||||
|
||||
#include <QSslCertificate>
|
||||
#include <QSslKey>
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
class QNetworkReply;
|
||||
@@ -25,6 +26,8 @@ class QAuthenticator;
|
||||
|
||||
namespace QKeychain {
|
||||
class Job;
|
||||
class WritePasswordJob;
|
||||
class ReadPasswordJob;
|
||||
}
|
||||
|
||||
namespace OCC
|
||||
@@ -33,10 +36,10 @@ namespace OCC
|
||||
class OWNCLOUDSYNC_EXPORT HttpCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class HttpCredentialsAccessManager;
|
||||
public:
|
||||
explicit HttpCredentials();
|
||||
HttpCredentials(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd);
|
||||
HttpCredentials(const QString& user, const QString& password, const QSslCertificate& certificate = QSslCertificate(), const QSslKey& key = QSslKey());
|
||||
|
||||
QString authType() const Q_DECL_OVERRIDE;
|
||||
QNetworkAccessManager* getQNAM() const Q_DECL_OVERRIDE;
|
||||
@@ -50,15 +53,19 @@ public:
|
||||
void forgetSensitiveData() Q_DECL_OVERRIDE;
|
||||
QString fetchUser();
|
||||
virtual bool sslIsTrusted() { return false; }
|
||||
QString certificatePath() const;
|
||||
QString certificatePasswd() const;
|
||||
|
||||
// To fetch the user name as early as possible
|
||||
void setAccount(Account* account) Q_DECL_OVERRIDE;
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||
|
||||
void slotReadClientCertPEMJobDone(QKeychain::Job*);
|
||||
void slotReadClientKeyPEMJobDone(QKeychain::Job*);
|
||||
void slotReadJobDone(QKeychain::Job*);
|
||||
|
||||
void slotWriteClientCertPEMJobDone(QKeychain::Job*);
|
||||
void slotWriteClientKeyPEMJobDone(QKeychain::Job*);
|
||||
void slotWriteJobDone(QKeychain::Job*);
|
||||
void clearQNAMCache();
|
||||
|
||||
@@ -66,12 +73,11 @@ protected:
|
||||
QString _user;
|
||||
QString _password;
|
||||
QString _previousPassword;
|
||||
|
||||
QString _fetchErrorString;
|
||||
bool _ready;
|
||||
|
||||
private:
|
||||
QString _certificatePath;
|
||||
QString _certificatePasswd;
|
||||
QSslKey _clientSslKey;
|
||||
QSslCertificate _clientSslCertificate;
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <QThread>
|
||||
#include <qmetaobject.h>
|
||||
|
||||
#include "csync.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
@@ -48,6 +50,14 @@ static void mirallLogCatcher(QtMsgType type, const QMessageLogContext &ctx, cons
|
||||
}
|
||||
#endif
|
||||
|
||||
static void csyncLogCatcher(int /*verbosity*/,
|
||||
const char * /*function*/,
|
||||
const char *buffer,
|
||||
void * /*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
Logger *Logger::instance()
|
||||
{
|
||||
static Logger log;
|
||||
@@ -55,7 +65,7 @@ Logger *Logger::instance()
|
||||
}
|
||||
|
||||
Logger::Logger( QObject* parent) : QObject(parent),
|
||||
_showTime(true), _doLogging(false), _doFileFlush(false), _logExpire(0)
|
||||
_showTime(true), _logWindowActivated(false), _doFileFlush(false), _logExpire(0)
|
||||
{
|
||||
#ifndef NO_MSG_HANDLER
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
|
||||
@@ -117,12 +127,8 @@ bool Logger::isNoop() const
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
return false;
|
||||
#else
|
||||
static auto signal = QMetaMethod::fromSignal(&Logger::newLog);
|
||||
if (isSignalConnected(signal)) {
|
||||
return false;
|
||||
}
|
||||
QMutexLocker lock(const_cast<QMutex *>(&_mutex));
|
||||
return !_logstream;
|
||||
return !_logstream && !_logWindowActivated;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -136,7 +142,7 @@ void Logger::doLog(const QString& msg)
|
||||
if( _doFileFlush ) _logstream->flush();
|
||||
}
|
||||
}
|
||||
emit newLog(msg);
|
||||
emit logWindowLog(msg);
|
||||
}
|
||||
|
||||
void Logger::csyncLog( const QString& message )
|
||||
@@ -164,10 +170,25 @@ void Logger::mirallLog( const QString& message )
|
||||
Logger::instance()->log( log_ );
|
||||
}
|
||||
|
||||
void Logger::setLogWindowActivated(bool activated)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
// Setup CSYNC logging to forward to our own logger
|
||||
csync_set_log_callback(csyncLogCatcher);
|
||||
csync_set_log_level(11);
|
||||
|
||||
_logWindowActivated = activated;
|
||||
}
|
||||
|
||||
void Logger::setLogFile(const QString & name)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
// Setup CSYNC logging to forward to our own logger
|
||||
csync_set_log_callback(csyncLogCatcher);
|
||||
csync_set_log_level(11);
|
||||
|
||||
if( _logstream ) {
|
||||
_logstream.reset(0);
|
||||
_logFile.close();
|
||||
|
||||
@@ -61,13 +61,15 @@ public:
|
||||
void postOptionalGuiLog(const QString& title, const QString& message);
|
||||
void postGuiMessage(const QString& title, const QString& message);
|
||||
|
||||
void setLogWindowActivated(bool activated);
|
||||
void setLogFile( const QString & name );
|
||||
void setLogExpire( int expire );
|
||||
void setLogDir( const QString& dir );
|
||||
void setLogFlush( bool flush );
|
||||
|
||||
signals:
|
||||
void newLog(const QString&);
|
||||
void logWindowLog(const QString&);
|
||||
|
||||
void guiLog(const QString&, const QString&);
|
||||
void guiMessage(const QString&, const QString&);
|
||||
void optionalGuiLog(const QString&, const QString&);
|
||||
@@ -80,7 +82,7 @@ private:
|
||||
~Logger();
|
||||
QList<Log> _logs;
|
||||
bool _showTime;
|
||||
bool _doLogging;
|
||||
bool _logWindowActivated;
|
||||
QFile _logFile;
|
||||
bool _doFileFlush;
|
||||
int _logExpire;
|
||||
|
||||
@@ -472,7 +472,7 @@ bool CheckServerJob::finished()
|
||||
QVariantMap status = QtJson::parse(QString::fromUtf8(body), success).toMap();
|
||||
// empty or invalid response
|
||||
if (!success || status.isEmpty()) {
|
||||
qDebug() << "status.php from server is not valid JSON!";
|
||||
qDebug() << "status.php from server is not valid JSON!" << body << reply()->request().url();
|
||||
}
|
||||
|
||||
qDebug() << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
|
||||
|
||||
@@ -277,7 +277,7 @@ PropagateItemJob* OwncloudPropagator::createJob(const SyncFileItemPtr &item) {
|
||||
} else {
|
||||
PropagateUploadFileCommon *job = 0;
|
||||
if (item->_size > chunkSize() && account()->capabilities().chunkingNg()) {
|
||||
job = new PropagateUploadFileNG(this, item, account()->capabilities().requestMaxDurationDC());
|
||||
job = new PropagateUploadFileNG(this, item);
|
||||
} else {
|
||||
job = new PropagateUploadFileV1(this, item);
|
||||
}
|
||||
@@ -459,18 +459,6 @@ quint64 OwncloudPropagator::chunkSize()
|
||||
return chunkSize;
|
||||
}
|
||||
|
||||
quint64 OwncloudPropagator::maxChunkSize()
|
||||
{
|
||||
static uint chunkSize;
|
||||
if (!chunkSize) {
|
||||
chunkSize = qgetenv("OWNCLOUD_MAX_CHUNK_SIZE").toUInt();
|
||||
if (chunkSize == 0) {
|
||||
ConfigFile cfg;
|
||||
chunkSize = cfg.maxChunkSize();
|
||||
}
|
||||
}
|
||||
return chunkSize;
|
||||
}
|
||||
|
||||
bool OwncloudPropagator::localFileNameClash( const QString& relFile )
|
||||
{
|
||||
|
||||
@@ -272,6 +272,7 @@ public:
|
||||
SyncJournalDb * const _journal;
|
||||
bool _finishedEmited; // used to ensure that finished is only emitted once
|
||||
|
||||
|
||||
public:
|
||||
OwncloudPropagator(AccountPtr account, const QString &localDir,
|
||||
const QString &remoteFolder, SyncJournalDb *progressDb)
|
||||
@@ -326,7 +327,6 @@ public:
|
||||
|
||||
/** returns the size of chunks in bytes */
|
||||
static quint64 chunkSize();
|
||||
static quint64 maxChunkSize();
|
||||
|
||||
AccountPtr account() const;
|
||||
|
||||
|
||||
@@ -70,7 +70,9 @@ bool SqlDatabase::openHelper( const QString& filename, int sqliteFlags )
|
||||
|
||||
bool SqlDatabase::checkDb()
|
||||
{
|
||||
SqlQuery quick_check("PRAGMA quick_check;", *this);
|
||||
// quick_check can fail with a disk IO error when diskspace is low
|
||||
SqlQuery quick_check(*this);
|
||||
quick_check.prepare("PRAGMA quick_check;", /*allow_failure=*/true);
|
||||
if( !quick_check.exec() ) {
|
||||
qDebug() << "Error running quick_check on database";
|
||||
return false;
|
||||
@@ -97,6 +99,14 @@ bool SqlDatabase::openOrCreateReadWrite( const QString& filename )
|
||||
}
|
||||
|
||||
if( !checkDb() ) {
|
||||
// When disk space is low, checking the db may fail even though it's fine.
|
||||
qint64 freeSpace = Utility::freeDiskSpace(filename);
|
||||
if (freeSpace < 1000000) {
|
||||
qDebug() << "Consistency check failed, disk space is low, aborting" << freeSpace;
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Consistency check failed, removing broken db" << filename;
|
||||
close();
|
||||
QFile::remove(filename);
|
||||
@@ -191,7 +201,7 @@ SqlQuery::SqlQuery(const QString& sql, SqlDatabase& db)
|
||||
prepare(sql);
|
||||
}
|
||||
|
||||
int SqlQuery::prepare( const QString& sql)
|
||||
int SqlQuery::prepare( const QString& sql, bool allow_failure )
|
||||
{
|
||||
QString s(sql);
|
||||
_sql = s.trimmed();
|
||||
@@ -213,7 +223,7 @@ int SqlQuery::prepare( const QString& sql)
|
||||
if( _errId != SQLITE_OK ) {
|
||||
_error = QString::fromUtf8(sqlite3_errmsg(_db));
|
||||
qWarning() << "Sqlite prepare statement error:" << _error << "in" <<_sql;
|
||||
Q_ASSERT(!"SQLITE Prepare error");
|
||||
Q_ASSERT(allow_failure || !"SQLITE Prepare error");
|
||||
}
|
||||
}
|
||||
return _errId;
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
bool isSelect();
|
||||
bool isPragma();
|
||||
bool exec();
|
||||
int prepare( const QString& sql );
|
||||
int prepare( const QString& sql, bool allow_failure = false );
|
||||
bool next();
|
||||
void bindValue(int pos, const QVariant& value);
|
||||
QString lastQuery() const;
|
||||
|
||||
@@ -145,8 +145,12 @@ void ProgressInfo::reset()
|
||||
_sizeProgress = Progress();
|
||||
_fileProgress = Progress();
|
||||
_totalSizeOfCompletedJobs = 0;
|
||||
_maxBytesPerSecond = 100000.0;
|
||||
_maxFilesPerSecond = 2.0;
|
||||
|
||||
// Historically, these starting estimates were way lower, but that lead
|
||||
// to gross overestimation of ETA when a good estimate wasn't available.
|
||||
_maxBytesPerSecond = 2000000.0; // 2 MB/s
|
||||
_maxFilesPerSecond = 10.0;
|
||||
|
||||
_updateEstimatesTimer.stop();
|
||||
_lastCompletedItem = SyncFileItem();
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ void PropagateDownloadFile::start()
|
||||
// Normal job, download from oC instance
|
||||
_job = new GETFileJob(_propagator->account(),
|
||||
_propagator->_remoteFolder + _item->_file,
|
||||
&_tmpFile, headers, expectedEtagForResume, _resumeStart);
|
||||
&_tmpFile, headers, expectedEtagForResume, _resumeStart, this);
|
||||
} else {
|
||||
// We were provided a direct URL, use that one
|
||||
qDebug() << Q_FUNC_INFO << "directDownloadUrl given for " << _item->_file << _item->_directDownloadUrl;
|
||||
@@ -413,7 +413,7 @@ void PropagateDownloadFile::start()
|
||||
QUrl url = QUrl::fromUserInput(_item->_directDownloadUrl);
|
||||
_job = new GETFileJob(_propagator->account(),
|
||||
url,
|
||||
&_tmpFile, headers, expectedEtagForResume, _resumeStart);
|
||||
&_tmpFile, headers, expectedEtagForResume, _resumeStart, this);
|
||||
}
|
||||
_job->setBandwidthManager(&_propagator->_bandwidthManager);
|
||||
connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotGetFinished()));
|
||||
|
||||
@@ -290,36 +290,12 @@ private:
|
||||
uint _transferId; /// transfer id (part of the url)
|
||||
int _currentChunk; /// Id of the next chunk that will be sent
|
||||
bool _removeJobError; /// If not null, there was an error removing the job
|
||||
quint64 _lastChunkSize; /// current chunk size
|
||||
|
||||
/*
|
||||
* This is value in ms obtained from the server.
|
||||
*
|
||||
* Dynamic Chunking attribute the maximum number of miliseconds that single request below chunk size can take
|
||||
* This value should be based on heuristics with default value 10000ms, time it takes to transfer 10MB chunk on 1MB/s upload link.
|
||||
*
|
||||
* Suggested solution will be to evaluate max(SNR, MORD) where:
|
||||
* > SNR - Slow network request, so time it will take to transmit default chunking sized request at specific low upload bandwidth
|
||||
* > MORD - Maximum observed request time, so double the time of maximum observed RTT of the very small PUT request (e.g. 1kB) to the system
|
||||
*
|
||||
* Exemplary, syncing 100MB files, with chunking size 10MB, will cause sync of 10 PUT requests which max evaluation was set to <max_single_upload_request_duration_msec>
|
||||
*
|
||||
* Dynamic chunking client algorithm is specified in the ownCloud documentation and uses <max_single_upload_request_duration_msec> to estimate if given
|
||||
* bandwidth allows higher chunk sizes (because of high goodput)
|
||||
*/
|
||||
quint64 _requestMaxDuration;
|
||||
|
||||
// Map chunk number with its size from the PROPFIND on resume.
|
||||
// (Only used from slotPropfindIterate/slotPropfindFinished because the LsColJob use signals to report data.)
|
||||
QMap<int, quint64> _serverChunks;
|
||||
|
||||
quint64 chunkSize() const { return _propagator->chunkSize(); }
|
||||
quint64 maxChunkSize() const { return _propagator->maxChunkSize(); }
|
||||
|
||||
quint64 getRequestMaxDurationDC(){
|
||||
return _requestMaxDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL of a chunk.
|
||||
* If chunk == -1, returns the URL of the parent folder containing the chunks
|
||||
@@ -327,11 +303,10 @@ private:
|
||||
QUrl chunkUrl(int chunk = -1);
|
||||
|
||||
public:
|
||||
PropagateUploadFileNG(OwncloudPropagator* propagator,const SyncFileItemPtr& item, const quint64& requestMaxDuration) :
|
||||
PropagateUploadFileCommon(propagator,item), _lastChunkSize(0), _requestMaxDuration(requestMaxDuration) {}
|
||||
PropagateUploadFileNG(OwncloudPropagator* propagator,const SyncFileItemPtr& item) :
|
||||
PropagateUploadFileCommon(propagator,item) {}
|
||||
|
||||
void doStartUpload() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void startNewUpload();
|
||||
void startNextChunk();
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <QDir>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
QUrl PropagateUploadFileNG::chunkUrl(int chunk)
|
||||
@@ -262,42 +262,7 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
|
||||
quint64 fileSize = _item->_size;
|
||||
Q_ASSERT(fileSize >= _sent);
|
||||
|
||||
quint64 currentChunkSize = chunkSize();
|
||||
|
||||
// this will check if getRequestMaxDurationDC is set to 0 or not
|
||||
double requestMaxDurationDC = (double) getRequestMaxDurationDC();
|
||||
if (requestMaxDurationDC != 0) {
|
||||
// this if first chunked file request, so it can start with default size of chunkSize()
|
||||
// if _lastChunkSize != 0 it means that we already have send one request
|
||||
if(_lastChunkSize != 0){
|
||||
//TODO: this is done step by step for debugging purposes
|
||||
|
||||
//get last request timestamp
|
||||
double lastChunkLap = (double) _stopWatch.durationOfLap(QLatin1String("ChunkDuration"));
|
||||
|
||||
//get duration of the request
|
||||
double requestDuration = (double) _stopWatch.addLapTime(QLatin1String("ChunkDuration")) - lastChunkLap;
|
||||
|
||||
// calculate natural logarithm
|
||||
double correctionParameter = log(requestMaxDurationDC / requestDuration) - 1;
|
||||
|
||||
// If logarithm is smaller or equal zero, it means that we exceeded max request duration
|
||||
// If exceeded it will use currentChunkSize = chunkSize()
|
||||
// If did not exceeded, we will increase the chunk size
|
||||
// motivation for logarithm is specified in the dynamic chunking documentation
|
||||
// TODO: give link to documentation
|
||||
if (correctionParameter>0){
|
||||
currentChunkSize = qMin(_lastChunkSize + (qint64) correctionParameter*chunkSize(), maxChunkSize());
|
||||
}
|
||||
}
|
||||
|
||||
//remember the value of last chunk size
|
||||
_lastChunkSize = currentChunkSize;
|
||||
}
|
||||
|
||||
// prevent situation that chunk size is bigger then required one to send
|
||||
currentChunkSize = qMin(currentChunkSize, fileSize - _sent);
|
||||
quint64 currentChunkSize = qMin(chunkSize(), fileSize - _sent);
|
||||
|
||||
if (currentChunkSize == 0) {
|
||||
Q_ASSERT(_jobs.isEmpty()); // There should be no running job anymore
|
||||
@@ -350,7 +315,7 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
QUrl url = chunkUrl(_currentChunk);
|
||||
|
||||
// job takes ownership of device via a QScopedPointer. Job deletes itself when finishing
|
||||
PUTFileJob* job = new PUTFileJob(_propagator->account(), url, device, headers, _currentChunk);
|
||||
PUTFileJob* job = new PUTFileJob(_propagator->account(), url, device, headers, _currentChunk, this);
|
||||
_jobs.append(job);
|
||||
connect(job, SIGNAL(finishedSignal()), this, SLOT(slotPutFinished()));
|
||||
connect(job, SIGNAL(uploadProgress(qint64,qint64)),
|
||||
|
||||
@@ -123,7 +123,7 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
}
|
||||
|
||||
// job takes ownership of device via a QScopedPointer. Job deletes itself when finishing
|
||||
PUTFileJob* job = new PUTFileJob(_propagator->account(), _propagator->_remoteFolder + path, device, headers, _currentChunk);
|
||||
PUTFileJob* job = new PUTFileJob(_propagator->account(), _propagator->_remoteFolder + path, device, headers, _currentChunk, this);
|
||||
_jobs.append(job);
|
||||
connect(job, SIGNAL(finishedSignal()), this, SLOT(slotPutFinished()));
|
||||
connect(job, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(slotUploadProgress(qint64,qint64)));
|
||||
@@ -134,18 +134,25 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
_currentChunk++;
|
||||
|
||||
bool parallelChunkUpload = true;
|
||||
QByteArray env = qgetenv("OWNCLOUD_PARALLEL_CHUNK");
|
||||
if (!env.isEmpty()) {
|
||||
parallelChunkUpload = env != "false" && env != "0";
|
||||
|
||||
if (_propagator->account()->capabilities().chunkingParallelUploadDisabled()) {
|
||||
// Server may also disable parallel chunked upload for any higher version
|
||||
parallelChunkUpload = false;
|
||||
} else {
|
||||
int versionNum = _propagator->account()->serverVersionInt();
|
||||
if (versionNum < 0x080003) {
|
||||
// Disable parallel chunk upload severs older than 8.0.3 to avoid too many
|
||||
// internal sever errors (#2743, #2938)
|
||||
parallelChunkUpload = false;
|
||||
QByteArray env = qgetenv("OWNCLOUD_PARALLEL_CHUNK");
|
||||
if (!env.isEmpty()) {
|
||||
parallelChunkUpload = env != "false" && env != "0";
|
||||
} else {
|
||||
int versionNum = _propagator->account()->serverVersionInt();
|
||||
if (versionNum < 0x080003) {
|
||||
// Disable parallel chunk upload severs older than 8.0.3 to avoid too many
|
||||
// internal sever errors (#2743, #2938)
|
||||
parallelChunkUpload = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_currentChunk + _startChunk >= _chunkCount - 1) {
|
||||
// Don't do parallel upload of chunk if this might be the last chunk because the server cannot handle that
|
||||
// https://github.com/owncloud/core/issues/11106
|
||||
|
||||
@@ -73,11 +73,13 @@ SyncEngine::SyncEngine(AccountPtr account, const QString& localPath,
|
||||
, _downloadLimit(0)
|
||||
, _newBigFolderSizeLimit(-1)
|
||||
, _checksum_hook(journal)
|
||||
, _anotherSyncNeeded(false)
|
||||
, _anotherSyncNeeded(NoFollowUpSync)
|
||||
{
|
||||
qRegisterMetaType<SyncFileItem>("SyncFileItem");
|
||||
qRegisterMetaType<SyncFileItem::Status>("SyncFileItem::Status");
|
||||
qRegisterMetaType<SyncFileStatus>("SyncFileStatus");
|
||||
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
|
||||
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
|
||||
|
||||
// Everything in the SyncEngine expects a trailing slash for the localPath.
|
||||
Q_ASSERT(localPath.endsWith(QLatin1Char('/')));
|
||||
@@ -693,12 +695,13 @@ void SyncEngine::startSync()
|
||||
Q_ASSERT(!_syncRunning);
|
||||
s_anySyncRunning = true;
|
||||
_syncRunning = true;
|
||||
_anotherSyncNeeded = false;
|
||||
_anotherSyncNeeded = NoFollowUpSync;
|
||||
_clearTouchedFilesTimer.stop();
|
||||
|
||||
_progressInfo->reset();
|
||||
|
||||
if (!QDir(_localPath).exists()) {
|
||||
_anotherSyncNeeded = DelayedFollowUp;
|
||||
// No _tr, it should only occur in non-mirall
|
||||
emit csyncError("Unable to find local sync folder.");
|
||||
finalize(false);
|
||||
@@ -712,6 +715,7 @@ void SyncEngine::startSync()
|
||||
qDebug() << "There are" << freeBytes << "bytes available at" << _localPath
|
||||
<< "and at least" << minFree << "are required";
|
||||
if (freeBytes < minFree) {
|
||||
_anotherSyncNeeded = DelayedFollowUp;
|
||||
emit csyncError(tr("Only %1 are available, need at least %2 to start",
|
||||
"Placeholders are postfixed with file sizes using Utility::octetsToString()").arg(
|
||||
Utility::octetsToString(freeBytes),
|
||||
@@ -1048,7 +1052,9 @@ void SyncEngine::slotItemCompleted(const SyncFileItem &item, const PropagatorJob
|
||||
|
||||
void SyncEngine::slotFinished(bool success)
|
||||
{
|
||||
_anotherSyncNeeded = _anotherSyncNeeded || _propagator->_anotherSyncNeeded;
|
||||
if (_propagator->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) {
|
||||
_anotherSyncNeeded = ImmediateFollowUp;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
_journal->setDataFingerprint(_discoveryMainThread->_dataFingerprint);
|
||||
@@ -1171,7 +1177,7 @@ void SyncEngine::checkForPermission()
|
||||
// be restored. Do that in the next sync by not considering as a rename
|
||||
// but delete and upload. It will then be restored if needed.
|
||||
_journal->avoidRenamesOnNextSync((*it)->_file);
|
||||
_anotherSyncNeeded = true;
|
||||
_anotherSyncNeeded = ImmediateFollowUp;
|
||||
qDebug() << "Moving of " << (*it)->_file << " canceled because no permission to add parent folder";
|
||||
}
|
||||
(*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
@@ -1328,7 +1334,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;
|
||||
_anotherSyncNeeded = ImmediateFollowUp;
|
||||
|
||||
|
||||
if ((*it)->_isDirectory) {
|
||||
|
||||
@@ -49,6 +49,13 @@ class SyncJournalDb;
|
||||
class OwncloudPropagator;
|
||||
class PropagatorJob;
|
||||
|
||||
enum AnotherSyncNeeded
|
||||
{
|
||||
NoFollowUpSync,
|
||||
ImmediateFollowUp, // schedule this again immediately (limited amount of times)
|
||||
DelayedFollowUp // regularly schedule this folder again (around 1/minute, unlimited)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The SyncEngine class
|
||||
* @ingroup libsync
|
||||
@@ -82,8 +89,8 @@ public:
|
||||
Utility::StopWatch &stopWatch() { return _stopWatch; }
|
||||
SyncFileStatusTracker &syncFileStatusTracker() { return *_syncFileStatusTracker; }
|
||||
|
||||
/* Return true if we detected that another sync is needed to complete the sync */
|
||||
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
|
||||
/* Returns whether another sync is needed to complete the sync */
|
||||
AnotherSyncNeeded isAnotherSyncNeeded() { return _anotherSyncNeeded; }
|
||||
|
||||
/** Get the ms since a file was touched, or -1 if it wasn't.
|
||||
*
|
||||
@@ -259,7 +266,7 @@ private:
|
||||
/// Hook for computing checksums from csync_update
|
||||
CSyncChecksumHook _checksum_hook;
|
||||
|
||||
bool _anotherSyncNeeded;
|
||||
AnotherSyncNeeded _anotherSyncNeeded;
|
||||
|
||||
/** Stores the time since a job touched a file. */
|
||||
QHash<QString, QElapsedTimer> _touchedFiles;
|
||||
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
QString _file;
|
||||
QString _renameTarget;
|
||||
Type _type BITFIELD(3);
|
||||
Direction _direction BITFIELD(2);
|
||||
Direction _direction BITFIELD(3);
|
||||
bool _isDirectory BITFIELD(1);
|
||||
bool _serverHasIgnoredFiles BITFIELD(1);
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
|
||||
bool mayBlacklist =
|
||||
item._errorMayBeBlacklisted // explicitly flagged for blacklisting
|
||||
|| (item._httpErrorCode != 0 // or non-local error
|
||||
&& item._httpErrorCode != 507 // Don't blacklist "Insufficient Storage"
|
||||
#ifdef OWNCLOUD_5XX_NO_BLACKLIST
|
||||
&& item._httpErrorCode / 100 != 5 // In this configuration, never blacklist error 5xx
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@ using namespace OCC;
|
||||
class HttpCredentialsTest : public HttpCredentials {
|
||||
public:
|
||||
HttpCredentialsTest(const QString& user, const QString& password)
|
||||
: HttpCredentials(user, password, "", "")
|
||||
: HttpCredentials(user, password)
|
||||
{}
|
||||
|
||||
void askFromUser() Q_DECL_OVERRIDE {
|
||||
|
||||
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+182
-177
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+182
-177
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+204
-197
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+190
-185
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+187
-182
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+185
-180
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+183
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Referência em uma Nova Issue
Bloquear um usuário