зеркало из https://github.com/nextcloud/desktop.git
Merge remote-tracking branch 'origin/checksum_1.8' into 1.8
This commit is contained in:
Коммит
6b9e123816
|
@ -165,13 +165,13 @@ endif()
|
|||
find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
|
||||
|
||||
find_package(SQLite3 3.8.0 REQUIRED)
|
||||
# On some OS, we want to use our own, not the system sqlite
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
include_directories(BEFORE ${SQLITE3_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
find_package(ZLIB)
|
||||
|
||||
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
ChangeLog
|
||||
=========
|
||||
version 1.8.2 (release 2015-06-xx)
|
||||
* HTTP: Add the branding name to the UserAgent string.
|
||||
|
||||
version 1.8.1 (release 2015-05-07)
|
||||
* Make "operation canceled" error a soft error
|
||||
* Do not throw an error for files that are scheduled to be removed,
|
||||
|
|
|
@ -17,6 +17,7 @@ if( Qt5Core_FOUND )
|
|||
message(STATUS "Found Qt5 core, checking for further dependencies...")
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Xml REQUIRED)
|
||||
find_package(Qt5Concurrent REQUIRED)
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5WebKitWidgets REQUIRED)
|
||||
find_package(Qt5WebKit REQUIRED)
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#cmakedefine APPLICATION_EXECUTABLE "@APPLICATION_EXECUTABLE@"
|
||||
#cmakedefine APPLICATION_UPDATE_URL "@APPLICATION_UPDATE_URL@"
|
||||
|
||||
#cmakedefine ZLIB_FOUND @ZLIB_FOUND@
|
||||
|
||||
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
|
||||
#cmakedefine DATADIR "@DATADIR@"
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ set(libsync_SRCS
|
|||
theme.cpp
|
||||
utility.cpp
|
||||
ownsql.cpp
|
||||
transmissionchecksumvalidator.cpp
|
||||
creds/dummycredentials.cpp
|
||||
creds/abstractcredentials.cpp
|
||||
creds/credentialsfactory.cpp
|
||||
|
@ -141,6 +142,11 @@ if(NEON_FOUND)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
list(APPEND libsync_LINK_TARGETS ${ZLIB_LIBRARIES})
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
endif(ZLIB_FOUND)
|
||||
|
||||
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
|
||||
GENERATE_EXPORT_HEADER( ${synclib_NAME}
|
||||
BASE_NAME ${synclib_NAME}
|
||||
|
@ -151,9 +157,9 @@ GENERATE_EXPORT_HEADER( ${synclib_NAME}
|
|||
|
||||
|
||||
if(TOKEN_AUTH_ONLY)
|
||||
qt5_use_modules(${synclib_NAME} Network)
|
||||
qt5_use_modules(${synclib_NAME} Network Concurrent)
|
||||
else()
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network WebKitWidgets)
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network WebKitWidgets Concurrent)
|
||||
endif()
|
||||
|
||||
set_target_properties( ${synclib_NAME} PROPERTIES
|
||||
|
|
|
@ -49,6 +49,7 @@ static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications"
|
|||
static const char skipUpdateCheckC[] = "skipUpdateCheck";
|
||||
static const char geometryC[] = "geometry";
|
||||
static const char timeoutC[] = "timeout";
|
||||
static const char transmissionChecksumC[] = "transmissionChecksum";
|
||||
|
||||
static const char proxyHostC[] = "Proxy/host";
|
||||
static const char proxyTypeC[] = "Proxy/type";
|
||||
|
@ -118,6 +119,20 @@ int ConfigFile::timeout() const
|
|||
return settings.value(QLatin1String(timeoutC), 300).toInt(); // default to 5 min
|
||||
}
|
||||
|
||||
QString ConfigFile::transmissionChecksum() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
|
||||
QString checksum = settings.value(QLatin1String(transmissionChecksumC), QString()).toString();
|
||||
|
||||
if( checksum.isEmpty() ) {
|
||||
// if the config file setting is empty, maybe the Branding requires it.
|
||||
checksum = Theme::instance()->transmissionChecksum();
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
void ConfigFile::setOptionalDesktopNotifications(bool show)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
|
|
|
@ -103,6 +103,12 @@ public:
|
|||
|
||||
int timeout() const;
|
||||
|
||||
// send a checksum as a header along with the transmission or not.
|
||||
// possible values:
|
||||
// empty: no checksum calculated or expected.
|
||||
// or "Adler32", "MD5", "SHA1"
|
||||
QString transmissionChecksum() const;
|
||||
|
||||
void saveGeometry(QWidget *w);
|
||||
void restoreGeometry(QWidget *w);
|
||||
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
#include <QFileInfo>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#ifdef ZLIB_FOUND
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <qabstractfileengine.h>
|
||||
|
@ -394,4 +399,58 @@ QString FileSystem::fileSystemForPath(const QString & path)
|
|||
}
|
||||
#endif
|
||||
|
||||
#define BUFSIZE 1024*1024*10
|
||||
|
||||
static QByteArray readToCrypto( const QString& filename, QCryptographicHash::Algorithm algo )
|
||||
{
|
||||
const qint64 bufSize = BUFSIZE;
|
||||
QByteArray buf(bufSize,0);
|
||||
QByteArray arr;
|
||||
QCryptographicHash crypto( algo );
|
||||
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
qint64 size;
|
||||
while (!file.atEnd()) {
|
||||
size = file.read( buf.data(), bufSize );
|
||||
if( size > 0 ) {
|
||||
crypto.addData(buf.data(), size);
|
||||
}
|
||||
}
|
||||
arr = crypto.result().toHex();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
QByteArray FileSystem::calcMd5( const QString& filename )
|
||||
{
|
||||
return readToCrypto( filename, QCryptographicHash::Md5 );
|
||||
}
|
||||
|
||||
QByteArray FileSystem::calcSha1( const QString& filename )
|
||||
{
|
||||
return readToCrypto( filename, QCryptographicHash::Sha1 );
|
||||
}
|
||||
|
||||
#ifdef ZLIB_FOUND
|
||||
QByteArray FileSystem::calcAdler32( const QString& filename )
|
||||
{
|
||||
unsigned int adler = adler32(0L, Z_NULL, 0);
|
||||
const qint64 bufSize = BUFSIZE;
|
||||
QByteArray buf(bufSize, 0);
|
||||
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
qint64 size;
|
||||
while (!file.atEnd()) {
|
||||
size = file.read(buf.data(), bufSize);
|
||||
if( size > 0 )
|
||||
adler = adler32(adler, (const Bytef*) buf.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
return QByteArray::number( adler, 16 );
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace OCC
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QString>
|
||||
#include <ctime>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include <owncloudlib.h>
|
||||
|
||||
|
@ -121,4 +124,10 @@ bool openAndSeekFileSharedRead(QFile* file, QString* error, qint64 seek);
|
|||
QString fileSystemForPath(const QString & path);
|
||||
#endif
|
||||
|
||||
QByteArray calcMd5( const QString& fileName );
|
||||
QByteArray calcSha1( const QString& fileName );
|
||||
#ifdef ZLIB_FOUND
|
||||
QByteArray calcAdler32( const QString& fileName );
|
||||
#endif
|
||||
|
||||
}}
|
||||
|
|
|
@ -43,7 +43,6 @@ namespace OCC {
|
|||
|
||||
AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _duration(0)
|
||||
, _timedout(false)
|
||||
, _followRedirects(false)
|
||||
, _ignoreCredentialFailure(false)
|
||||
|
|
|
@ -259,6 +259,21 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
|
|||
{
|
||||
Q_ASSERT(std::is_sorted(items.begin(), items.end()));
|
||||
|
||||
/* Check and log the transmission checksum type */
|
||||
ConfigFile cfg;
|
||||
const QString checksumType = cfg.transmissionChecksum().toUpper();
|
||||
|
||||
/* if the checksum type is empty, it is not send. No error */
|
||||
if( !checksumType.isEmpty() ) {
|
||||
if( checksumType == checkSumAdlerUpperC ||
|
||||
checksumType == checkSumMD5C ||
|
||||
checksumType == checkSumSHA1C ) {
|
||||
qDebug() << "Client sends and expects transmission checksum type" << checksumType;
|
||||
} else {
|
||||
qWarning() << "Unknown transmission checksum type from config" << checksumType;
|
||||
}
|
||||
}
|
||||
|
||||
/* This builds all the job needed for the propagation.
|
||||
* Each directories is a PropagateDirectory job, which contains the files in it.
|
||||
* In order to do that we loop over the items. (which are sorted by destination)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
* for more details.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "owncloudpropagator_p.h"
|
||||
#include "propagatedownload.h"
|
||||
#include "networkjobs.h"
|
||||
|
@ -21,6 +22,8 @@
|
|||
#include "utility.h"
|
||||
#include "filesystem.h"
|
||||
#include "propagatorjobs.h"
|
||||
#include "transmissionchecksumvalidator.h"
|
||||
|
||||
#include <json.h>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QFileInfo>
|
||||
|
@ -483,7 +486,21 @@ void PropagateDownloadFileQNAM::slotGetFinished()
|
|||
return;
|
||||
}
|
||||
|
||||
downloadFinished();
|
||||
// Do checksum validation for the download. If there is no checksum header, the validator
|
||||
// will also emit the validated() signal to continue the flow in slot downloadFinished()
|
||||
// as this is (still) also correct.
|
||||
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(_tmpFile.fileName(), this);
|
||||
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(downloadFinished()));
|
||||
connect(validator, SIGNAL(validationFailed(QString)), this, SLOT(slotChecksumFail(QString)));
|
||||
validator->downloadValidation(job->reply()->rawHeader(checkSumHeaderC));
|
||||
|
||||
}
|
||||
|
||||
void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg )
|
||||
{
|
||||
_tmpFile.remove();
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, errMsg ); // tr("The file downloaded with a broken checksum, will be redownloaded."));
|
||||
}
|
||||
|
||||
QString makeConflictFileName(const QString &fn, const QDateTime &dt)
|
||||
|
|
|
@ -101,19 +101,21 @@ private slots:
|
|||
|
||||
class PropagateDownloadFileQNAM : public PropagateItemJob {
|
||||
Q_OBJECT
|
||||
QPointer<GETFileJob> _job;
|
||||
|
||||
// QFile *_file;
|
||||
QFile _tmpFile;
|
||||
public:
|
||||
PropagateDownloadFileQNAM(OwncloudPropagator* propagator,const SyncFileItem& item)
|
||||
: PropagateItemJob(propagator, item) {}
|
||||
void start() Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void slotGetFinished();
|
||||
void abort() Q_DECL_OVERRIDE;
|
||||
void downloadFinished();
|
||||
void slotDownloadProgress(qint64,qint64);
|
||||
void slotChecksumFail( const QString& errMsg );
|
||||
|
||||
private:
|
||||
QPointer<GETFileJob> _job;
|
||||
QFile _tmpFile;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
* for more details.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "propagateupload.h"
|
||||
#include "owncloudpropagator_p.h"
|
||||
#include "networkjobs.h"
|
||||
|
@ -21,6 +22,8 @@
|
|||
#include "utility.h"
|
||||
#include "filesystem.h"
|
||||
#include "propagatorjobs.h"
|
||||
#include "transmissionchecksumvalidator.h"
|
||||
|
||||
#include <json.h>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QFileInfo>
|
||||
|
@ -190,22 +193,51 @@ bool PollJob::finished()
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PropagateUploadFileQNAM::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString filePath = _propagator->getFilePath(_item._file);
|
||||
|
||||
// remember the modtime before checksumming to be able to detect a file
|
||||
// change during the checksum calculation
|
||||
_item._modtime = FileSystem::getModTime(filePath);
|
||||
|
||||
_stopWatch.start();
|
||||
|
||||
// do whatever is needed to add a checksum to the http upload request.
|
||||
// in any case, the validator will emit signal startUpload to let the flow
|
||||
// continue in slotStartUpload here.
|
||||
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(filePath, this);
|
||||
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(slotStartUpload(QByteArray)));
|
||||
validator->uploadValidation();
|
||||
}
|
||||
|
||||
void PropagateUploadFileQNAM::slotStartUpload(const QByteArray& checksum)
|
||||
{
|
||||
const QString fullFilePath(_propagator->getFilePath(_item._file));
|
||||
|
||||
_item._checksum = checksum;
|
||||
|
||||
if (!FileSystem::fileExists(fullFilePath)) {
|
||||
done(SyncFileItem::SoftError, tr("File Removed"));
|
||||
return;
|
||||
}
|
||||
_stopWatch.addLapTime(QLatin1String("Checksum"));
|
||||
|
||||
time_t prevModtime = _item._modtime; // the _item value was set in PropagateUploadFileQNAM::start()
|
||||
// but a potential checksum calculation could have taken some time during which the file could
|
||||
// have been changed again, so better check again here.
|
||||
|
||||
// Update the mtime and size, it might have changed since discovery.
|
||||
_item._modtime = FileSystem::getModTime(fullFilePath);
|
||||
if( prevModtime != _item._modtime ) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed."));
|
||||
return;
|
||||
}
|
||||
|
||||
quint64 fileSize = FileSystem::getSize(fullFilePath);
|
||||
_item._size = fileSize;
|
||||
|
||||
|
@ -432,6 +464,14 @@ void PropagateUploadFileQNAM::startNextChunk()
|
|||
if( currentChunkSize == 0 ) { // if the last chunk pretents to be 0, its actually the full chunk size.
|
||||
currentChunkSize = chunkSize();
|
||||
}
|
||||
if( !_item._checksum.isEmpty() ) {
|
||||
headers[checkSumHeaderC] = _item._checksum;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// checksum if its only one chunk
|
||||
if( !_item._checksum.isEmpty() ) {
|
||||
headers[checkSumHeaderC] = _item._checksum;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,6 +692,11 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
|||
// Well, the mtime was not set
|
||||
#endif
|
||||
}
|
||||
|
||||
// performance logging
|
||||
_item._requestDuration = _stopWatch.stop();
|
||||
qDebug() << "*==* duration UPLOAD" << _item._size << _stopWatch.durationOfLap(QLatin1String("Checksum")) << _item._requestDuration;
|
||||
|
||||
finalize(_item);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace OCC {
|
||||
class BandwidthManager;
|
||||
|
||||
|
@ -75,6 +76,8 @@ protected slots:
|
|||
|
||||
class PUTFileJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QScopedPointer<QIODevice> _device;
|
||||
QMap<QByteArray, QByteArray> _headers;
|
||||
QString _errorString;
|
||||
|
@ -146,6 +149,7 @@ signals:
|
|||
class PropagateUploadFileQNAM : public PropagateItemJob {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
/**
|
||||
* That's the start chunk that was stored in the database for resuming.
|
||||
* In the non-resuming case it is 0.
|
||||
|
@ -163,6 +167,10 @@ class PropagateUploadFileQNAM : public PropagateItemJob {
|
|||
QElapsedTimer _duration;
|
||||
QVector<PUTFileJob*> _jobs; /// network jobs that are currently in transit
|
||||
bool _finished; // Tells that all the jobs have been finished
|
||||
|
||||
// measure the performance of checksum calc and upload
|
||||
Utility::StopWatch _stopWatch;
|
||||
|
||||
public:
|
||||
PropagateUploadFileQNAM(OwncloudPropagator* propagator,const SyncFileItem& item)
|
||||
: PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false) {}
|
||||
|
@ -175,6 +183,8 @@ private slots:
|
|||
void startNextChunk();
|
||||
void finalize(const SyncFileItem&);
|
||||
void slotJobDestroyed(QObject *job);
|
||||
void slotStartUpload(const QByteArray &checksum);
|
||||
|
||||
private:
|
||||
void startPollJob(const QString& path);
|
||||
void abortWithError(SyncFileItem::Status status, const QString &error);
|
||||
|
|
|
@ -21,6 +21,22 @@
|
|||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* Tags for checksum headers.
|
||||
* They are here for being shared between Upload- and Download Job
|
||||
*/
|
||||
|
||||
// the header itself
|
||||
static const char checkSumHeaderC[] = "OC-Checksum";
|
||||
// ...and it's values
|
||||
static const char checkSumMD5C[] = "MD5";
|
||||
static const char checkSumSHA1C[] = "SHA1";
|
||||
static const char checkSumAdlerC[] = "Adler32";
|
||||
static const char checkSumAdlerUpperC[] = "ADLER32";
|
||||
|
||||
/**
|
||||
* Declaration of the other propagation jobs
|
||||
*/
|
||||
class PropagateLocalRemove : public PropagateItemJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -150,6 +150,7 @@ public:
|
|||
quint64 _inode;
|
||||
QByteArray _fileId;
|
||||
QByteArray _remotePerm;
|
||||
QByteArray _checksum;
|
||||
QString _directDownloadUrl;
|
||||
QString _directDownloadCookies;
|
||||
|
||||
|
|
|
@ -241,12 +241,17 @@ QString Theme::updateCheckUrl() const
|
|||
return QLatin1String("https://updates.owncloud.com/client/");
|
||||
}
|
||||
|
||||
QString Theme::transmissionChecksum() const
|
||||
{
|
||||
return QString::null; // No transmission by default.
|
||||
}
|
||||
|
||||
QString Theme::gitSHA1() const
|
||||
{
|
||||
QString devString;
|
||||
#ifdef GIT_SHA1
|
||||
const QString githubPrefix(QLatin1String(
|
||||
"https://github.com/owncloud/mirall/commit/"));
|
||||
"https://github.com/owncloud/client/commit/"));
|
||||
const QString gitSha1(QLatin1String(GIT_SHA1));
|
||||
devString = QCoreApplication::translate("ownCloudTheme::about()",
|
||||
"<p><small>Built from Git revision <a href=\"%1\">%2</a>"
|
||||
|
@ -389,5 +394,5 @@ bool Theme::wizardSelectiveSyncDefaultNothing() const
|
|||
}
|
||||
|
||||
|
||||
} // end namespace mirall
|
||||
} // end namespace client
|
||||
|
||||
|
|
|
@ -189,12 +189,19 @@ public:
|
|||
*/
|
||||
virtual QString updateCheckUrl() const;
|
||||
|
||||
|
||||
/**
|
||||
* When true, the setup wizard will show the selective sync dialog by default and default
|
||||
* to nothing selected
|
||||
*/
|
||||
virtual bool wizardSelectiveSyncDefaultNothing() const;
|
||||
/**
|
||||
* @brief Add an additional checksum header to PUT requests and compare them
|
||||
* if they come with GET requests.
|
||||
* This value sets the checksum type (SHA1, MD5 or Adler32) or is left empty
|
||||
* if no checksumming is wanted. In that case it can still be overwritten in
|
||||
* the client config file.
|
||||
*/
|
||||
virtual QString transmissionChecksum() const;
|
||||
|
||||
protected:
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "filesystem.h"
|
||||
#include "transmissionchecksumvalidator.h"
|
||||
#include "syncfileitem.h"
|
||||
#include "propagatorjobs.h"
|
||||
#include "configfile.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
TransmissionChecksumValidator::TransmissionChecksumValidator(const QString& filePath, QObject *parent)
|
||||
:QObject(parent),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::setChecksumType( const QByteArray& type )
|
||||
{
|
||||
_checksumType = type;
|
||||
}
|
||||
|
||||
QString TransmissionChecksumValidator::checksumType() const
|
||||
{
|
||||
QString checksumType = _checksumType;
|
||||
if( checksumType.isEmpty() ) {
|
||||
ConfigFile cfg;
|
||||
checksumType = cfg.transmissionChecksum();
|
||||
}
|
||||
|
||||
return checksumType;
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::uploadValidation()
|
||||
{
|
||||
const QString csType = checksumType();
|
||||
|
||||
if( csType.isEmpty() ) {
|
||||
// if there is no checksum defined, continue to upload
|
||||
emit validated(QByteArray());
|
||||
} else {
|
||||
// Calculate the checksum in a different thread first.
|
||||
|
||||
connect( &_watcher, SIGNAL(finished()),
|
||||
this, SLOT(slotUploadChecksumCalculated()));
|
||||
if( csType == checkSumMD5C ) {
|
||||
_checksumHeader = checkSumMD5C;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
|
||||
|
||||
} else if( csType == checkSumSHA1C ) {
|
||||
_checksumHeader = checkSumSHA1C;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, _filePath));
|
||||
}
|
||||
#ifdef ZLIB_FOUND
|
||||
else if( csType == checkSumAdlerC) {
|
||||
_checksumHeader = checkSumAdlerC;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// for an unknown checksum, continue to upload
|
||||
emit validated(QByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::slotUploadChecksumCalculated( )
|
||||
{
|
||||
QByteArray checksum = _watcher.future().result();
|
||||
|
||||
if( !checksum.isEmpty() ) {
|
||||
checksum.prepend( _checksumHeader );
|
||||
}
|
||||
|
||||
emit validated(checksum);
|
||||
}
|
||||
|
||||
|
||||
void TransmissionChecksumValidator::downloadValidation( const QByteArray& checksumHeader )
|
||||
{
|
||||
// if the incoming header is empty, there was no checksum header, and
|
||||
// no validation can happen. Just continue.
|
||||
const QString csType = checksumType();
|
||||
|
||||
// for empty checksum type, everything is valid.
|
||||
if( csType.isEmpty() ) {
|
||||
emit validated(QByteArray());
|
||||
return;
|
||||
}
|
||||
|
||||
int indx = checksumHeader.indexOf(':');
|
||||
if( indx < 0 ) {
|
||||
qDebug() << "Checksum header malformed:" << checksumHeader;
|
||||
emit validationFailed(tr("The checksum header is malformed.")); // show must go on - even not validated.
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray type = checksumHeader.left(indx).toUpper();
|
||||
_expectedHash = checksumHeader.mid(indx+1);
|
||||
|
||||
connect( &_watcher, SIGNAL(finished()), this, SLOT(slotDownloadChecksumCalculated()) );
|
||||
|
||||
// start the calculation in different thread
|
||||
if( type == checkSumMD5C ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
|
||||
} else if( type == checkSumSHA1C ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcSha1, _filePath));
|
||||
}
|
||||
#ifdef ZLIB_FOUND
|
||||
else if( type == checkSumAdlerUpperC ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
qDebug() << "Unknown checksum type" << type;
|
||||
emit validationFailed(tr("The checksum header is malformed."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::slotDownloadChecksumCalculated()
|
||||
{
|
||||
const QByteArray hash = _watcher.future().result();
|
||||
|
||||
if( hash != _expectedHash ) {
|
||||
emit validationFailed(tr("The downloaded file does not match the checksum, it will be resumed."));
|
||||
} else {
|
||||
// qDebug() << "Checksum checked and matching: " << _expectedHash;
|
||||
emit validated(hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class TransmissionChecksumValidator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TransmissionChecksumValidator(const QString& filePath, QObject *parent = 0);
|
||||
|
||||
/**
|
||||
* method to prepare a checksum for transmission and save it to the _checksum
|
||||
* member of the SyncFileItem *item.
|
||||
* The kind of requested checksum is taken from config. No need to set from outside.
|
||||
*
|
||||
* In any case of processing (checksum set, no checksum required and also unusual error)
|
||||
* the object will emit the signal validated(). The item->_checksum is than either
|
||||
* set to a proper value or empty.
|
||||
*/
|
||||
void uploadValidation();
|
||||
|
||||
/**
|
||||
* method to verify the checksum coming with requests in a checksum header. The required
|
||||
* checksum method is read from config.
|
||||
*
|
||||
* If no checksum is there, or if a correct checksum is there, the signal validated()
|
||||
* will be emitted. In case of any kind of error, the signal validationFailed() will
|
||||
* be emitted.
|
||||
*/
|
||||
void downloadValidation( const QByteArray& checksumHeader );
|
||||
|
||||
// This is only used in test cases (by now). This class reads the required
|
||||
// test case from the config file.
|
||||
void setChecksumType(const QByteArray &type );
|
||||
QString checksumType() const;
|
||||
|
||||
signals:
|
||||
void validated(const QByteArray& checksum);
|
||||
void validationFailed( const QString& errMsg );
|
||||
|
||||
private slots:
|
||||
void slotUploadChecksumCalculated();
|
||||
void slotDownloadChecksumCalculated();
|
||||
|
||||
private:
|
||||
QByteArray _checksumType;
|
||||
QByteArray _expectedHash;
|
||||
QByteArray _checksumHeader;
|
||||
|
||||
QString _filePath;
|
||||
|
||||
// watcher for the checksum calculation thread
|
||||
QFutureWatcher<QByteArray> _watcher;
|
||||
};
|
||||
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
#include "utility.h"
|
||||
|
||||
#include "version.h"
|
||||
#include "theme.h"
|
||||
|
||||
// Note: This file must compile without QtGui
|
||||
#include <QCoreApplication>
|
||||
|
@ -154,10 +155,19 @@ QString Utility::platform()
|
|||
|
||||
QByteArray Utility::userAgentString()
|
||||
{
|
||||
return QString::fromLatin1("Mozilla/5.0 (%1) mirall/%2")
|
||||
QString re = QString::fromLatin1("Mozilla/5.0 (%1) mirall/%2")
|
||||
.arg(Utility::platform())
|
||||
.arg(QLatin1String(MIRALL_STRINGIFY(MIRALL_VERSION)))
|
||||
.toLatin1();
|
||||
.arg(QLatin1String(MIRALL_STRINGIFY(MIRALL_VERSION)));
|
||||
|
||||
const QString appName = Theme::instance()->appName();
|
||||
|
||||
// this constant "ownCloud" is defined in the default OEM theming
|
||||
// that is used for the standard client. If it is changed there,
|
||||
// it needs to be adjusted here.
|
||||
if( appName != QLatin1String("ownCloud") ) {
|
||||
re += QString(" (%1)").arg(appName);
|
||||
}
|
||||
return re.toLatin1();
|
||||
}
|
||||
|
||||
bool Utility::hasLaunchOnStartup(const QString &appName)
|
||||
|
@ -401,10 +411,12 @@ void Utility::StopWatch::start()
|
|||
_timer.start();
|
||||
}
|
||||
|
||||
void Utility::StopWatch::stop()
|
||||
quint64 Utility::StopWatch::stop()
|
||||
{
|
||||
addLapTime(QLatin1String(STOPWATCH_END_TAG));
|
||||
quint64 duration = _timer.elapsed();
|
||||
_timer.invalidate();
|
||||
return duration;
|
||||
}
|
||||
|
||||
void Utility::StopWatch::reset()
|
||||
|
|
|
@ -105,7 +105,7 @@ namespace Utility
|
|||
QElapsedTimer _timer;
|
||||
public:
|
||||
void start();
|
||||
void stop();
|
||||
quint64 stop();
|
||||
quint64 addLapTime( const QString& lapName );
|
||||
void reset();
|
||||
|
||||
|
|
|
@ -33,4 +33,6 @@ owncloud_add_test(SyncFileItem "")
|
|||
owncloud_add_test(ConcatUrl "")
|
||||
|
||||
owncloud_add_test(XmlParse "")
|
||||
owncloud_add_test(FileSystem "")
|
||||
owncloud_add_test(TransChecksumValidator "")
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
This software is in the public domain, furnished "as is", without technical
|
||||
support, and with no warranty, express or implied, as to its usefulness for
|
||||
any purpose.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_TESTFILESYSTEM_H
|
||||
#define MIRALL_TESTFILESYSTEM_H
|
||||
|
||||
#include <QtTest>
|
||||
#include <QDebug>
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "utility.h"
|
||||
|
||||
using namespace OCC::Utility;
|
||||
using namespace OCC::FileSystem;
|
||||
|
||||
class TestFileSystem : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString _root;
|
||||
|
||||
|
||||
QByteArray shellSum( const QByteArray& cmd, const QString& file )
|
||||
{
|
||||
QProcess md5;
|
||||
QStringList args;
|
||||
args.append(file);
|
||||
md5.start(cmd, args);
|
||||
QByteArray sumShell;
|
||||
qDebug() << "File: "<< file;
|
||||
|
||||
if( md5.waitForFinished() ) {
|
||||
|
||||
sumShell = md5.readAll();
|
||||
sumShell = sumShell.left( sumShell.indexOf(' '));
|
||||
}
|
||||
return sumShell;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase() {
|
||||
qsrand(QTime::currentTime().msec());
|
||||
|
||||
QString subdir("test_"+QString::number(qrand()));
|
||||
_root = QDir::tempPath() + "/" + subdir;
|
||||
|
||||
QDir dir("/tmp");
|
||||
dir.mkdir(subdir);
|
||||
qDebug() << "creating test directory " << _root;
|
||||
}
|
||||
|
||||
void cleanupTestCase()
|
||||
{
|
||||
if( !_root.isEmpty() )
|
||||
system(QString("rm -rf "+_root).toUtf8());
|
||||
}
|
||||
|
||||
void testMd5Calc()
|
||||
{
|
||||
QString file( _root+"/file_a.bin");
|
||||
writeRandomFile(file);
|
||||
QFileInfo fi(file);
|
||||
QVERIFY(fi.exists());
|
||||
QByteArray sum = calcMd5(file);
|
||||
|
||||
QByteArray sSum = shellSum("/usr/bin/md5sum", file);
|
||||
qDebug() << "calulated" << sum << "versus md5sum:"<< sSum;
|
||||
QVERIFY(!sSum.isEmpty());
|
||||
QVERIFY(!sum.isEmpty());
|
||||
QVERIFY(sSum == sum );
|
||||
}
|
||||
|
||||
void testSha1Calc()
|
||||
{
|
||||
QString file( _root+"/file_b.bin");
|
||||
writeRandomFile(file);
|
||||
QFileInfo fi(file);
|
||||
QVERIFY(fi.exists());
|
||||
QByteArray sum = calcSha1(file);
|
||||
|
||||
QByteArray sSum = shellSum("/usr/bin/sha1sum", file);
|
||||
qDebug() << "calulated" << sum << "versus sha1sum:"<< sSum;
|
||||
QVERIFY(!sSum.isEmpty());
|
||||
QVERIFY(!sum.isEmpty());
|
||||
QVERIFY(sSum == sum );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* This software is in the public domain, furnished "as is", without technical
|
||||
* support, and with no warranty, express or implied, as to its usefulness for
|
||||
* any purpose.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtTest>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
#include "transmissionchecksumvalidator.h"
|
||||
#include "networkjobs.h"
|
||||
#include "utility.h"
|
||||
#include "filesystem.h"
|
||||
#include "propagatorjobs.h"
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
class TestTransChecksumValidator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QString _root;
|
||||
QString _testfile;
|
||||
QString _expectedError;
|
||||
QEventLoop _loop;
|
||||
QByteArray _expected;
|
||||
bool _successDown;
|
||||
bool _errorSeen;
|
||||
|
||||
void processAndWait() {
|
||||
_loop.processEvents();
|
||||
Utility::usleep(200000);
|
||||
_loop.processEvents();
|
||||
}
|
||||
|
||||
public slots:
|
||||
|
||||
void slotUpValidated(const QByteArray& checksum) {
|
||||
qDebug() << "Checksum: " << checksum;
|
||||
QVERIFY(_expected == checksum );
|
||||
}
|
||||
|
||||
void slotDownValidated() {
|
||||
_successDown = true;
|
||||
}
|
||||
|
||||
void slotDownError( const QString& errMsg ) {
|
||||
QVERIFY(_expectedError == errMsg );
|
||||
_errorSeen = true;
|
||||
}
|
||||
|
||||
private slots:
|
||||
|
||||
void initTestCase() {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
_root = QDir::tempPath() + "/" + "test_" + QString::number(qrand());
|
||||
QDir rootDir(_root);
|
||||
|
||||
rootDir.mkpath(_root );
|
||||
_testfile = _root+"/csFile";
|
||||
Utility::writeRandomFile( _testfile);
|
||||
|
||||
}
|
||||
|
||||
void testUploadChecksummingAdler() {
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType("Adler32");
|
||||
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
||||
|
||||
QString testfile = _testfile;
|
||||
_expected = "Adler32:"+FileSystem::calcAdler32( testfile );
|
||||
qDebug() << "XX Expected Checksum: " << _expected;
|
||||
vali->uploadValidation();
|
||||
|
||||
usleep(5000);
|
||||
|
||||
_loop.processEvents();
|
||||
delete vali;
|
||||
}
|
||||
|
||||
void testUploadChecksummingMd5() {
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType( OCC::checkSumMD5C );
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
||||
|
||||
_expected = checkSumMD5C;
|
||||
_expected.append(":"+FileSystem::calcMd5( _testfile ));
|
||||
vali->uploadValidation();
|
||||
|
||||
usleep(2000);
|
||||
|
||||
_loop.processEvents();
|
||||
delete vali;
|
||||
}
|
||||
|
||||
void testUploadChecksummingSha1() {
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType( OCC::checkSumSHA1C );
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
||||
|
||||
_expected = checkSumSHA1C;
|
||||
_expected.append(":"+FileSystem::calcSha1( _testfile ));
|
||||
|
||||
vali->uploadValidation();
|
||||
|
||||
usleep(2000);
|
||||
|
||||
_loop.processEvents();
|
||||
delete vali;
|
||||
}
|
||||
|
||||
void testDownloadChecksummingAdler() {
|
||||
|
||||
QByteArray adler = checkSumAdlerC;
|
||||
adler.append(":");
|
||||
adler.append(FileSystem::calcAdler32( _testfile ));
|
||||
_successDown = false;
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType("Adler32");
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotDownValidated()));
|
||||
connect(vali, SIGNAL(validationFailed(QString)), this, SLOT(slotDownError(QString)));
|
||||
vali->downloadValidation(adler);
|
||||
|
||||
usleep(2000);
|
||||
|
||||
_loop.processEvents();
|
||||
QVERIFY(_successDown);
|
||||
|
||||
_expectedError = QLatin1String("The downloaded file does not match the checksum, it will be resumed.");
|
||||
_errorSeen = false;
|
||||
vali->downloadValidation("Adler32:543345");
|
||||
usleep(2000);
|
||||
_loop.processEvents();
|
||||
QVERIFY(_errorSeen);
|
||||
|
||||
_expectedError = QLatin1String("The checksum header is malformed.");
|
||||
_errorSeen = false;
|
||||
vali->downloadValidation("Klaas32:543345");
|
||||
usleep(2000);
|
||||
_loop.processEvents();
|
||||
QVERIFY(_errorSeen);
|
||||
|
||||
delete vali;
|
||||
}
|
||||
|
||||
|
||||
void cleanupTestCase() {
|
||||
}
|
||||
};
|
Загрузка…
Ссылка в новой задаче