1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2026-04-04 03:11:32 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Markus Goetz
27e98d13fe VERSION.cmake: 2.3.0 rc3
We're getting there.
2017-02-27 18:15:13 +01:00
92 changed files with 8302 additions and 9257 deletions

View File

@@ -1,21 +1,7 @@
ChangeLog
=========
version 2.3.2 (2017-05-08)
* Fix more crashes (thanks to everyone submitting to our crash reporter!)
* Improve compatibility with server 10.0 (#5691, X-OC-Total-Size)
* Share dialog: UI improvements, Bring to front on tray click
* owncloudcmd: Align process return value with sync return value (#3936)
* Fix disk free check on Windows when opening the local DB
version 2.3.1 (2017-03-21)
* Fix several crashes (thanks to everyone submitting to our crash reporter!)
* Improve HTTP redirect handling (#5555)
* Blacklist: Escalate repeated soft error to normal error (#5500)
* NTFS: Do not attempt to upload two existing files with similar casing (#5544)
* Fix URL for linking to application password generation for ownCloud 10.0 (#5605)
version 2.3.0 (2017-03-03)
version 2.3.0 (2017-02-xx)
* Decreased memory usage during sync
* Overlay icons: Lower CPU usage
* Allow to not sync the server's external storages by default

View File

@@ -1,11 +1,11 @@
set( MIRALL_VERSION_MAJOR 2 )
set( MIRALL_VERSION_MINOR 3 )
set( MIRALL_VERSION_PATCH 2 )
set( MIRALL_VERSION_YEAR 2017 )
set( MIRALL_VERSION_PATCH 0 )
set( MIRALL_VERSION_YEAR 2016 )
set( MIRALL_SOVERSION 0 )
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
set( MIRALL_VERSION_SUFFIX "") #e.g. beta1, beta2, rc1
set( MIRALL_VERSION_SUFFIX "rc3") #e.g. beta1, beta2, rc1
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
if( NOT DEFINED MIRALL_VERSION_BUILD )

View File

@@ -9,7 +9,6 @@ StrCpy $PageReinstall_NEW_Field_3 "Ez desinstalatu"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Dagoeneko Instalatuta"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Hautatu nola nahi duzun ${APPLICATION_NAME} instalatzea."
StrCpy $PageReinstall_OLD_Field_1 "${APPLICATION_NAME}ren bertsio berriago bat instalatuta dago! Ez da aholkatzen bertsio zaharrago bat instalatzea. Benetan bertsio zaharrago hau instalatu nahi baduzu, hobe da lehenengo bertsio berria desinstalatzea. Hautatu nahi duzun aukera eta sakatu Hurrengoa jarraitzeko."
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} dagoeneko instalatuta dago.$\nHautatu zer operazio egin nahi duzu eta klikatu Hurrengoa jarraitzeko."
StrCpy $PageReinstall_SAME_Field_2 "Gehitu/Berrinstalatu osagaiak"
StrCpy $PageReinstall_SAME_Field_3 "Desinstalatu ${APPLICATION_NAME}"
StrCpy $UNINSTALLER_APPDATA_TITLE "Desinstalatu ${APPLICATION_NAME}"
@@ -41,3 +40,4 @@ StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Desinstalatzaile honek administratzaile b
StrCpy $UAC_ERROR_LOGON_SERVICE "Saioa hasteko zerbitzua ez dago martxan, bertan behera uzten!"
StrCpy $INIT_UNINSTALLER_RUNNING "Desinstalatzailea dagoeneko martxan da."
StrCpy $SectionGroup_Shortcuts "Lasterbideak"
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} is already installed.$\r$\nSelect the operation you want to perform and click Next to continue."

View File

@@ -25,7 +25,7 @@ StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Raccourci de lancement rapide"
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Création d'un raccourci de lancement rapide"
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "Essentiels de ${APPLICATION_NAME}."
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "Raccourci de ${APPLICATION_NAME}"
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Raccourci de bureau pour ${APPLICATION_NAME}."
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Raccourci Bureau de ${APPLICATION_NAME}."
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Raccourci de lancement rapide de ${APPLICATION_NAME}."
StrCpy $UNINSTALLER_FILE_Detail "Écriture du désinstallateur"
StrCpy $UNINSTALLER_REGISTRY_Detail "Écriture des clefs de registre du désinstallateur"

View File

@@ -8,7 +8,7 @@ StrCpy $PageReinstall_NEW_Field_2 "Desinstalar antes de instalar"
StrCpy $PageReinstall_NEW_Field_3 "Não desinstale"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Já instalado"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Escolha como pretende instalar ${APPLICATION_NAME}."
StrCpy $PageReinstall_OLD_Field_1 "Já está instalada uma versão mais recente de ${APPLICATION_NAME}! Não é recomendada a instalação de uma versão mais antiga. Se realmente desejar instalar esta versão antiga, aconselha-se que desinstale primeiro a versão atual. Selecione a operação que deseja executar e clique em $\"Seguinte$\" para continuar."
StrCpy $PageReinstall_OLD_Field_1 "Uma versão mais recente da aplicação ${APPLICATION_NAME} já está instalada! Não é recomendada a instalação de uma versão mais antiga. Se realmente deseja instalar esta versão, aconselha-se a desinstalação da versão atual primeiro. Selecione a operação que deseja executar e clique em Avançar para continuar."
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} já está instalada.$\nSelecione a operação que deseja realizar e clique em 'Seguinte' para continuar."
StrCpy $PageReinstall_SAME_Field_2 "Adicionar/Reinstalar Componentes"
StrCpy $PageReinstall_SAME_Field_3 "Desinstalar ${APPLICATION_NAME}"
@@ -17,7 +17,7 @@ StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Escolha a opção de manute
StrCpy $SEC_APPLICATION_DETAILS "A instalar o essencial de ${APPLICATION_NAME}."
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integração para Windows Explorer"
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "A instalar integração para Windows Explorer"
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Atalho do progama no Menu Iniciar"
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Atalho do progama no Menu Inicial"
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "A adicionar o atalho de ${APPLICATION_NAME} ao Menu Inicial."
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Atalho da área de trabalho"
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "A criar atalhos na área de trabalho"

View File

@@ -116,9 +116,8 @@ ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll"
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP ${WIN_SETUP_BITMAP_PATH}/page_header.bmp
!define MUI_COMPONENTSPAGE_SMALLDESC
; We removed this, h1 issue 191687
;!define MUI_FINISHPAGE_LINK "${APPLICATION_DOMAIN}"
;!define MUI_FINISHPAGE_LINK_LOCATION "http://${APPLICATION_DOMAIN}"
!define MUI_FINISHPAGE_LINK "${APPLICATION_DOMAIN}"
!define MUI_FINISHPAGE_LINK_LOCATION "http://${APPLICATION_DOMAIN}"
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
!ifdef OPTION_FINISHPAGE_RELEASE_NOTES
!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED

View File

@@ -16,50 +16,20 @@ format. You can overwrite changes using the ownCloud configuration dialog.
.. note:: Use caution when making changes to the ownCloud Client configuration
file. Incorrect settings can produce unintended results.
Some interesting values that can be set on the configuration file are:
You can change the following configuration settings in the ``[ownCloud]`` section:
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[ownCloud]`` section |
+=================================+===============+========================================================================================================+
| Variable | Default | Meaning |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``remotePollInterval`` | ``30000`` | Specifies the poll time for the remote repository in milliseconds. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``forceSyncInterval`` | ``7200000`` | The duration of no activity after which a synchronization run shall be triggered automatically. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``notificationRefreshInterval`` | ``300000`` | Specifies the default interval of checking for new server notifications in milliseconds. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
- ``remotePollInterval`` (default: ``30000``) -- Specifies the poll time for the remote repository in milliseconds.
- ``forceSyncInterval`` (default: ``7200000``) -- The duration of no activity after which a synchronization run shall be triggered automatically.
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[General]`` section |
+=================================+===============+========================================================================================================+
| Variable | Default | Meaning |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``chunkSize`` | ``5242880`` | Specifies the chunk size of uploaded files in bytes. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``promptDeleteAllFiles`` | ``true`` | If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``maxLogLines`` | ``20000`` | Specifies the maximum number of log lines displayed in the log window. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``timeout`` | ``300`` | The timeout for network connections in seconds. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
- ``notificationRefreshInterval`` (default: ``300000``) -- Specifies the default interval of checking for new server notifications in milliseconds.
You can change the following configuration settings in the ``[General]`` section:
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[Proxy]`` section |
+=================================+===============+========================================================================================================+
| Variable | Default | Meaning |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``host`` | ``127.0.0.1`` | The address of the proxy server. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``port`` | ``8080`` | The port were the proxy is listening. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
| ``type`` | ``2`` | ``0`` for System Proxy. |
+ + +--------------------------------------------------------------------------------------------------------+
| | | ``1`` for SOCKS5 Proxy. |
+ + +--------------------------------------------------------------------------------------------------------+
| | | ``2`` for No Proxy. |
+ + +--------------------------------------------------------------------------------------------------------+
| | | ``3`` for HTTP(S) Proxy. |
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
- ``chunkSize`` (default: ``5242880``) -- Specifies the chunk size of uploaded files in bytes.
- ``promptDeleteAllFiles`` (default: ``true``) -- If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted.
- ``maxLogLines`` (default: ``20000``) -- Specifies the maximum number of log lines displayed in the log window.
- ``timeout`` (default: ``300``) -- The timeout for network connections in seconds.

View File

@@ -60,9 +60,10 @@ Other command line switches supported by ``owncloudcmd`` include the following:
Credential Handling
~~~~~~~~~~~~~~~~~~~
``owncloudcmd`` requires the user to specify the username and password using the standard URL pattern, e.g.,
::
``owncloudcmd`` uses the credentials of the GUI synchronization client.
If no client is configured, or if you choose to use a different user to synchronize,
you can specify the user
password setting with the usual URL pattern. For example::
$ owncloudcmd /home/user/my_sync_folder https://carla:secret@server/owncloud/remote.php/webdav/

View File

@@ -55,8 +55,8 @@ Identifying Basic Functionality Problems
---------------------
If you see this error message stop your client, delete the
``._sync_xxxxxxx.db`` file, and then restart your client.
There is a hidden ``._sync_xxxxxxx.db`` file inside the folder of every account
``.csync_journal.db`` file, and then restart your client.
There is a ``.csync_journal.db`` file inside the folder of every account
configured on your client.
.. NOTE::

View File

@@ -754,210 +754,6 @@ 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
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# 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
@@ -967,10 +763,6 @@ Comment[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
GenericName[ar]=مزامنة المجلد
Name[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
Icon[ar]=@APPLICATION_EXECUTABLE@
Comment[bg_BG]=@APPLICATION_NAME@ клиент за десктоп синхронизация
GenericName[bg_BG]=Синхронизиране на папката
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
Comment[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
GenericName[ca]=Sincronització de carpetes
Name[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
@@ -1003,16 +795,16 @@ Comment[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
GenericName[de_DE]=Ordner-Synchronisation
Name[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
Icon[de_DE]=@APPLICATION_EXECUTABLE@
Comment[eu]=@APPLICATION_NAME@ mahaigaineko sinkronizazio bezeroa
GenericName[eu]=Karpetaren sinkronizazioa
Name[eu]=@APPLICATION_NAME@ mahaigaineko sinkronizazio bezeroa
Icon[eu]=@APPLICATION_EXECUTABLE@
Comment[bg_BG]=@APPLICATION_NAME@ клиент за десктоп синхронизация
GenericName[bg_BG]=Синхронизиране на папката
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
GenericName[fa]=همسان سازی پوشه‌ها
Name[fa]=@APPLICATION_EXECUTABLE@ نسخه‌ی همسان سازی مشتری
Icon[fa]=@APPLICATION_EXECUTABLE@
Comment[fr]=Synchronisez vos dossiers avec un serveur @APPLICATION_NAME@
GenericName[fr]=Synchronisation de dossier
Name[fr]=Client de synchronisation @APPLICATION_NAME@
Comment[fr]=@APPLICATION_NAME@ synchronisation du client
GenericName[fr]=Dossier de Synchronisation
Name[fr]=@APPLICATION_NAME@ synchronisation du client
Icon[fr]=@APPLICATION_EXECUTABLE@
Comment[he]=@APPLICATION_NAME@ לקוח סנכון שולחן עבודה
GenericName[he]=סנכון תיקייה

View File

@@ -478,12 +478,7 @@ restart_sync:
SyncEngine engine(account, options.source_dir, folder, &db);
engine.setIgnoreHiddenFiles(options.ignoreHiddenFiles);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(&engine, &SyncEngine::finished,
[&app](bool result) { app.exit(result ? EXIT_SUCCESS : EXIT_FAILURE); });
#else
QObject::connect(&engine, SIGNAL(finished(bool)), &app, SLOT(quit()));
#endif
QObject::connect(&engine, SIGNAL(transmissionProgress(ProgressInfo)), &cmd, SLOT(transmissionProgressSlot()));
@@ -510,7 +505,7 @@ restart_sync:
// Have to be done async, else, an error before exec() does not terminate the event loop.
QMetaObject::invokeMethod(&engine, "startSync", Qt::QueuedConnection);
int resultCode = app.exec();
app.exec();
if (engine.isAnotherSyncNeeded() != NoFollowUpSync) {
if (restartCount < options.restartTimes) {
@@ -521,6 +516,6 @@ restart_sync:
qWarning() << "Another sync is needed, but not done because restart count is exceeded" << restartCount;
}
return resultCode;
return 0;
}

View File

@@ -313,9 +313,10 @@ void AccountSettings::slotFolderWizardAccepted()
tr("<p>Could not create local folder <i>%1</i>.")
.arg(QDir::toNativeSeparators(definition.localPath)));
return;
} else {
FileSystem::setFolderMinimumPermissions(definition.localPath);
}
}
FileSystem::setFolderMinimumPermissions(definition.localPath);
}
/* take the value from the definition of already existing folders. All folders have

View File

@@ -281,12 +281,9 @@ void AccountState::slotCredentialsFetched(AbstractCredentials* credentials)
_waitingForNewCredentials = false;
if (_connectionValidator) {
// When new credentials become available we always want to restart the
// connection validation, even if it's currently running.
_connectionValidator->deleteLater();
_connectionValidator = 0;
}
// When new credentials become available we always want to restart the
// connection validation, even if it's currently running.
delete _connectionValidator;
checkConnectivity();
}
@@ -301,12 +298,9 @@ void AccountState::slotCredentialsAsked(AbstractCredentials* credentials)
return;
}
if (_connectionValidator) {
// When new credentials become available we always want to restart the
// connection validation, even if it's currently running.
_connectionValidator->deleteLater();
_connectionValidator = 0;
}
// When new credentials become available we always want to restart the
// connection validation, even if it's currently running.
delete _connectionValidator;
checkConnectivity();
}

View File

@@ -27,10 +27,6 @@
#include "activitydata.h"
#include "activitylistmodel.h"
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_DECLARE_METATYPE(QPointer<OCC::AccountState>)
#endif
namespace OCC {
ActivityListModel::ActivityListModel(QWidget *parent)
@@ -47,8 +43,6 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
a = _finalList.at(index.row());
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
if (!ast)
return QVariant();
QStringList list;
switch (role) {
@@ -128,7 +122,7 @@ void ActivityListModel::startFetchJob(AccountState* s)
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)),
this, SLOT(slotActivitiesReceived(QVariantMap, int)));
job->setProperty("AccountStatePtr", QVariant::fromValue<QPointer<AccountState>>(s));
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
QList< QPair<QString,QString> > params;
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
@@ -145,10 +139,7 @@ void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int stat
auto activities = json.value("ocs").toMap().value("data").toList();
ActivityList list;
auto ast = qvariant_cast<QPointer<AccountState>>(sender()->property("AccountStatePtr"));
if (!ast)
return;
AccountState* ast = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
_currentlyFetching.remove(ast);
foreach( auto activ, activities ) {

View File

@@ -37,7 +37,6 @@
#include "updater/ocupdater.h"
#include "excludedfiles.h"
#include "owncloudsetupwizard.h"
#include "version.h"
#include "config.h"
@@ -53,8 +52,6 @@
#include <QMenu>
#include <QMessageBox>
#include <openssl/crypto.h>
class QSocket;
namespace OCC {
@@ -511,13 +508,7 @@ void Application::showVersion()
stream << _theme->appName().toLatin1().constData()
<< QLatin1String(" version ")
<< _theme->version().toLatin1().constData() << endl;
#ifdef GIT_SHA1
stream << "Git revision " << GIT_SHA1 << endl;
#endif
stream << "Using Qt " << qVersion() << ", built against Qt " << QT_VERSION_STR << endl;
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
stream << "Using '" << QSslSocket::sslLibraryVersionString() << "'" << endl;
#endif
stream << "Using Qt " << qVersion() << endl;
displayHelpText(helpText);
}

View File

@@ -70,21 +70,13 @@ void HttpCredentialsGui::askFromUserAsync()
QString HttpCredentialsGui::requestAppPasswordText(const Account* account)
{
int version = account->serverVersionInt();
QString path;
// Version may not be available before login on new servers!
if (!version || version >= Account::makeServerVersion(10, 0, 0)) {
path = QLatin1String("/index.php/settings/personal?sectionid=security#apppasswords");
} else if (version >= Account::makeServerVersion(9, 1, 0)) {
path = QLatin1String("/index.php/settings/personal?section=apppasswords");
} else {
// Older server than 9.1 does not have the feature to request App Password
if (account->serverVersionInt() < 0x090100) {
// Older server than 9.1 does not have trhe feature to request App Password
return QString();
}
return tr("<a href=\"%1\">Click here</a> to request an app password from the web interface.")
.arg(account->url().toString() + path);
.arg(account->url().toString() + QLatin1String("/index.php/settings/personal?section=apppasswords"));
}

View File

@@ -345,9 +345,7 @@ void Folder::showSyncResultPopup()
if( _syncResult.firstConflictItem() ) {
createGuiLog( _syncResult.firstConflictItem()->_file, LogStatusConflict, _syncResult.numConflictItems() );
}
if (int errorCount = _syncResult.numErrorItems()) {
createGuiLog( _syncResult.firstItemError()->_file, LogStatusError, errorCount );
}
createGuiLog( _syncResult.firstItemError()->_file, LogStatusError, _syncResult.numErrorItems() );
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
}
@@ -911,7 +909,6 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, bool *cancel
}
*cancel = msgBox.clickedButton() == keepBtn;
if (*cancel) {
FileSystem::setFolderMinimumPermissions(path());
journalDb()->clearFileTable();
_lastEtag.clear();
slotScheduleThisFolder();

View File

@@ -631,8 +631,8 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
const auto permissionMap = job->property(propertyPermissionMap).toMap();
QStringList sortedSubfolders = list;
if (!sortedSubfolders.isEmpty())
sortedSubfolders.removeFirst(); // skip the parent item (first in the list)
// skip the parent item (first in the list)
sortedSubfolders.erase(sortedSubfolders.begin());
Utility::sortFilenames(sortedSubfolders);
QVarLengthArray<int, 10> undecidedIndexes;
@@ -652,7 +652,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
newInfo._size = job->_sizes.value(path);
newInfo._isExternal = permissionMap.value(removeTrailingSlash(path)).toString().contains("M");
newInfo._path = relativePath;
newInfo._name = removeTrailingSlash(relativePath).split('/').last();
newInfo._name = relativePath.split('/', QString::SkipEmptyParts).last();
if (relativePath.isEmpty())
continue;

View File

@@ -57,7 +57,6 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
connect(ui->removePushButton, SIGNAL(clicked()), SLOT(slotRemoveCurrentItem()));
connect(ui->addPushButton, SIGNAL(clicked()), SLOT(slotAddPattern()));
ui->tableWidget->resizeColumnsToContents();
ui->tableWidget->horizontalHeader()->setResizeMode(patternCol, QHeaderView::Stretch);
ui->tableWidget->verticalHeader()->setVisible(false);

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>516</width>
<height>546</height>
<width>438</width>
<height>463</height>
</rect>
</property>
<property name="windowTitle">
@@ -103,18 +103,9 @@
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>

View File

@@ -54,7 +54,9 @@ void NotificationConfirmJob::start()
req.setRawHeader("Ocs-APIREQUEST", "true");
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
sendRequest(_verb, _link, req);
QIODevice *iodevice = 0;
setReply(davRequest(_verb, _link, req, iodevice));
setupConnections(reply());
AbstractNetworkJob::start();
}

View File

@@ -93,7 +93,9 @@ void OcsJob::start()
queryItems.append(qMakePair(QByteArray("format"), QByteArray("json")));
url.setEncodedQueryItems(queryItems);
sendRequest(_verb, url, req, buffer);
setReply(davRequest(_verb, url, req, buffer));
setupConnections(reply());
buffer->setParent(reply());
AbstractNetworkJob::start();
}

View File

@@ -174,31 +174,22 @@ void ownCloudGui::slotTrayClicked( QSystemTrayIcon::ActivationReason reason )
last_click.start();
}
// Left click
// A click on the tray icon should only open the status window on Win and
// Linux, not on Mac. They want a menu entry.
#if !defined Q_OS_MAC
if( reason == QSystemTrayIcon::Trigger ) {
if (OwncloudSetupWizard::bringWizardToFrontIfVisible()) {
// brought wizard to front
} else if (_shareDialogs.size() > 0) {
// Share dialog(s) be hidden by other apps, bring them back
Q_FOREACH(const QPointer<ShareDialog> &shareDialog, _shareDialogs) {
Q_ASSERT(shareDialog.data());
raiseDialog(shareDialog);
}
} else {
#ifdef Q_OS_MAC
// on macOS, a left click always opens menu.
// However if the settings dialog is already visible but hidden
// by other applications, this will bring it to the front.
if (!_settingsDialog.isNull() && _settingsDialog->isVisible()) {
raiseDialog(_settingsDialog.data());
}
// Start settings if config is existing.
slotOpenSettingsDialog();
}
#else
slotOpenSettingsDialog();
#endif
// On Mac, if the settings dialog is already visible but hidden
// by other applications, this will bring it to the front.
if( reason == QSystemTrayIcon::Trigger ) {
if (!_settingsDialog.isNull() && _settingsDialog->isVisible()) {
slotShowSettings();
}
}
// FIXME: Also make sure that any auto updater dialogue https://github.com/owncloud/client/issues/5613
// or SSL error dialog also comes to front.
#endif
}
void ownCloudGui::slotSyncStateChange( Folder* folder )

View File

@@ -32,7 +32,6 @@
#include "accountmanager.h"
#include "clientproxy.h"
#include "filesystem.h"
#include "owncloudgui.h"
#include "creds/credentialsfactory.h"
#include "creds/abstractcredentials.h"
@@ -65,10 +64,10 @@ OwncloudSetupWizard::~OwncloudSetupWizard()
_ocWizard->deleteLater();
}
static QPointer<OwncloudSetupWizard> wiz = 0;
void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *parent)
{
static QPointer<OwncloudSetupWizard> wiz;
if (!wiz.isNull()) {
return;
}
@@ -79,16 +78,6 @@ void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *
wiz->startWizard();
}
bool OwncloudSetupWizard::bringWizardToFrontIfVisible()
{
if (wiz.isNull()) {
return false;
}
ownCloudGui::raiseDialog(wiz->_ocWizard);
return true;
}
void OwncloudSetupWizard::startWizard()
{
AccountPtr account = AccountManager::createAccount();
@@ -194,8 +183,6 @@ void OwncloudSetupWizard::slotOwnCloudFoundAuth(const QUrl& url, const QVariantM
Utility::escape(CheckServerJob::versionString(info)),
Utility::escape(serverVersion)));
// Note with newer servers we get the version actually only later in capabilities
// https://github.com/owncloud/core/pull/27473/files
_ocWizard->account()->setServerVersion(serverVersion);
QString p = url.path();
@@ -381,7 +368,6 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFo
bool nextStep = true;
if( fi.exists() ) {
FileSystem::setFolderMinimumPermissions(localFolder);
// there is an existing local folder. If its non empty, it can only be synced if the
// ownCloud is newly created.
_ocWizard->appendToConfigurationLog(
@@ -608,7 +594,9 @@ DetermineAuthTypeJob::DetermineAuthTypeJob(AccountPtr account, QObject *parent)
void DetermineAuthTypeJob::start()
{
sendRequest("GET", account()->davUrl());
QNetworkReply *reply = getRequest(account()->davPath());
setReply(reply);
setupConnections(reply);
AbstractNetworkJob::start();
}
@@ -625,7 +613,8 @@ bool DetermineAuthTypeJob::finished()
// do a new run
_redirects++;
resetTimeout();
sendRequest("GET", redirection);
setReply(getRequest(redirection));
setupConnections(reply());
return false; // don't discard
} else {
#ifndef NO_SHIBBOLETH

View File

@@ -60,7 +60,6 @@ class OwncloudSetupWizard : public QObject
public:
/** Run the wizard */
static void runWizard(QObject *obj, const char* amember, QWidget *parent = 0 );
static bool bringWizardToFrontIfVisible();
signals:
// overall dialog close signal.
void ownCloudWizardDone( int );

View File

@@ -81,14 +81,9 @@ void ProxyAuthHandler::handleProxyAuthenticationRequired(
}
// Find the responsible QNAM if possible.
QNetworkAccessManager* sending_qnam = 0;
QWeakPointer<QNetworkAccessManager> qnam_alive;
QNetworkAccessManager* sending_qnam = qobject_cast<QNetworkAccessManager*>(sender());
if (Account* account = qobject_cast<Account*>(sender())) {
// Since we go into an event loop, it's possible for the account's qnam
// to be destroyed before we get back. We can use this to check for its
// liveness.
qnam_alive = account->sharedNetworkAccessManager();
sending_qnam = qnam_alive.data();
sending_qnam = account->networkAccessManager();
}
if (!sending_qnam) {
qDebug() << "Could not get the sending QNAM for" << sender();
@@ -127,7 +122,6 @@ void ProxyAuthHandler::handleProxyAuthenticationRequired(
qDebug() << "got creds for" << _proxy;
authenticator->setUser(_username);
authenticator->setPassword(_password);
sending_qnam = qnam_alive.data();
if (sending_qnam) {
_gaveCredentialsTo.insert(sending_qnam);
connect(sending_qnam, SIGNAL(destroyed(QObject*)),

View File

@@ -32,8 +32,6 @@
namespace OCC {
static const int thumbnailSize = 40;
ShareDialog::ShareDialog(QPointer<AccountState> accountState,
const QString &sharePath,
const QString &localPath,
@@ -69,7 +67,7 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
QFileInfo f_info(_localPath);
QFileIconProvider icon_provider;
QIcon icon = icon_provider.icon(f_info);
_ui->label_icon->setPixmap(icon.pixmap(thumbnailSize, thumbnailSize));
_ui->label_icon->setPixmap(icon.pixmap(40,40));
// Set filename
QFileInfo lPath(_localPath);
@@ -178,7 +176,7 @@ void ShareDialog::showSharingUi()
// We only do user/group sharing from 8.2.0
bool userGroupSharing =
theme->userGroupSharing()
&& _accountState->account()->serverVersionInt() >= Account::makeServerVersion(8, 2, 0);
&& _accountState->account()->serverVersionInt() >= ((8 << 16) + (2 << 8));
bool autoShare = !userGroupSharing;
@@ -215,7 +213,6 @@ void ShareDialog::slotThumbnailFetched(const int &statusCode, const QByteArray &
QPixmap p;
p.loadFromData(reply, "PNG");
p = p.scaledToHeight(thumbnailSize, Qt::SmoothTransformation);
_ui->label_icon->setPixmap(p);
}

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>408</width>
<height>281</height>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
@@ -64,24 +64,7 @@
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="shareWidgetsLayout">
<property name="spacing">
<number>10</number>
</property>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
<layout class="QVBoxLayout" name="shareWidgetsLayout"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">

View File

@@ -14,20 +14,11 @@
<string>Share NewDocument.odt</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_shareLink">
<property name="topMargin">
<number>10</number>
</property>
<item>
<widget class="QCheckBox" name="checkBox_shareLink">
<property name="text">
@@ -97,7 +88,7 @@
<number>20</number>
</property>
<property name="topMargin">
<number>0</number>
<number>1</number>
</property>
<property name="rightMargin">
<number>0</number>

View File

@@ -343,7 +343,7 @@ QSharedPointer<LinkShare> ShareManager::parseLinkShare(const QVariantMap &data)
// From ownCloud server 8.2 the url field is always set for public shares
if (data.contains("url")) {
url = QUrl(data.value("url").toString());
} else if (_account->serverVersionInt() >= Account::makeServerVersion(8, 0, 0)) {
} else if (_account->serverVersionInt() >= (8 << 16)) {
// From ownCloud server version 8 on, a different share link scheme is used.
url = QUrl(Utility::concatUrlPath(_account->url(), QLatin1String("index.php/s/") + data.value("token").toString())).toString();
} else {

View File

@@ -254,7 +254,7 @@ void ShareUserGroupWidget::slotCompleterActivated(const QModelIndex & index)
* https://github.com/owncloud/client/issues/4996
*/
if (sharee->type() == Sharee::Federated
&& _account->serverVersionInt() < Account::makeServerVersion(9, 1, 0)) {
&& _account->serverVersionInt() < 0x090100) {
int permissions = SharePermissionRead | SharePermissionUpdate;
if (!_isFile) {
permissions |= SharePermissionCreate | SharePermissionDelete;
@@ -280,16 +280,9 @@ void ShareUserGroupWidget::slotCompleterHighlighted(const QModelIndex & index)
void ShareUserGroupWidget::displayError(int code, const QString& message)
{
_pi_sharee.stopAnimation();
// Also remove the spinner in the widget list, if any
foreach (auto pi, _ui->scrollArea->findChildren<QProgressIndicator*>()) {
delete pi;
}
qDebug() << "Error from server" << code << message;
_ui->errorLabel->setText(message);
_ui->errorLabel->show();
_ui->shareeLineEdit->setEnabled(true);
}
ShareWidget::ShareWidget(QSharedPointer<Share> share,
@@ -331,11 +324,6 @@ ShareWidget::ShareWidget(QSharedPointer<Share> share,
QIcon icon(QLatin1String(":/client/resources/more.png"));
_ui->permissionToolButton->setIcon(icon);
// If there's only a single entry in the detailed permission menu, hide it
if (menu->actions().size() == 1) {
_ui->permissionToolButton->hide();
}
// Set the permissions checkboxes
displayPermissions();
@@ -355,7 +343,7 @@ ShareWidget::ShareWidget(QSharedPointer<Share> share,
* https://github.com/owncloud/client/issues/4996
*/
if (share->getShareType() == Share::TypeRemote
&& share->account()->serverVersionInt() < Account::makeServerVersion(9, 1, 0)) {
&& share->account()->serverVersionInt() < 0x090100) {
_ui->permissionShare->setVisible(false);
_ui->permissionToolButton->setVisible(false);
}

View File

@@ -6,26 +6,14 @@
<rect>
<x>0</x>
<y>0</y>
<width>397</width>
<height>273</height>
<width>457</width>
<height>164</height>
</rect>
</property>
<property name="windowTitle">
<string>Share NewDocument.odt</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="shareeHorizontalLayout">
<item>
@@ -106,8 +94,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>395</width>
<height>221</height>
<width>437</width>
<height>94</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3"/>

View File

@@ -404,7 +404,7 @@ void SocketApi::command_SHARE(const QString& localFile, SocketListener* listener
listener->sendMessage(message);
} else if (!theme->linkSharing() && (
!theme->userGroupSharing() ||
shareFolder->accountState()->account()->serverVersionInt() < Account::makeServerVersion(8, 2, 0))) {
shareFolder->accountState()->account()->serverVersionInt() < ((8 << 16) + (2 << 8)))) {
const QString message = QLatin1String("SHARE:NOP:")+QDir::toNativeSeparators(localFile);
listener->sendMessage(message);
} else {

View File

@@ -59,7 +59,7 @@ static QString addCertDetailsField(const QString &key, const QString &value)
return QString();
return QLatin1String("<tr><td style=\"vertical-align: top;\"><b>") + key
+ QLatin1String("</b></td><td style=\"vertical-align: bottom;\">") + value
+ QLatin1String("</b></td><td style=\"vertical-align: bottom;\">)") + value
+ QLatin1String("</td></tr>");
}

View File

@@ -27,7 +27,8 @@ ThumbnailJob::ThumbnailJob(const QString &path, AccountPtr account, QObject* par
void ThumbnailJob::start()
{
sendRequest("GET", makeAccountUrl(path()));
setReply(getRequest(path()));
setupConnections(reply());
AbstractNetworkJob::start();
}

View File

@@ -122,49 +122,40 @@ QNetworkReply* AbstractNetworkJob::addTimer(QNetworkReply *reply)
return reply;
}
QNetworkReply *AbstractNetworkJob::sendRequest(const QByteArray &verb, const QUrl &url,
QNetworkRequest req, QIODevice *requestBody)
QNetworkReply* AbstractNetworkJob::davRequest(const QByteArray &verb, const QString &relPath,
QNetworkRequest req, QIODevice *data)
{
auto reply = _account->sendRequest(verb, url, req, requestBody);
_requestBody = requestBody;
if (_requestBody) {
_requestBody->setParent(reply);
}
addTimer(reply);
setReply(reply);
setupConnections(reply);
return reply;
return addTimer(_account->davRequest(verb, relPath, req, data));
}
QUrl AbstractNetworkJob::makeAccountUrl(const QString& relativePath) const
QNetworkReply *AbstractNetworkJob::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
{
return Utility::concatUrlPath(_account->url(), relativePath);
return addTimer(_account->davRequest(verb, url, req, data));
}
QUrl AbstractNetworkJob::makeDavUrl(const QString& relativePath) const
QNetworkReply* AbstractNetworkJob::getRequest(const QString &relPath)
{
return Utility::concatUrlPath(_account->davUrl(), relativePath);
return addTimer(_account->getRequest(relPath));
}
QByteArray AbstractNetworkJob::requestVerb(QNetworkReply* reply)
QNetworkReply *AbstractNetworkJob::getRequest(const QUrl &url)
{
switch (reply->operation()) {
case QNetworkAccessManager::HeadOperation:
return "HEAD";
case QNetworkAccessManager::GetOperation:
return "GET";
case QNetworkAccessManager::PutOperation:
return "PUT";
case QNetworkAccessManager::PostOperation:
return "POST";
case QNetworkAccessManager::DeleteOperation:
return "DELETE";
case QNetworkAccessManager::CustomOperation:
return reply->request().attribute(QNetworkRequest::CustomVerbAttribute).toByteArray();
case QNetworkAccessManager::UnknownOperation:
break;
}
return QByteArray();
return addTimer(_account->getRequest(url));
}
QNetworkReply *AbstractNetworkJob::headRequest(const QString &relPath)
{
return addTimer(_account->headRequest(relPath));
}
QNetworkReply *AbstractNetworkJob::headRequest(const QUrl &url)
{
return addTimer(_account->headRequest(url));
}
QNetworkReply *AbstractNetworkJob::deleteRequest(const QUrl &url)
{
return addTimer(_account->deleteRequest(url));
}
void AbstractNetworkJob::slotFinished()
@@ -187,36 +178,24 @@ void AbstractNetworkJob::slotFinished()
// get the Date timestamp from reply
_responseTimestamp = _reply->rawHeader("Date");
QUrl requestedUrl = reply()->request().url();
QUrl redirectUrl = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (_followRedirects && !redirectUrl.isEmpty()) {
_redirectCount++;
// ### some of the qWarnings here should be exported via displayErrors() so they
if (_followRedirects) {
// ### the qWarnings here should be exported via displayErrors() so they
// ### can be presented to the user if the job executor has a GUI
QByteArray verb = requestVerb(reply());
if (requestedUrl.scheme() == QLatin1String("https") &&
redirectUrl.scheme() == QLatin1String("http")) {
qWarning() << this << "HTTPS->HTTP downgrade detected!";
} else if (requestedUrl == redirectUrl || _redirectCount >= maxRedirects()) {
qWarning() << this << "Redirect loop detected!";
} else if (_requestBody && _requestBody->isSequential()) {
qWarning() << this << "cannot redirect request with sequential body";
} else if (verb.isEmpty()) {
qWarning() << this << "cannot redirect request: could not detect original verb";
} else {
// Create the redirected request and send it
qDebug() << "Redirecting" << verb << requestedUrl << redirectUrl;
resetTimeout();
if (_requestBody) {
_requestBody->seek(0);
QUrl requestedUrl = reply()->request().url();
QUrl redirectUrl = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (!redirectUrl.isEmpty()) {
_redirectCount++;
if (requestedUrl.scheme() == QLatin1String("https") &&
redirectUrl.scheme() == QLatin1String("http")) {
qWarning() << this << "HTTPS->HTTP downgrade detected!";
} else if (requestedUrl == redirectUrl || _redirectCount >= maxRedirects()) {
qWarning() << this << "Redirect loop detected!";
} else {
resetTimeout();
setReply(getRequest(redirectUrl));
setupConnections(reply());
return;
}
sendRequest(
verb,
redirectUrl,
reply()->request(),
_requestBody);
return;
}
}

View File

@@ -78,29 +78,17 @@ signals:
void networkActivity();
protected:
void setupConnections(QNetworkReply *reply);
/** Initiate a network request, returning a QNetworkReply.
*
* Calls setReply() and setupConnections() on it.
*
* Takes ownership of the requestBody (to allow redirects).
*/
QNetworkReply* sendRequest(const QByteArray& verb, const QUrl &url,
QNetworkRequest req = QNetworkRequest(),
QIODevice *requestBody = 0);
// sendRequest does not take a relative path instead of an url,
// but the old API allowed that. We have this undefined overload
// to help catch usage errors
QNetworkReply* sendRequest(const QByteArray& verb, const QString &relativePath,
QNetworkRequest req = QNetworkRequest(),
QIODevice *requestBody = 0);
/// Creates a url for the account from a relative path
QUrl makeAccountUrl(const QString& relativePath) const;
/// Like makeAccountUrl() but uses the account's dav base path
QUrl makeDavUrl(const QString& relativePath) const;
QNetworkReply* davRequest(const QByteArray& verb, const QString &relPath,
QNetworkRequest req = QNetworkRequest(),
QIODevice *data = 0);
QNetworkReply* davRequest(const QByteArray& verb, const QUrl &url,
QNetworkRequest req = QNetworkRequest(),
QIODevice *data = 0);
QNetworkReply* getRequest(const QString &relPath);
QNetworkReply* getRequest(const QUrl &url);
QNetworkReply* headRequest(const QString &relPath);
QNetworkReply* headRequest(const QUrl &url);
QNetworkReply* deleteRequest(const QUrl &url);
int maxRedirects() const { return 10; }
virtual bool finished() = 0;
@@ -111,19 +99,12 @@ protected:
// GET requests that don't set up any HTTP body or other flags.
bool _followRedirects;
/** Helper to construct the HTTP verb used in the request
*
* Returns an empty QByteArray for UnknownOperation.
*/
static QByteArray requestVerb(QNetworkReply* reply);
private slots:
void slotFinished();
virtual void slotTimeout();
protected:
AccountPtr _account;
private:
QNetworkReply* addTimer(QNetworkReply *reply);
bool _ignoreCredentialFailure;
@@ -131,12 +112,6 @@ private:
QString _path;
QTimer _timer;
int _redirectCount;
// Set by the xyzRequest() functions and needed to be able to redirect
// requests, should it be required.
//
// Reparented to the currently running QNetworkReply.
QPointer<QIODevice> _requestBody;
};
/**

View File

@@ -202,28 +202,54 @@ QNetworkAccessManager *Account::networkAccessManager()
return _am.data();
}
QSharedPointer<QNetworkAccessManager> Account::sharedNetworkAccessManager()
QNetworkReply *Account::headRequest(const QString &relPath)
{
return _am;
return headRequest(Utility::concatUrlPath(url(), relPath));
}
QNetworkReply *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
QNetworkReply *Account::headRequest(const QUrl &url)
{
QNetworkRequest request(url);
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
request.setSslConfiguration(this->getOrCreateSslConfig());
#endif
return _am->head(request);
}
QNetworkReply *Account::getRequest(const QString &relPath)
{
return getRequest(Utility::concatUrlPath(url(), relPath));
}
QNetworkReply *Account::getRequest(const QUrl &url)
{
QNetworkRequest request(url);
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
request.setSslConfiguration(this->getOrCreateSslConfig());
#endif
return _am->get(request);
}
QNetworkReply *Account::deleteRequest( const QUrl &url)
{
QNetworkRequest request(url);
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
request.setSslConfiguration(this->getOrCreateSslConfig());
#endif
return _am->deleteResource(request);
}
QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data)
{
return davRequest(verb, Utility::concatUrlPath(davUrl(), relPath), req, data);
}
QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
{
req.setUrl(url);
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
req.setSslConfiguration(this->getOrCreateSslConfig());
#endif
if (verb == "HEAD" && !data) {
return _am->head(req);
} else if (verb == "GET" && !data) {
return _am->get(req);
} else if (verb == "POST") {
return _am->post(req, data);
} else if (verb == "PUT") {
return _am->put(req, data);
} else if (verb == "DELETE" && !data) {
return _am->deleteResource(req);
}
return _am->sendCustomRequest(req, verb, data);
}
@@ -401,14 +427,9 @@ int Account::serverVersionInt() const
{
// FIXME: Use Qt 5.5 QVersionNumber
auto components = serverVersion().split('.');
return makeServerVersion(components.value(0).toInt(),
components.value(1).toInt(),
components.value(2).toInt());
}
int Account::makeServerVersion(int majorVersion, int minorVersion, int patchVersion)
{
return (majorVersion << 16) + (minorVersion << 8) + patchVersion;
return (components.value(0).toInt() << 16)
+ (components.value(1).toInt() << 8)
+ components.value(2).toInt();
}
bool Account::serverVersionUnsupported() const
@@ -417,7 +438,7 @@ bool Account::serverVersionUnsupported() const
// not detected yet, assume it is fine.
return false;
}
return serverVersionInt() < makeServerVersion(7, 0, 0);
return serverVersionInt() < 0x070000;
}
void Account::setServerVersion(const QString& version)
@@ -433,7 +454,7 @@ void Account::setServerVersion(const QString& version)
bool Account::rootEtagChangesNotOnlySubFolderEtags()
{
return (serverVersionInt() >= makeServerVersion(8, 1, 0));
return (serverVersionInt() >= 0x080100);
}
void Account::setNonShib(bool nonShib)

View File

@@ -106,10 +106,14 @@ public:
// For creating various network requests
QNetworkReply* sendRequest(const QByteArray &verb,
const QUrl &url,
QNetworkRequest req = QNetworkRequest(),
QIODevice *data = 0);
QNetworkReply* headRequest(const QString &relPath);
QNetworkReply* headRequest(const QUrl &url);
QNetworkReply* getRequest(const QString &relPath);
QNetworkReply* getRequest(const QUrl &url);
QNetworkReply* deleteRequest( const QUrl &url);
QNetworkReply* davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data = 0);
QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0);
/** The ssl configuration during the first connection */
QSslConfiguration getOrCreateSslConfig();
@@ -145,22 +149,9 @@ public:
const Capabilities &capabilities() const;
void setCapabilities(const QVariantMap &caps);
/** Access the server version
*
* For servers >= 10.0.0, this can be the empty string until capabilities
* have been received.
*/
/** Access the server version */
QString serverVersion() const;
/** Server version for easy comparison.
*
* Example: serverVersionInt() >= makeServerVersion(11, 2, 3)
*
* Will be 0 if the version is not available yet.
*/
int serverVersionInt() const;
static int makeServerVersion(int majorVersion, int minorVersion, int patchVersion);
void setServerVersion(const QString &version);
/** Whether the server is too old.
@@ -185,7 +176,6 @@ public:
void resetNetworkAccessManager();
QNetworkAccessManager* networkAccessManager();
QSharedPointer<QNetworkAccessManager> sharedNetworkAccessManager();
/// Called by network jobs on credential errors, emits invalidCredentials()
void handleInvalidCredentials();

View File

@@ -114,21 +114,26 @@ void ConnectionValidator::slotCheckServerAndAuth()
void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &info)
{
// Newer servers don't disclose any version in status.php anymore
// https://github.com/owncloud/core/pull/27473/files
// so this string can be empty.
QString serverVersion = CheckServerJob::version(info);
// status.php was found.
qDebug() << "** Application: ownCloud found: "
<< url << " with version "
<< CheckServerJob::versionString(info)
<< "(" << serverVersion << ")";
<< "(" << CheckServerJob::version(info) << ")";
if (!serverVersion.isEmpty() && !setAndCheckServerVersion(serverVersion)) {
QString version = CheckServerJob::version(info);
_account->setServerVersion(version);
// We cannot deal with servers < 5.0.0
if (version.contains('.') && version.split('.')[0].toInt() < 5) {
_errors.append( tr("The configured server for this client is too old") );
_errors.append( tr("Please update to the latest server and restart the client.") );
reportResult( ServerVersionMismatch );
return;
}
// We attempt to work with servers >= 5.0.0 but warn users.
// Check usages of Account::serverVersionUnsupported() for details.
// now check the authentication
if (_account->credentials()->ready())
QTimer::singleShot( 0, this, SLOT( checkAuthentication() ));
@@ -230,13 +235,6 @@ void ConnectionValidator::slotCapabilitiesRecieved(const QVariantMap &json)
auto caps = json.value("ocs").toMap().value("data").toMap().value("capabilities");
qDebug() << "Server capabilities" << caps;
_account->setCapabilities(caps.toMap());
// New servers also report the version in the capabilities
QString serverVersion = caps.toMap()["core"].toMap()["status"].toMap()["version"].toString();
if (!serverVersion.isEmpty() && !setAndCheckServerVersion(serverVersion)) {
return;
}
fetchUser();
}
@@ -249,26 +247,6 @@ void ConnectionValidator::fetchUser()
job->start();
}
bool ConnectionValidator::setAndCheckServerVersion(const QString& version)
{
qDebug() << _account->url() << "has server version" << version;
_account->setServerVersion(version);
// We cannot deal with servers < 5.0.0
if (_account->serverVersionInt()
&& _account->serverVersionInt() < Account::makeServerVersion(5, 0, 0)) {
_errors.append( tr("The configured server for this client is too old") );
_errors.append( tr("Please update to the latest server and restart the client.") );
reportResult( ServerVersionMismatch );
return false;
}
// We attempt to work with servers >= 5.0.0 but warn users.
// Check usages of Account::serverVersionUnsupported() for details.
return true;
}
void ConnectionValidator::slotUserFetched(const QVariantMap &json)
{
QString user = json.value("ocs").toMap().value("data").toMap().value("id").toString();

View File

@@ -125,12 +125,6 @@ private:
void checkServerCapabilities();
void fetchUser();
/** Sets the account's server version
*
* Returns false and reports ServerVersionMismatch for very old servers.
*/
bool setAndCheckServerVersion(const QString& version);
QStringList _errors;
AccountPtr _account;
bool _isCheckingServerAndAuth;

View File

@@ -67,7 +67,9 @@ void RequestEtagJob::start()
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
// assumes ownership
sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
setReply(davRequest("PROPFIND", path(), req, buf));
buf->setParent(reply());
setupConnections(reply());
if( reply()->error() != QNetworkReply::NoError ) {
qDebug() << "getting etag: request network error: " << reply()->errorString();
@@ -120,11 +122,10 @@ void MkColJob::start()
}
// assumes ownership
if (_url.isValid()) {
sendRequest("MKCOL", _url, req);
} else {
sendRequest("MKCOL", makeDavUrl(path()), req);
}
QNetworkReply *reply = _url.isValid() ? davRequest("MKCOL", _url, req)
: davRequest("MKCOL", path(), req);
setReply(reply);
setupConnections(reply);
AbstractNetworkJob::start();
}
@@ -321,11 +322,11 @@ void LsColJob::start()
QBuffer *buf = new QBuffer(this);
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
if (_url.isValid()) {
sendRequest("PROPFIND", _url, req, buf);
} else {
sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
}
QNetworkReply *reply = _url.isValid() ? davRequest("PROPFIND", _url, req, buf)
: davRequest("PROPFIND", path(), req, buf);
buf->setParent(reply);
setReply(reply);
setupConnections(reply);
AbstractNetworkJob::start();
}
@@ -379,7 +380,8 @@ CheckServerJob::CheckServerJob(AccountPtr account, QObject *parent)
void CheckServerJob::start()
{
sendRequest("GET", makeAccountUrl(path()));
setReply(getRequest(path()));
setupConnections(reply());
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot()));
connect(reply(), SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
AbstractNetworkJob::start();
@@ -474,7 +476,10 @@ bool CheckServerJob::finished()
}
qDebug() << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
if( status.contains("installed") ) {
if( status.contains("installed")
&& status.contains("version")
&& status.contains("versionstring") ) {
emit instanceFound(reply()->url(), status);
} else {
qDebug() << "No proper answer on " << reply()->url();
@@ -524,7 +529,9 @@ void PropfindJob::start()
QBuffer *buf = new QBuffer(this);
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
setReply(davRequest("PROPFIND", path(), req, buf));
buf->setParent(reply());
setupConnections(reply());
AbstractNetworkJob::start();
}
@@ -625,7 +632,9 @@ void ProppatchJob::start()
QBuffer *buf = new QBuffer(this);
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
sendRequest("PROPPATCH", makeDavUrl(path()), req, buf);
setReply(davRequest("PROPPATCH", path(), req, buf));
buf->setParent(reply());
setupConnections(reply());
AbstractNetworkJob::start();
}
@@ -662,7 +671,8 @@ EntityExistsJob::EntityExistsJob(AccountPtr account, const QString &path, QObjec
void EntityExistsJob::start()
{
sendRequest("HEAD", makeAccountUrl(path()));
setReply(headRequest(path()));
setupConnections(reply());
AbstractNetworkJob::start();
}
@@ -690,7 +700,8 @@ void JsonApiJob::start()
QList<QPair<QString, QString> > params = _additionalParams;
params << qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"));
url.setQueryItems(params);
sendRequest("GET", url, req);
setReply(davRequest("GET", url, req));
setupConnections(reply());
AbstractNetworkJob::start();
}

View File

@@ -94,132 +94,32 @@ int OwncloudPropagator::hardMaximumActiveJob()
return max;
}
PropagateItemJob::~PropagateItemJob()
{
if (auto p = propagator()) {
// Normally, every job should clean itself from the _activeJobList. So this should not be
// needed. But if a job has a bug or is deleted before the network jobs signal get received,
// we might risk end up with dangling pointer in the list which may cause crashes.
p->_activeJobList.removeAll(this);
}
}
static time_t getMinBlacklistTime()
{
return qMax(qgetenv("OWNCLOUD_BLACKLIST_TIME_MIN").toInt(),
25); // 25 seconds
}
static time_t getMaxBlacklistTime()
{
int v = qgetenv("OWNCLOUD_BLACKLIST_TIME_MAX").toInt();
if (v > 0)
return v;
return 24*60*60; // 1 day
}
/** Creates a blacklist entry, possibly taking into account an old one.
*
* The old entry may be invalid, then a fresh entry is created.
*/
static SyncJournalErrorBlacklistRecord createBlacklistEntry(
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item)
{
SyncJournalErrorBlacklistRecord entry;
entry._errorString = item._errorString;
entry._lastTryModtime = item._modtime;
entry._lastTryEtag = item._etag;
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
entry._file = item._file;
entry._renameTarget = item._renameTarget;
entry._retryCount = old._retryCount + 1;
static time_t minBlacklistTime(getMinBlacklistTime());
static time_t maxBlacklistTime(qMax(getMaxBlacklistTime(), minBlacklistTime));
// The factor of 5 feels natural: 25s, 2 min, 10 min, ~1h, ~5h, ~24h
entry._ignoreDuration = old._ignoreDuration * 5;
if( item._httpErrorCode == 403 ) {
qDebug() << "Probably firewall error: " << item._httpErrorCode << ", blacklisting up to 1h only";
entry._ignoreDuration = qMin(entry._ignoreDuration, time_t(60*60));
} else if( item._httpErrorCode == 413 || item._httpErrorCode == 415 ) {
qDebug() << "Fatal Error condition" << item._httpErrorCode << ", maximum blacklist ignore time!";
entry._ignoreDuration = maxBlacklistTime;
}
entry._ignoreDuration = qBound(minBlacklistTime, entry._ignoreDuration, maxBlacklistTime);
if( item._status == SyncFileItem::SoftError ) {
// Track these errors, but don't actively suppress them.
entry._ignoreDuration = 0;
}
return entry;
}
/** Updates, creates or removes a blacklist entry for the given item.
*
* May adjust the status or item._errorString.
* Returns whether the error should be suppressed.
*/
static void blacklistUpdate(SyncJournalDb* journal, SyncFileItem& item)
static bool blacklistCheck(SyncJournalDb* journal, const SyncFileItem& item)
{
SyncJournalErrorBlacklistRecord oldEntry = journal->errorBlacklistEntry(item._file);
SyncJournalErrorBlacklistRecord newEntry = SyncJournalErrorBlacklistRecord::update(oldEntry, item);
bool mayBlacklist =
item._errorMayBeBlacklisted // explicitly flagged for blacklisting
|| ((item._status == SyncFileItem::NormalError
|| item._status == SyncFileItem::SoftError)
&& item._httpErrorCode != 0 // or non-local error
);
// No new entry? Possibly remove the old one, then done.
if (!mayBlacklist) {
if (oldEntry.isValid()) {
journal->wipeErrorBlacklistEntry(item._file);
}
return;
if (newEntry.isValid()) {
journal->updateErrorBlacklistEntry(newEntry);
} else if (oldEntry.isValid()) {
journal->wipeErrorBlacklistEntry(item._file);
}
auto newEntry = createBlacklistEntry(oldEntry, item);
journal->updateErrorBlacklistEntry(newEntry);
// Suppress the error if it was and continues to be blacklisted.
// An ignoreDuration of 0 mean we're tracking the error, but not actively
// suppressing it.
if (item._hasBlacklistEntry && newEntry._ignoreDuration > 0) {
item._status = SyncFileItem::FileIgnored;
item._errorString.prepend(PropagateItemJob::tr("Continue blacklisting:") + " ");
qDebug() << "blacklisting " << item._file
<< " for " << newEntry._ignoreDuration
<< ", retry count " << newEntry._retryCount;
return;
}
// Some soft errors might become louder on repeat occurrence
if (item._status == SyncFileItem::SoftError
&& newEntry._retryCount > 1) {
qDebug() << "escalating soft error on " << item._file
<< " to normal error, " << item._httpErrorCode;
item._status = SyncFileItem::NormalError;
return;
}
// In some cases we add errors to the blacklist for tracking, but don't
// want to actively suppress them.
return newEntry.isValid() && newEntry._ignoreDuration > 0;
}
void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &errorString)
void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorString)
{
_item->_status = statusArg;
_state = Finished;
if (_item->_isRestoration) {
if( _item->_status == SyncFileItem::Success
|| _item->_status == SyncFileItem::Conflict) {
_item->_status = SyncFileItem::Restoration;
if( status == SyncFileItem::Success || status == SyncFileItem::Conflict) {
status = SyncFileItem::Restoration;
} else {
_item->_errorString += tr("; Restoration Failed: %1").arg(errorString);
}
@@ -230,18 +130,24 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error
}
if( propagator()->_abortRequested.fetchAndAddRelaxed(0) &&
(_item->_status == SyncFileItem::NormalError
|| _item->_status == SyncFileItem::FatalError)) {
(status == SyncFileItem::NormalError || status == SyncFileItem::FatalError)) {
// an abort request is ongoing. Change the status to Soft-Error
_item->_status = SyncFileItem::SoftError;
status = SyncFileItem::SoftError;
}
switch( _item->_status ) {
switch( status ) {
case SyncFileItem::SoftError:
case SyncFileItem::FatalError:
case SyncFileItem::NormalError:
// Check the blacklist, possibly adjusting the item (including its status)
blacklistUpdate(propagator()->_journal, *_item);
// For normal errors, we blacklist aggressively, otherwise only on
// explicit request.
if ((status == SyncFileItem::NormalError || _item->_errorMayBeBlacklisted)
&& blacklistCheck(propagator()->_journal, *_item)
&& _item->_hasBlacklistEntry) {
// do not error if the item was, and continues to be, blacklisted
status = SyncFileItem::FileIgnored;
_item->_errorString.prepend(tr("Continue blacklisting:") + " ");
}
break;
case SyncFileItem::Success:
case SyncFileItem::Restoration:
@@ -261,8 +167,10 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error
break;
}
_item->_status = status;
emit itemCompleted(_item);
emit finished(_item->_status);
emit finished(status);
}
/**
@@ -601,36 +509,6 @@ bool OwncloudPropagator::localFileNameClash( const QString& relFile )
return re;
}
bool OwncloudPropagator::hasCaseClashAccessibilityProblem(const QString &relfile)
{
#ifdef Q_OS_WIN
bool result = false;
const QString file( _localDir + relfile );
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
hFind = FindFirstFileW(reinterpret_cast<const wchar_t*>(file.utf16()), &FindFileData);
if (hFind != INVALID_HANDLE_VALUE) {
QString firstFile = QString::fromWCharArray( FindFileData.cFileName );
if (FindNextFile(hFind, &FindFileData)) {
QString secondFile = QString::fromWCharArray( FindFileData.cFileName );
// This extra check shouldn't be necessary, but ensures that there
// are two different filenames that are identical when case is ignored.
if (firstFile != secondFile
&& QString::compare(firstFile, secondFile, Qt::CaseInsensitive) == 0) {
result = true;
qDebug() << "Found two filepaths that only differ in case: " << firstFile << secondFile;
}
}
FindClose(hFind);
}
return result;
#else
Q_UNUSED(relfile);
return false;
#endif
}
QString OwncloudPropagator::getFilePath(const QString& tmp_file_name) const
{
return _localDir + tmp_file_name;
@@ -791,11 +669,9 @@ void PropagateDirectory::slotSubJobFinished(SyncFileItem::Status status)
if (status == SyncFileItem::FatalError ||
(wasFirstJob && status != SyncFileItem::Success && status != SyncFileItem::Restoration)) {
if (_state != Finished) {
abort();
_state = Finished;
emit finished(status);
}
abort();
_state = Finished;
emit finished(status);
return;
} else if (status == SyncFileItem::NormalError || status == SyncFileItem::SoftError) {
_hasError = status;
@@ -812,9 +688,6 @@ void PropagateDirectory::slotSubJobFinished(SyncFileItem::Status status)
void PropagateDirectory::finalize()
{
if (_state == Finished)
return;
bool ok = true;
if (!_item->isEmpty() && _hasError == SyncFileItem::NoStatus) {
if( !_item->_renameTarget.isEmpty() ) {

View File

@@ -160,7 +160,6 @@ private:
public:
PropagateItemJob(OwncloudPropagator* propagator, const SyncFileItemPtr &item)
: PropagatorJob(propagator), _item(item) {}
~PropagateItemJob();
bool scheduleNextJob() Q_DECL_OVERRIDE {
if (_state != NotYetStarted) {
@@ -257,7 +256,10 @@ public:
class OwncloudPropagator : public QObject {
Q_OBJECT
PropagateItemJob *createJob(const SyncFileItemPtr& item);
QScopedPointer<PropagateDirectory> _rootJob;
public:
const QString _localDir; // absolute path to the local directory. ends with '/'
const QString _remoteFolder; // remote folder, ends with '/'
@@ -305,23 +307,7 @@ public:
int hardMaximumActiveJob();
bool isInSharedDirectory(const QString& file);
/** Check whether a download would clash with an existing file
* in filesystems that are only case-preserving.
*/
bool localFileNameClash(const QString& relfile);
/** Check whether a file is properly accessible for upload.
*
* It is possible to create files with filenames that differ
* only by case in NTFS, but most operations such as stat and
* open only target one of these by default.
*
* When that happens, we want to avoid uploading incorrect data
* and give up on the file.
*/
bool hasCaseClashAccessibilityProblem(const QString& relfile);
QString getFilePath(const QString& tmp_file_name) const;
void abort() {
@@ -383,7 +369,6 @@ signals:
private:
AccountPtr _account;
QScopedPointer<PropagateDirectory> _rootJob;
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// access to signals which are protected in Qt4

View File

@@ -17,8 +17,6 @@
#include <QString>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include "ownsql.h"
#include "utility.h"
@@ -103,8 +101,8 @@ 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(QFileInfo(filename).dir().absolutePath());
if (freeSpace != -1 && freeSpace < 1000000) {
qint64 freeSpace = Utility::freeDiskSpace(filename);
if (freeSpace < 1000000) {
qDebug() << "Consistency check failed, disk space is low, aborting" << freeSpace;
close();
return false;

View File

@@ -100,11 +100,12 @@ void GETFileJob::start() {
}
if (_directDownloadUrl.isEmpty()) {
sendRequest("GET", makeDavUrl(path()), req);
setReply(davRequest("GET", path(), req));
} else {
// Use direct URL
sendRequest("GET", _directDownloadUrl, req);
setReply(davRequest("GET", _directDownloadUrl, req));
}
setupConnections(reply());
reply()->setReadBufferSize(16 * 1024); // keep low so we can easier limit the bandwidth
qDebug() << Q_FUNC_INFO << _bandwidthManager << _bandwidthChoked << _bandwidthLimited;
@@ -231,8 +232,6 @@ qint64 GETFileJob::currentDownloadPosition()
void GETFileJob::slotReadyRead()
{
if (!reply())
return;
int bufferSize = qMin(1024*8ll , reply()->bytesAvailable());
QByteArray buffer(bufferSize, Qt::Uninitialized);
@@ -769,7 +768,7 @@ void PropagateDownloadFile::downloadFinished()
// Apply the remote permissions
// Older server versions sometimes provide empty remote permissions
// see #4450 - don't adjust the write permissions there.
const int serverVersionGoodRemotePerm = Account::makeServerVersion(7, 0, 0);
const int serverVersionGoodRemotePerm = 0x070000; // 7.0.0
if (propagator()->account()->serverVersionInt() >= serverVersionGoodRemotePerm) {
FileSystem::setFileReadOnlyWeak(_tmpFile.fileName(),
!_item->_remotePerm.contains('W'));

View File

@@ -30,11 +30,8 @@ DeleteJob::DeleteJob(AccountPtr account, const QUrl& url, QObject* parent)
void DeleteJob::start()
{
QNetworkRequest req;
if (_url.isValid()) {
sendRequest("DELETE", _url, req);
} else {
sendRequest("DELETE", makeDavUrl(path()), req);
}
setReply(_url.isValid() ? davRequest("DELETE", _url, req) : davRequest("DELETE", path(), req));
setupConnections(reply());
if( reply()->error() != QNetworkReply::NoError ) {
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();

View File

@@ -43,11 +43,8 @@ void MoveJob::start()
for(auto it = _extraHeaders.constBegin(); it != _extraHeaders.constEnd(); ++it) {
req.setRawHeader(it.key(), it.value());
}
if (_url.isValid()) {
sendRequest("MOVE", _url, req);
} else {
sendRequest("MOVE", makeDavUrl(path()), req);
}
setReply(_url.isValid() ? davRequest("MOVE", _url, req) : davRequest("MOVE", path(), req));
setupConnections(reply());
if( reply()->error() != QNetworkReply::NoError ) {
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();

View File

@@ -73,11 +73,9 @@ void PUTFileJob::start() {
req.setRawHeader(it.key(), it.value());
}
if (_url.isValid()) {
sendRequest("PUT", _url, req, _device);
} else {
sendRequest("PUT", makeDavUrl(path()), req, _device);
}
setReply(_url.isValid() ? davRequest("PUT", _url, req, _device.data())
: davRequest("PUT", path(), req, _device.data()));
setupConnections(reply());
if( reply()->error() != QNetworkReply::NoError ) {
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
@@ -91,10 +89,10 @@ void PUTFileJob::start() {
// (workaround disabled on windows and mac because the binaries we ship have patched qt)
#if QT_VERSION < QT_VERSION_CHECK(4, 8, 7)
if (QLatin1String(qVersion()) < QLatin1String("4.8.7"))
connect(_device, SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
#elif QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 4, 2) && !defined Q_OS_WIN && !defined Q_OS_MAC
if (QLatin1String(qVersion()) < QLatin1String("5.4.2"))
connect(_device, SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
#endif
AbstractNetworkJob::start();
@@ -121,7 +119,8 @@ void PollJob::start()
QUrl accountUrl = account()->url();
QUrl finalUrl = QUrl::fromUserInput(accountUrl.scheme() + QLatin1String("://") + accountUrl.authority()
+ (path().startsWith('/') ? QLatin1String("") : QLatin1String("/")) + path());
sendRequest("GET", finalUrl);
setReply(getRequest(finalUrl));
setupConnections(reply());
connect(reply(), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(resetTimeout()));
AbstractNetworkJob::start();
}
@@ -199,13 +198,6 @@ void PropagateUploadFileCommon::start()
return;
}
// Check if the specific file can be accessed
if( propagator()->hasCaseClashAccessibilityProblem(_item->_file) ) {
done( SyncFileItem::NormalError, tr("File %1 cannot be uploaded because another file with the same name, differing only in case, exists")
.arg(QDir::toNativeSeparators(_item->_file)) );
return;
}
propagator()->_activeJobList.append(this);
if (!_deleteExisting) {

View File

@@ -86,7 +86,7 @@ class PUTFileJob : public AbstractNetworkJob {
Q_OBJECT
private:
QIODevice* _device;
QScopedPointer<QIODevice> _device;
QMap<QByteArray, QByteArray> _headers;
QString _errorString;
QUrl _url;
@@ -95,17 +95,11 @@ public:
// Takes ownership of the device
explicit PUTFileJob(AccountPtr account, const QString& path, QIODevice *device,
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
: AbstractNetworkJob(account, path, parent), _device(device), _headers(headers), _chunk(chunk)
{
_device->setParent(this);
}
: AbstractNetworkJob(account, path, parent), _device(device), _headers(headers), _chunk(chunk) {}
explicit PUTFileJob(AccountPtr account, const QUrl& url, QIODevice *device,
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
: AbstractNetworkJob(account, QString(), parent), _device(device), _headers(headers)
, _url(url), _chunk(chunk)
{
_device->setParent(this);
}
, _url(url), _chunk(chunk) {}
~PUTFileJob();
int _chunk;

View File

@@ -292,8 +292,6 @@ void PropagateUploadFileNG::startNextChunk()
_transmissionChecksumType, _transmissionChecksum);
}
headers["OC-Total-Length"] = QByteArray::number(fileSize);
auto job = new MoveJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), "/.file"),
destination, headers, this);
_jobs.append(job);

View File

@@ -145,7 +145,7 @@ void PropagateUploadFileV1::startNextChunk()
parallelChunkUpload = env != "false" && env != "0";
} else {
int versionNum = propagator()->account()->serverVersionInt();
if (versionNum < Account::makeServerVersion(8, 0, 3)) {
if (versionNum < 0x080003) {
// Disable parallel chunk upload severs older than 8.0.3 to avoid too many
// internal sever errors (#2743, #2938)
parallelChunkUpload = false;

View File

@@ -230,7 +230,7 @@ bool SyncEngine::checkErrorBlacklisting( SyncFileItem &item )
// If duration has expired, it's not blacklisted anymore
time_t now = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
if( now >= entry._lastTryTime + entry._ignoreDuration ) {
if( now > entry._lastTryTime + entry._ignoreDuration ) {
qDebug() << "blacklist entry for " << item._file << " has expired!";
return false;
}
@@ -930,7 +930,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
}
// Check for invalid character in old server version
if (_account->serverVersionInt() < Account::makeServerVersion(8, 1, 0)) {
if (_account->serverVersionInt() < 0x080100) {
// Server version older than 8.1 don't support these character in filename.
static const QRegExp invalidCharRx("[\\\\:?*\"<>|]");
for (auto it = syncItems.begin(); it != syncItems.end(); ++it) {
@@ -961,8 +961,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
&& _discoveryMainThread->_dataFingerprint != databaseFingerprint) {
qDebug() << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryMainThread->_dataFingerprint;
restoreOldFiles(syncItems);
} else if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2
&& _account->serverVersionInt() < Account::makeServerVersion(9, 1, 0)) {
} else if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2 && _account->serverVersionInt() < 0x090100) {
// The server before ownCloud 9.1 did not have the data-fingerprint property. So in that
// case we use heuristics to detect restored backup. This is disabled with newer version
// because this causes troubles to the user and is not as reliable as the data-fingerprint.

View File

@@ -103,6 +103,20 @@ SyncFileItem SyncJournalFileRecord::toSyncFileItem()
return item;
}
static time_t getMinBlacklistTime()
{
return qMax(qgetenv("OWNCLOUD_BLACKLIST_TIME_MIN").toInt(),
25); // 25 seconds
}
static time_t getMaxBlacklistTime()
{
int v = qgetenv("OWNCLOUD_BLACKLIST_TIME_MAX").toInt();
if (v > 0)
return v;
return 24*60*60; // 1 day
}
bool SyncJournalErrorBlacklistRecord::isValid() const
{
return ! _file.isEmpty()
@@ -110,6 +124,54 @@ bool SyncJournalErrorBlacklistRecord::isValid() const
&& _lastTryTime > 0;
}
SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item)
{
SyncJournalErrorBlacklistRecord entry;
bool mayBlacklist =
item._errorMayBeBlacklisted // explicitly flagged for blacklisting
|| (item._httpErrorCode != 0 // or non-local error
#ifdef OWNCLOUD_5XX_NO_BLACKLIST
&& item._httpErrorCode / 100 != 5 // In this configuration, never blacklist error 5xx
#endif
);
if (!mayBlacklist) {
qDebug() << "This error is not blacklisted " << item._httpErrorCode << item._errorMayBeBlacklisted;
return entry;
}
static time_t minBlacklistTime(getMinBlacklistTime());
static time_t maxBlacklistTime(qMax(getMaxBlacklistTime(), minBlacklistTime));
entry._retryCount = old._retryCount + 1;
entry._errorString = item._errorString;
entry._lastTryModtime = item._modtime;
entry._lastTryEtag = item._etag;
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
// The factor of 5 feels natural: 25s, 2 min, 10 min, ~1h, ~5h, ~24h
entry._ignoreDuration = old._ignoreDuration * 5;
entry._file = item._file;
entry._renameTarget = item._renameTarget;
if( item._httpErrorCode == 403 ) {
qDebug() << "Probably firewall error: " << item._httpErrorCode << ", blacklisting up to 1h only";
entry._ignoreDuration = qMin(entry._ignoreDuration, time_t(60*60));
} else if( item._httpErrorCode == 413 || item._httpErrorCode == 415 ) {
qDebug() << "Fatal Error condition" << item._httpErrorCode << ", maximum blacklist ignore time!";
entry._ignoreDuration = maxBlacklistTime;
}
entry._ignoreDuration = qBound(minBlacklistTime, entry._ignoreDuration, maxBlacklistTime);
qDebug() << "blacklisting " << item._file
<< " for " << entry._ignoreDuration
<< ", retry count " << entry._retryCount;
return entry;
}
bool operator==(const SyncJournalFileRecord & lhs,
const SyncJournalFileRecord & rhs)
{

View File

@@ -93,6 +93,15 @@ public:
QString _renameTarget;
bool isValid() const;
/** Takes an old blacklist entry and updates it for a new sync result.
*
* The old entry may be invalid, then a fresh entry is created.
* If the returned record is invalid, the file shall not be
* blacklisted.
*/
static SyncJournalErrorBlacklistRecord update(
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item);
};
}

View File

@@ -23,7 +23,6 @@
#ifndef TOKEN_AUTH_ONLY
#include <QtGui>
#endif
#include <QSslSocket>
#include "owncloudtheme.h"
@@ -298,13 +297,8 @@ QString Theme::gitSHA1() const
" on %3, %4 using Qt %5, %6</small></p>")
.arg(githubPrefix+gitSha1).arg(gitSha1.left(6))
.arg(__DATE__).arg(__TIME__)
.arg(QString::fromAscii(qVersion()))
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
.arg(QSslSocket::sslLibraryVersionString());
#else
.arg(QCoreApplication::translate("ownCloudTheme::about()", "built with %1").arg(
QString::fromAscii(OPENSSL_VERSION_TEXT)));
#endif
.arg(QT_VERSION_STR)
.arg(QString::fromAscii(OPENSSL_VERSION_TEXT));
#endif
return devString;
}

View File

@@ -122,8 +122,8 @@ QString Utility::octetsToString( qint64 octets )
QString s;
qreal value = octets;
// Whether we care about decimals: only for GB/MB and only
// if it's less than 10 units.
// Whether we care about decimals: only for GB and only
// if it's less than 10 GB.
bool round = true;
// do not display terra byte with the current units, as when
@@ -137,7 +137,6 @@ QString Utility::octetsToString( qint64 octets )
} else if (octets >= mb) {
s = QCoreApplication::translate("Utility", "%L1 MB");
value /= mb;
round = false;
} else if (octets >= kb) {
s = QCoreApplication::translate("Utility", "%L1 KB");
value /= kb;

View File

@@ -43,12 +43,6 @@ namespace Utility
OWNCLOUDSYNC_EXPORT QByteArray userAgentString();
OWNCLOUDSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName);
OWNCLOUDSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString& guiName, bool launch);
/**
* Return the amount of free space available.
*
* \a path must point to a directory
*/
OWNCLOUDSYNC_EXPORT qint64 freeDiskSpace(const QString &path);
/**

View File

@@ -536,7 +536,6 @@ public:
const FileInfo *fileInfo;
char payload;
int size;
bool aborted = false;
FakeGetReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
: QNetworkReply{parent} {
@@ -552,12 +551,6 @@ public:
}
Q_INVOKABLE void respond() {
if (aborted) {
setError(OperationCanceledError, "Operation Canceled");
emit metaDataChanged();
emit finished();
return;
}
payload = fileInfo->contentChar;
size = fileInfo->size;
setHeader(QNetworkRequest::ContentLengthHeader, size);
@@ -571,14 +564,8 @@ public:
emit finished();
}
void abort() override {
aborted = true;
}
qint64 bytesAvailable() const override {
if (aborted)
return 0;
return size + QIODevice::bytesAvailable();
}
void abort() override { }
qint64 bytesAvailable() const override { return size + QIODevice::bytesAvailable(); }
qint64 readData(char *data, qint64 maxlen) override {
qint64 len = std::min(qint64{size}, maxlen);
@@ -679,9 +666,8 @@ class FakeErrorReply : public QNetworkReply
{
Q_OBJECT
public:
FakeErrorReply(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
QObject *parent, int httpErrorCode)
: QNetworkReply{parent}, _httpErrorCode(httpErrorCode) {
FakeErrorReply(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
: QNetworkReply{parent} {
setRequest(request);
setUrl(request.url());
setOperation(op);
@@ -690,7 +676,7 @@ public:
}
Q_INVOKABLE void respond() {
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, _httpErrorCode);
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 500);
setError(InternalServerError, "Internal Server Fake Error");
emit metaDataChanged();
emit finished();
@@ -698,22 +684,18 @@ public:
void abort() override { }
qint64 readData(char *, qint64) override { return 0; }
int _httpErrorCode;
};
class FakeQNAM : public QNetworkAccessManager
{
FileInfo _remoteRootFileInfo;
FileInfo _uploadFileInfo;
// maps a path to an HTTP error
QHash<QString, int> _errorPaths;
QStringList _errorPaths;
public:
FakeQNAM(FileInfo initialRoot) : _remoteRootFileInfo{std::move(initialRoot)} { }
FileInfo &currentRemoteState() { return _remoteRootFileInfo; }
FileInfo &uploadState() { return _uploadFileInfo; }
QHash<QString, int> &errorPaths() { return _errorPaths; }
QStringList &errorPaths() { return _errorPaths; }
protected:
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
@@ -721,7 +703,7 @@ protected:
const QString fileName = getFilePathFromUrl(request.url());
Q_ASSERT(!fileName.isNull());
if (_errorPaths.contains(fileName))
return new FakeErrorReply{op, request, this, _errorPaths[fileName]};
return new FakeErrorReply{op, request, this};
bool isUpload = request.url().path().startsWith(sUploadUrl.path());
FileInfo &info = isUpload ? _uploadFileInfo : _remoteRootFileInfo;
@@ -730,13 +712,13 @@ protected:
if (verb == QLatin1String("PROPFIND"))
// Ignore outgoingData always returning somethign good enough, works for now.
return new FakePropfindReply{info, op, request, this};
else if (verb == QLatin1String("GET") || op == QNetworkAccessManager::GetOperation)
else if (verb == QLatin1String("GET"))
return new FakeGetReply{info, op, request, this};
else if (verb == QLatin1String("PUT") || op == QNetworkAccessManager::PutOperation)
else if (verb == QLatin1String("PUT"))
return new FakePutReply{info, op, request, outgoingData->readAll(), this};
else if (verb == QLatin1String("MKCOL"))
return new FakeMkcolReply{info, op, request, this};
else if (verb == QLatin1String("DELETE") || op == QNetworkAccessManager::DeleteOperation)
else if (verb == QLatin1String("DELETE"))
return new FakeDeleteReply{info, op, request, this};
else if (verb == QLatin1String("MOVE") && !isUpload)
return new FakeMoveReply{info, op, request, this};
@@ -816,13 +798,7 @@ public:
FileInfo currentRemoteState() { return _fakeQnam->currentRemoteState(); }
FileInfo &uploadState() { return _fakeQnam->uploadState(); }
struct ErrorList {
FakeQNAM *_qnam;
void append(const QString &path, int error = 500)
{ _qnam->errorPaths().insert(path, error); }
void clear() { _qnam->errorPaths().clear(); }
};
ErrorList serverErrorPaths() { return {_fakeQnam}; }
QStringList &serverErrorPaths() { return _fakeQnam->errorPaths(); }
QString localPath() const {
// SyncEngine wants a trailing slash

View File

@@ -225,40 +225,6 @@ private slots:
QCOMPARE(finishedSpy.size(), 1);
QCOMPARE(finishedSpy.first().first().toBool(), false);
}
void testDirDownloadWithError() {
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
fakeFolder.remoteModifier().mkdir("Y");
fakeFolder.remoteModifier().mkdir("Y/Z");
fakeFolder.remoteModifier().insert("Y/Z/d0");
fakeFolder.remoteModifier().insert("Y/Z/d1");
fakeFolder.remoteModifier().insert("Y/Z/d2");
fakeFolder.remoteModifier().insert("Y/Z/d3");
fakeFolder.remoteModifier().insert("Y/Z/d4");
fakeFolder.remoteModifier().insert("Y/Z/d5");
fakeFolder.remoteModifier().insert("Y/Z/d6");
fakeFolder.remoteModifier().insert("Y/Z/d7");
fakeFolder.remoteModifier().insert("Y/Z/d8");
fakeFolder.remoteModifier().insert("Y/Z/d9");
fakeFolder.serverErrorPaths().append("Y/Z/d2", 503); // 503 is a fatal error
fakeFolder.serverErrorPaths().append("Y/Z/d3", 503); // 503 is a fatal error
QVERIFY(!fakeFolder.syncOnce());
QCoreApplication::processEvents(); // should not crash
QSet<QString> seen;
for(const QList<QVariant> &args : completeSpy) {
auto item = args[0].value<SyncFileItemPtr>();
qDebug() << item->_file << item->_isDirectory << item->_status;
QVERIFY(!seen.contains(item->_file)); // signal only sent once per item
seen.insert(item->_file);
if (item->_file == "Y/Z/d2" || item->_file == "Y/Z/d3") {
QVERIFY(item->_status == SyncFileItem::FatalError);
}
QVERIFY(item->_file != "Y/Z/d9"); // we should have aborted the sync before d9 starts
}
}
};
QTEST_GUILESS_MAIN(TestSyncEngine)

View File

@@ -40,7 +40,7 @@ private slots:
QCOMPARE(octetsToString(10240) , QString("10 KB"));
QCOMPARE(octetsToString(123456) , QString("121 KB"));
QCOMPARE(octetsToString(1234567) , QString("1.2 MB"));
QCOMPARE(octetsToString(1234567) , QString("1 MB"));
QCOMPARE(octetsToString(12345678) , QString("12 MB"));
QCOMPARE(octetsToString(123456789) , QString("118 MB"));
QCOMPARE(octetsToString(1000LL*1000*1000 * 5) , QString("4.7 GB"));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff