Merge pull request #3951 from ckamm/checksum
Checksums stored in database #3735
This commit is contained in:
Коммит
251679253a
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "capabilities.h"
|
||||
|
||||
#include "configfile.h"
|
||||
|
||||
#include <QVariantMap>
|
||||
|
||||
namespace OCC {
|
||||
|
@ -63,9 +65,28 @@ bool Capabilities::shareResharing() const
|
|||
return _capabilities["files_sharing"].toMap()["resharing"].toBool();
|
||||
}
|
||||
|
||||
QStringList Capabilities::supportedChecksumTypes() const
|
||||
QList<QByteArray> Capabilities::supportedChecksumTypesAdvertised() const
|
||||
{
|
||||
return QStringList();
|
||||
return QList<QByteArray>();
|
||||
}
|
||||
|
||||
QList<QByteArray> Capabilities::supportedChecksumTypes() const
|
||||
{
|
||||
auto list = supportedChecksumTypesAdvertised();
|
||||
QByteArray cfgType = ConfigFile().transmissionChecksum().toLatin1();
|
||||
if (!cfgType.isEmpty()) {
|
||||
list.prepend(cfgType);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
QByteArray Capabilities::preferredChecksumType() const
|
||||
{
|
||||
auto list = supportedChecksumTypes();
|
||||
if (list.isEmpty()) {
|
||||
return QByteArray();
|
||||
}
|
||||
return list.first();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,15 @@ public:
|
|||
bool sharePublicLinkEnforceExpireDate() const;
|
||||
int sharePublicLinkExpireDateDays() const;
|
||||
bool shareResharing() const;
|
||||
QStringList supportedChecksumTypes() const;
|
||||
|
||||
/// Returns the checksum types the server explicitly advertises
|
||||
QList<QByteArray> supportedChecksumTypesAdvertised() const;
|
||||
|
||||
/// Like supportedChecksumTypesRaw(), but includes the type from the config
|
||||
QList<QByteArray> supportedChecksumTypes() const;
|
||||
|
||||
/// Returns the checksum type that should be used for new uploads.
|
||||
QByteArray preferredChecksumType() const;
|
||||
|
||||
private:
|
||||
QVariantMap _capabilities;
|
||||
|
|
|
@ -277,14 +277,14 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
|
|||
|
||||
/* Check and log the transmission checksum type */
|
||||
ConfigFile cfg;
|
||||
const QString checksumType = cfg.transmissionChecksum().toUpper();
|
||||
const QString checksumType = cfg.transmissionChecksum();
|
||||
|
||||
/* if the checksum type is empty, it is not sent. No error */
|
||||
if( !checksumType.isEmpty() ) {
|
||||
if( checksumType == checkSumAdlerUpperC ||
|
||||
if( checksumType == checkSumAdlerC ||
|
||||
checksumType == checkSumMD5C ||
|
||||
checksumType == checkSumSHA1C ) {
|
||||
qDebug() << "Client sends and expects transmission checksum type" << checksumType;
|
||||
qDebug() << "Client sends transmission checksum type" << checksumType;
|
||||
} else {
|
||||
qWarning() << "Unknown transmission checksum type from config" << checksumType;
|
||||
}
|
||||
|
|
|
@ -316,6 +316,11 @@ void SqlQuery::bindValue(int pos, const QVariant& value)
|
|||
Q_ASSERT( res == SQLITE_OK );
|
||||
}
|
||||
|
||||
bool SqlQuery::nullValue(int index)
|
||||
{
|
||||
return sqlite3_column_type(_stmt, index) == SQLITE_NULL;
|
||||
}
|
||||
|
||||
QString SqlQuery::stringValue(int index)
|
||||
{
|
||||
return QString::fromUtf16(static_cast<const ushort*>(sqlite3_column_text16(_stmt, index)));
|
||||
|
|
|
@ -66,6 +66,9 @@ public:
|
|||
~SqlQuery();
|
||||
QString error() const;
|
||||
|
||||
/// Checks whether the value at the given column index is NULL
|
||||
bool nullValue(int index);
|
||||
|
||||
QString stringValue(int index);
|
||||
int intValue(int index);
|
||||
quint64 int64Value(int index);
|
||||
|
|
|
@ -351,7 +351,10 @@ void PropagateDownloadFileQNAM::start()
|
|||
if (_resumeStart == _item->_size) {
|
||||
qDebug() << "File is already complete, no need to download";
|
||||
_tmpFile.close();
|
||||
downloadFinished();
|
||||
|
||||
// Unfortunately we lost the checksum header, if any...
|
||||
QByteArray noChecksumData;
|
||||
downloadFinished(noChecksumData, noChecksumData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -532,11 +535,16 @@ void PropagateDownloadFileQNAM::slotGetFinished()
|
|||
// 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));
|
||||
|
||||
ValidateChecksumHeader *validator = new ValidateChecksumHeader(this);
|
||||
connect(validator, SIGNAL(validated(QByteArray,QByteArray)),
|
||||
SLOT(downloadFinished(QByteArray,QByteArray)));
|
||||
connect(validator, SIGNAL(validationFailed(QString)),
|
||||
SLOT(slotChecksumFail(QString)));
|
||||
auto checksumHeader = job->reply()->rawHeader(checkSumHeaderC);
|
||||
if (!downloadChecksumEnabled()) {
|
||||
checksumHeader.clear();
|
||||
}
|
||||
validator->start(_tmpFile.fileName(), checksumHeader);
|
||||
}
|
||||
|
||||
void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg )
|
||||
|
@ -613,8 +621,13 @@ static void handleRecallFile(const QString &fn)
|
|||
}
|
||||
} // end namespace
|
||||
|
||||
void PropagateDownloadFileQNAM::downloadFinished()
|
||||
void PropagateDownloadFileQNAM::downloadFinished(const QByteArray& checksumType, const QByteArray& checksum)
|
||||
{
|
||||
if (!checksumType.isEmpty()) {
|
||||
_item->_transmissionChecksum = checksum;
|
||||
_item->_transmissionChecksumType = checksumType;
|
||||
}
|
||||
|
||||
QString fn = _propagator->getFilePath(_item->_file);
|
||||
|
||||
// In case of file name clash, report an error
|
||||
|
|
|
@ -117,7 +117,7 @@ public:
|
|||
private slots:
|
||||
void slotGetFinished();
|
||||
void abort() Q_DECL_OVERRIDE;
|
||||
void downloadFinished();
|
||||
void downloadFinished(const QByteArray& checksumType, const QByteArray& checksum);
|
||||
void slotDownloadProgress(qint64,qint64);
|
||||
void slotChecksumFail( const QString& errMsg );
|
||||
|
||||
|
|
|
@ -205,30 +205,48 @@ void PropagateUploadFileQNAM::start()
|
|||
|
||||
_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);
|
||||
auto supportedChecksumTypes = _propagator->account()->capabilities().supportedChecksumTypes();
|
||||
|
||||
// If the config file does not specify a checksum type but the
|
||||
// server supports it choose a type based on that.
|
||||
if (validator->checksumType().isEmpty()) {
|
||||
QStringList checksumTypes = _propagator->account()->capabilities().supportedChecksumTypes();
|
||||
if (!checksumTypes.isEmpty()) {
|
||||
// TODO: We might want to prefer some types over others instead
|
||||
// of choosing the first.
|
||||
validator->setChecksumType(checksumTypes.first());
|
||||
// If we already have a checksum header and the checksum type is supported
|
||||
// by the server, we keep that - otherwise recompute.
|
||||
//
|
||||
// Note: Currently we *always* recompute because we usually only upload
|
||||
// files that have changed and thus have a new checksum. But if an earlier
|
||||
// phase computed a checksum, this is where we would make use of it.
|
||||
if (!_item->_transmissionChecksumType.isEmpty()) {
|
||||
if (supportedChecksumTypes.contains(_item->_transmissionChecksumType)) {
|
||||
// TODO: We could validate the old checksum and thereby determine whether
|
||||
// an upload is necessary or not.
|
||||
slotStartUpload(_item->_transmissionChecksumType, _item->_transmissionChecksum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(slotStartUpload(QByteArray)));
|
||||
validator->uploadValidation();
|
||||
// Compute a new checksum.
|
||||
auto computeChecksum = new ComputeChecksum(this);
|
||||
if (uploadChecksumEnabled()) {
|
||||
computeChecksum->setChecksumType(_propagator->account()->capabilities().preferredChecksumType());
|
||||
} else {
|
||||
computeChecksum->setChecksumType(QByteArray());
|
||||
}
|
||||
|
||||
connect(computeChecksum, SIGNAL(done(QByteArray,QByteArray)),
|
||||
SLOT(slotStartUpload(QByteArray,QByteArray)));
|
||||
computeChecksum->start(filePath);
|
||||
}
|
||||
|
||||
void PropagateUploadFileQNAM::slotStartUpload(const QByteArray& checksum)
|
||||
void PropagateUploadFileQNAM::slotStartUpload(const QByteArray& checksumType, const QByteArray& checksum)
|
||||
{
|
||||
// Store the computed checksum in the database, if different
|
||||
if (checksumType != _item->_transmissionChecksumType
|
||||
|| checksum != _item->_transmissionChecksum) {
|
||||
_item->_transmissionChecksum = checksum;
|
||||
_item->_transmissionChecksumType = checksumType;
|
||||
_propagator->_journal->updateFileRecordChecksum(
|
||||
_item->_file, checksum, checksumType);
|
||||
}
|
||||
|
||||
const QString fullFilePath = _propagator->getFilePath(_item->_file);
|
||||
_item->_checksum = checksum;
|
||||
|
||||
if (!FileSystem::fileExists(fullFilePath)) {
|
||||
done(SyncFileItem::SoftError, tr("File Removed"));
|
||||
|
@ -458,6 +476,7 @@ void PropagateUploadFileQNAM::startNextChunk()
|
|||
UploadDevice *device = new UploadDevice(&_propagator->_bandwidthManager);
|
||||
qint64 chunkStart = 0;
|
||||
qint64 currentChunkSize = fileSize;
|
||||
bool isFinalChunk = false;
|
||||
if (_chunkCount > 1) {
|
||||
int sendingChunk = (_currentChunk + _startChunk) % _chunkCount;
|
||||
// XOR with chunk size to make sure everything goes well if chunk size changes between runs
|
||||
|
@ -474,15 +493,16 @@ void PropagateUploadFileQNAM::startNextChunk()
|
|||
if( currentChunkSize == 0 ) { // if the last chunk pretends to be 0, its actually the full chunk size.
|
||||
currentChunkSize = chunkSize();
|
||||
}
|
||||
if( !_item->_checksum.isEmpty() ) {
|
||||
headers[checkSumHeaderC] = _item->_checksum;
|
||||
}
|
||||
isFinalChunk = true;
|
||||
}
|
||||
} else {
|
||||
// checksum if its only one chunk
|
||||
if( !_item->_checksum.isEmpty() ) {
|
||||
headers[checkSumHeaderC] = _item->_checksum;
|
||||
}
|
||||
// if there's only one chunk, it's the final one
|
||||
isFinalChunk = true;
|
||||
}
|
||||
|
||||
if (isFinalChunk && !_item->_transmissionChecksumType.isEmpty()) {
|
||||
headers[checkSumHeaderC] = makeChecksumHeader(
|
||||
_item->_transmissionChecksumType, _item->_transmissionChecksum);
|
||||
}
|
||||
|
||||
if (! device->prepareAndOpen(_propagator->getFilePath(_item->_file), chunkStart, currentChunkSize)) {
|
||||
|
|
|
@ -195,7 +195,7 @@ private slots:
|
|||
void startNextChunk();
|
||||
void finalize(const SyncFileItem&);
|
||||
void slotJobDestroyed(QObject *job);
|
||||
void slotStartUpload(const QByteArray &checksum);
|
||||
void slotStartUpload(const QByteArray& checksumType, const QByteArray& checksum);
|
||||
|
||||
private:
|
||||
void startPollJob(const QString& path);
|
||||
|
|
|
@ -32,7 +32,6 @@ static const char checkSumHeaderC[] = "OC-Checksum";
|
|||
static const char checkSumMD5C[] = "MD5";
|
||||
static const char checkSumSHA1C[] = "SHA1";
|
||||
static const char checkSumAdlerC[] = "Adler32";
|
||||
static const char checkSumAdlerUpperC[] = "ADLER32";
|
||||
|
||||
/**
|
||||
* @brief Declaration of the other propagation jobs
|
||||
|
|
|
@ -468,7 +468,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
|||
// the file system in the DB, this is to avoid spurious upload on the next sync
|
||||
item->_modtime = file->other.modtime;
|
||||
|
||||
_journal->setFileRecord(SyncJournalFileRecord(*item, _localPath + item->_file));
|
||||
_journal->updateFileRecordMetadata(SyncJournalFileRecord(*item, _localPath + item->_file));
|
||||
item->_should_update_metadata = false;
|
||||
}
|
||||
if (item->_isDirectory && file->should_update_metadata) {
|
||||
|
|
|
@ -165,7 +165,8 @@ public:
|
|||
quint64 _inode;
|
||||
QByteArray _fileId;
|
||||
QByteArray _remotePerm;
|
||||
QByteArray _checksum;
|
||||
QByteArray _transmissionChecksum;
|
||||
QByteArray _transmissionChecksumType;
|
||||
QString _directDownloadUrl;
|
||||
QString _directDownloadCookies;
|
||||
|
||||
|
|
|
@ -206,6 +206,8 @@ bool SyncJournalDb::checkConnect()
|
|||
"md5 VARCHAR(32)," /* This is the etag. Called md5 for compatibility */
|
||||
// updateDatabaseStructure() will add a fileid column
|
||||
// updateDatabaseStructure() will add a remotePerm column
|
||||
// updateDatabaseStructure() will add a transmissionChecksum column
|
||||
// updateDatabaseStructure() will add a transmissionChecksumTypeId column
|
||||
"PRIMARY KEY(phash)"
|
||||
");");
|
||||
|
||||
|
@ -271,6 +273,14 @@ bool SyncJournalDb::checkConnect()
|
|||
return sqlFail("Create table selectivesync", createQuery);
|
||||
}
|
||||
|
||||
// create the checksumtype table.
|
||||
createQuery.prepare("CREATE TABLE IF NOT EXISTS checksumtype("
|
||||
"id INTEGER PRIMARY KEY,"
|
||||
"name TEXT UNIQUE"
|
||||
");");
|
||||
if (!createQuery.exec()) {
|
||||
return sqlFail("Create table version", createQuery);
|
||||
}
|
||||
|
||||
|
||||
createQuery.prepare("CREATE TABLE IF NOT EXISTS version("
|
||||
|
@ -346,13 +356,30 @@ bool SyncJournalDb::checkConnect()
|
|||
}
|
||||
|
||||
_getFileRecordQuery.reset(new SqlQuery(_db));
|
||||
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM "
|
||||
"metadata WHERE phash=?1" );
|
||||
_getFileRecordQuery->prepare(
|
||||
"SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize,"
|
||||
" ignoredChildrenRemote, transmissionChecksum, checksumtype.name"
|
||||
" FROM metadata"
|
||||
" LEFT JOIN checksumtype ON metadata.transmissionChecksumTypeId == checksumtype.id"
|
||||
" WHERE phash=?1" );
|
||||
|
||||
_setFileRecordQuery.reset(new SqlQuery(_db) );
|
||||
_setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
|
||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote) "
|
||||
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14);" );
|
||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, transmissionChecksum, transmissionChecksumTypeId) "
|
||||
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14, ?15, ?16);" );
|
||||
|
||||
_setFileRecordChecksumQuery.reset(new SqlQuery(_db) );
|
||||
_setFileRecordChecksumQuery->prepare(
|
||||
"UPDATE metadata"
|
||||
" SET transmissionChecksum = ?2, transmissionChecksumTypeId = ?3"
|
||||
" WHERE phash == ?1;");
|
||||
|
||||
_setFileRecordMetadataQuery.reset(new SqlQuery(_db) );
|
||||
_setFileRecordMetadataQuery->prepare(
|
||||
"UPDATE metadata"
|
||||
" SET inode=?2, mode=?3, modtime=?4, type=?5, md5=?6, fileid=?7,"
|
||||
" remotePerm=?8, filesize=?9, ignoredChildrenRemote=?10"
|
||||
" WHERE phash == ?1;");
|
||||
|
||||
_getDownloadInfoQuery.reset(new SqlQuery(_db) );
|
||||
_getDownloadInfoQuery->prepare( "SELECT tmpfile, etag, errorcount FROM "
|
||||
|
@ -403,6 +430,12 @@ bool SyncJournalDb::checkConnect()
|
|||
_getSelectiveSyncListQuery.reset(new SqlQuery(_db));
|
||||
_getSelectiveSyncListQuery->prepare("SELECT path FROM selectivesync WHERE type=?1");
|
||||
|
||||
_getChecksumTypeIdQuery.reset(new SqlQuery(_db));
|
||||
_getChecksumTypeIdQuery->prepare("SELECT id FROM checksumtype WHERE name=?1");
|
||||
|
||||
_insertChecksumTypeQuery.reset(new SqlQuery(_db));
|
||||
_insertChecksumTypeQuery->prepare("INSERT OR IGNORE INTO checksumtype (name) VALUES (?1)");
|
||||
|
||||
// don't start a new transaction now
|
||||
commitInternal(QString("checkConnect End"), false);
|
||||
|
||||
|
@ -527,6 +560,27 @@ bool SyncJournalDb::updateMetadataTableStructure()
|
|||
}
|
||||
commitInternal("update database structure: add ignoredChildrenRemote col");
|
||||
}
|
||||
|
||||
if( columns.indexOf(QLatin1String("transmissionChecksum")) == -1 ) {
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE metadata ADD COLUMN transmissionChecksum TEXT;");
|
||||
if( !query.exec()) {
|
||||
sqlFail("updateMetadataTableStructure: add transmissionChecksum column", query);
|
||||
re = false;
|
||||
}
|
||||
commitInternal("update database structure: add transmissionChecksum col");
|
||||
}
|
||||
if( columns.indexOf(QLatin1String("transmissionChecksumTypeId")) == -1 ) {
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE metadata ADD COLUMN transmissionChecksumTypeId INTEGER;");
|
||||
if( !query.exec()) {
|
||||
sqlFail("updateMetadataTableStructure: add transmissionChecksumTypeId column", query);
|
||||
re = false;
|
||||
}
|
||||
commitInternal("update database structure: add transmissionChecksumTypeId col");
|
||||
}
|
||||
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
|
@ -627,6 +681,7 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
|
|||
if( fileId.isEmpty() ) fileId = "";
|
||||
QString remotePerm (record._remotePerm);
|
||||
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
|
||||
int checksumTypeId = mapChecksumType(record._transmissionChecksumType);
|
||||
_setFileRecordQuery->reset();
|
||||
_setFileRecordQuery->bindValue(1, QString::number(phash));
|
||||
_setFileRecordQuery->bindValue(2, plen);
|
||||
|
@ -642,6 +697,8 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
|
|||
_setFileRecordQuery->bindValue(12, remotePerm );
|
||||
_setFileRecordQuery->bindValue(13, record._fileSize );
|
||||
_setFileRecordQuery->bindValue(14, record._serverHasIgnoredFiles ? 1:0);
|
||||
_setFileRecordQuery->bindValue(15, record._transmissionChecksum );
|
||||
_setFileRecordQuery->bindValue(16, checksumTypeId );
|
||||
|
||||
if( !_setFileRecordQuery->exec() ) {
|
||||
qWarning() << "Error SQL statement setFileRecord: " << _setFileRecordQuery->lastQuery() << " :"
|
||||
|
@ -652,7 +709,8 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
|
|||
qDebug() << _setFileRecordQuery->lastQuery() << phash << plen << record._path << record._inode
|
||||
<< record._mode
|
||||
<< QString::number(Utility::qDateTimeToTime_t(record._modtime)) << QString::number(record._type)
|
||||
<< record._etag << record._fileId << record._remotePerm << record._fileSize << (record._serverHasIgnoredFiles ? 1:0);
|
||||
<< record._etag << record._fileId << record._remotePerm << record._fileSize << (record._serverHasIgnoredFiles ? 1:0)
|
||||
<< record._transmissionChecksum << record._transmissionChecksumType << checksumTypeId;
|
||||
|
||||
_setFileRecordQuery->reset();
|
||||
return true;
|
||||
|
@ -732,6 +790,10 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord( const QString& filename )
|
|||
rec._remotePerm = _getFileRecordQuery->baValue(9);
|
||||
rec._fileSize = _getFileRecordQuery->int64Value(10);
|
||||
rec._serverHasIgnoredFiles = (_getFileRecordQuery->intValue(11) > 0);
|
||||
rec._transmissionChecksum = _getFileRecordQuery->baValue(12);
|
||||
if( !_getFileRecordQuery->nullValue(13) ) {
|
||||
rec._transmissionChecksumType = _getFileRecordQuery->baValue(13);
|
||||
}
|
||||
} else {
|
||||
QString err = _getFileRecordQuery->error();
|
||||
qDebug() << "No journal entry found for " << filename;
|
||||
|
@ -820,6 +882,86 @@ int SyncJournalDb::getFileRecordCount()
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool SyncJournalDb::updateFileRecordChecksum(const QString& filename,
|
||||
const QByteArray& transmisisonChecksum,
|
||||
const QByteArray& transmissionChecksumType)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
qlonglong phash = getPHash(filename);
|
||||
if( !checkConnect() ) {
|
||||
qDebug() << "Failed to connect database.";
|
||||
return false;
|
||||
}
|
||||
|
||||
int checksumTypeId = mapChecksumType(transmissionChecksumType);
|
||||
auto & query = _setFileRecordChecksumQuery;
|
||||
|
||||
query->reset();
|
||||
query->bindValue(1, QString::number(phash));
|
||||
query->bindValue(2, transmisisonChecksum);
|
||||
query->bindValue(3, checksumTypeId);
|
||||
|
||||
if( !query->exec() ) {
|
||||
qWarning() << "Error SQL statement setFileRecordChecksumQuery: "
|
||||
<< query->lastQuery() << " :"
|
||||
<< query->error();
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << query->lastQuery() << phash << transmisisonChecksum
|
||||
<< transmissionChecksumType << checksumTypeId;
|
||||
|
||||
query->reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SyncJournalDb::updateFileRecordMetadata(const SyncJournalFileRecord& record)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
qlonglong phash = getPHash(record._path);
|
||||
QString etag( record._etag );
|
||||
if( etag.isEmpty() ) etag = "";
|
||||
QString fileId( record._fileId);
|
||||
if( fileId.isEmpty() ) fileId = "";
|
||||
QString remotePerm (record._remotePerm);
|
||||
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
|
||||
|
||||
if( !checkConnect() ) {
|
||||
qDebug() << "Failed to connect database.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto & query = _setFileRecordMetadataQuery;
|
||||
|
||||
query->reset();
|
||||
query->bindValue(1, QString::number(phash));
|
||||
query->bindValue(2, record._inode);
|
||||
query->bindValue(3, record._mode);
|
||||
query->bindValue(4, QString::number(Utility::qDateTimeToTime_t(record._modtime)));
|
||||
query->bindValue(5, QString::number(record._type));
|
||||
query->bindValue(6, etag);
|
||||
query->bindValue(7, fileId);
|
||||
query->bindValue(8, remotePerm);
|
||||
query->bindValue(9, record._fileSize);
|
||||
query->bindValue(10, record._serverHasIgnoredFiles ? 1 : 0);
|
||||
|
||||
if( !query->exec() ) {
|
||||
qWarning() << "Error SQL statement setFileRecordMetadataQuery: "
|
||||
<< query->lastQuery() << " :"
|
||||
<< query->error();
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << query->lastQuery() << record._path << record._inode << record._mode << record._modtime
|
||||
<< record._type << etag << fileId << remotePerm << record._fileSize
|
||||
<< record._serverHasIgnoredFiles;
|
||||
|
||||
query->reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void toDownloadInfo(SqlQuery &query, SyncJournalDb::DownloadInfo * res)
|
||||
{
|
||||
bool ok = true;
|
||||
|
@ -1389,6 +1531,39 @@ void SyncJournalDb::forceRemoteDiscoveryNextSyncLocked()
|
|||
}
|
||||
}
|
||||
|
||||
int SyncJournalDb::mapChecksumType(const QByteArray& checksumType)
|
||||
{
|
||||
if (checksumType.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ensure the checksum type is in the db
|
||||
_insertChecksumTypeQuery->reset();
|
||||
_insertChecksumTypeQuery->bindValue(1, checksumType);
|
||||
if( !_insertChecksumTypeQuery->exec() ) {
|
||||
qWarning() << "Error SQL statement insertChecksumType: "
|
||||
<< _insertChecksumTypeQuery->lastQuery() << " :"
|
||||
<< _insertChecksumTypeQuery->error();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Retrieve the id
|
||||
_getChecksumTypeIdQuery->reset();
|
||||
_getChecksumTypeIdQuery->bindValue(1, checksumType);
|
||||
if( !_getChecksumTypeIdQuery->exec() ) {
|
||||
qWarning() << "Error SQL statement getChecksumTypeId: "
|
||||
<< _getChecksumTypeIdQuery->lastQuery() << " :"
|
||||
<< _getChecksumTypeIdQuery->error();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !_getChecksumTypeIdQuery->next() ) {
|
||||
qDebug() << "No checksum type mapping found for" << checksumType;
|
||||
return 0;
|
||||
}
|
||||
return _getChecksumTypeIdQuery->intValue(0);
|
||||
}
|
||||
|
||||
|
||||
void SyncJournalDb::commit(const QString& context, bool startTrans)
|
||||
{
|
||||
|
|
|
@ -42,6 +42,10 @@ public:
|
|||
bool setFileRecord( const SyncJournalFileRecord& record );
|
||||
bool deleteFileRecord( const QString& filename, bool recursively = false );
|
||||
int getFileRecordCount();
|
||||
bool updateFileRecordChecksum(const QString& filename,
|
||||
const QByteArray& transmisisonChecksum,
|
||||
const QByteArray& transmissionChecksumType);
|
||||
bool updateFileRecordMetadata(const SyncJournalFileRecord& record);
|
||||
bool exists();
|
||||
void walCheckpoint();
|
||||
|
||||
|
@ -153,12 +157,19 @@ private:
|
|||
// Same as forceRemoteDiscoveryNextSync but without acquiring the lock
|
||||
void forceRemoteDiscoveryNextSyncLocked();
|
||||
|
||||
// Returns the integer id of the checksum type
|
||||
//
|
||||
// Returns 0 on failure and for empty checksum types.
|
||||
int mapChecksumType(const QByteArray& checksumType);
|
||||
|
||||
SqlDatabase _db;
|
||||
QString _dbFile;
|
||||
QMutex _mutex; // Public functions are protected with the mutex.
|
||||
int _transaction;
|
||||
QScopedPointer<SqlQuery> _getFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _setFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _setFileRecordChecksumQuery;
|
||||
QScopedPointer<SqlQuery> _setFileRecordMetadataQuery;
|
||||
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _setDownloadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _deleteDownloadInfoQuery;
|
||||
|
@ -170,6 +181,8 @@ private:
|
|||
QScopedPointer<SqlQuery> _getErrorBlacklistQuery;
|
||||
QScopedPointer<SqlQuery> _setErrorBlacklistQuery;
|
||||
QScopedPointer<SqlQuery> _getSelectiveSyncListQuery;
|
||||
QScopedPointer<SqlQuery> _getChecksumTypeIdQuery;
|
||||
QScopedPointer<SqlQuery> _insertChecksumTypeQuery;
|
||||
|
||||
/* This is the list of paths we called avoidReadFromDbOnNextSync on.
|
||||
* It means that they should not be written to the DB in any case since doing
|
||||
|
|
|
@ -35,7 +35,9 @@ SyncJournalFileRecord::SyncJournalFileRecord()
|
|||
SyncJournalFileRecord::SyncJournalFileRecord(const SyncFileItem &item, const QString &localFileName)
|
||||
: _path(item._file), _modtime(Utility::qDateTimeFromTime_t(item._modtime)),
|
||||
_type(item._type), _etag(item._etag), _fileId(item._fileId), _fileSize(item._size),
|
||||
_remotePerm(item._remotePerm), _mode(0), _serverHasIgnoredFiles(item._serverHasIgnoredFiles)
|
||||
_remotePerm(item._remotePerm), _mode(0), _serverHasIgnoredFiles(item._serverHasIgnoredFiles),
|
||||
_transmissionChecksum(item._transmissionChecksum),
|
||||
_transmissionChecksumType(item._transmissionChecksumType)
|
||||
{
|
||||
// use the "old" inode coming with the item for the case where the
|
||||
// filesystem stat fails. That can happen if the the file was removed
|
||||
|
@ -154,9 +156,12 @@ bool operator==(const SyncJournalFileRecord & lhs,
|
|||
&& lhs._type == rhs._type
|
||||
&& lhs._etag == rhs._etag
|
||||
&& lhs._fileId == rhs._fileId
|
||||
&& lhs._fileSize == rhs._fileSize
|
||||
&& lhs._remotePerm == rhs._remotePerm
|
||||
&& lhs._mode == rhs._mode
|
||||
&& lhs._fileSize == rhs._fileSize;
|
||||
&& lhs._serverHasIgnoredFiles == rhs._serverHasIgnoredFiles
|
||||
&& lhs._transmissionChecksum == rhs._transmissionChecksum
|
||||
&& lhs._transmissionChecksumType == rhs._transmissionChecksumType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ public:
|
|||
QByteArray _remotePerm;
|
||||
int _mode;
|
||||
bool _serverHasIgnoredFiles;
|
||||
QByteArray _transmissionChecksum;
|
||||
QByteArray _transmissionChecksumType;
|
||||
};
|
||||
|
||||
bool OWNCLOUDSYNC_EXPORT
|
||||
|
|
|
@ -16,133 +16,139 @@
|
|||
#include "transmissionchecksumvalidator.h"
|
||||
#include "syncfileitem.h"
|
||||
#include "propagatorjobs.h"
|
||||
#include "configfile.h"
|
||||
#include "account.h"
|
||||
|
||||
#include <qtconcurrentrun.h>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
TransmissionChecksumValidator::TransmissionChecksumValidator(const QString& filePath, QObject *parent)
|
||||
: QObject(parent),
|
||||
_filePath(filePath)
|
||||
QByteArray makeChecksumHeader(const QByteArray& checksumType, const QByteArray& checksum)
|
||||
{
|
||||
// If the config file specifies a checksum type, use that.
|
||||
ConfigFile cfg;
|
||||
_checksumType = cfg.transmissionChecksum();
|
||||
QByteArray header = checksumType;
|
||||
header.append(':');
|
||||
header.append(checksum);
|
||||
return header;
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::setChecksumType(const QString& type)
|
||||
bool parseChecksumHeader(const QByteArray& header, QByteArray* type, QByteArray* checksum)
|
||||
{
|
||||
if (header.isEmpty()) {
|
||||
type->clear();
|
||||
checksum->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto idx = header.indexOf(':');
|
||||
if (idx < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*type = header.left(idx);
|
||||
*checksum = header.mid(idx + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uploadChecksumEnabled()
|
||||
{
|
||||
static bool enabled = qgetenv("OWNCLOUD_DISABLE_CHECKSUM_UPLOAD").isEmpty();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
bool downloadChecksumEnabled()
|
||||
{
|
||||
static bool enabled = qgetenv("OWNCLOUD_DISABLE_CHECKSUM_DOWNLOAD").isEmpty();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
ComputeChecksum::ComputeChecksum(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ComputeChecksum::setChecksumType(const QByteArray& type)
|
||||
{
|
||||
_checksumType = type;
|
||||
}
|
||||
|
||||
QString TransmissionChecksumValidator::checksumType() const
|
||||
QByteArray ComputeChecksum::checksumType() const
|
||||
{
|
||||
return _checksumType;
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::uploadValidation()
|
||||
void ComputeChecksum::start(const QString& filePath)
|
||||
{
|
||||
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.
|
||||
// Calculate the checksum in a different thread first.
|
||||
connect( &_watcher, SIGNAL(finished()),
|
||||
this, SLOT(slotCalculationDone()),
|
||||
Qt::UniqueConnection );
|
||||
if( csType == checkSumMD5C ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, filePath));
|
||||
|
||||
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));
|
||||
} else if( csType == checkSumSHA1C ) {
|
||||
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, filePath));
|
||||
}
|
||||
#ifdef ZLIB_FOUND
|
||||
else if( type == checkSumAdlerUpperC ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
||||
else if( csType == checkSumAdlerC) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, filePath));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
qDebug() << "Unknown checksum type" << type;
|
||||
// for an unknown checksum or no checksum, we're done right now
|
||||
if( !csType.isEmpty() ) {
|
||||
qDebug() << "Unknown checksum type:" << csType;
|
||||
}
|
||||
emit done(QByteArray(), QByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeChecksum::slotCalculationDone()
|
||||
{
|
||||
QByteArray checksum = _watcher.future().result();
|
||||
emit done(_checksumType, checksum);
|
||||
}
|
||||
|
||||
|
||||
ValidateChecksumHeader::ValidateChecksumHeader(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ValidateChecksumHeader::start(const QString& filePath, const QByteArray& checksumHeader)
|
||||
{
|
||||
// If the incoming header is empty no validation can happen. Just continue.
|
||||
if( checksumHeader.isEmpty() ) {
|
||||
emit validated(QByteArray(), QByteArray());
|
||||
return;
|
||||
}
|
||||
|
||||
if( !parseChecksumHeader(checksumHeader, &_expectedChecksumType, &_expectedChecksum) ) {
|
||||
qDebug() << "Checksum header malformed:" << checksumHeader;
|
||||
emit validationFailed(tr("The checksum header is malformed."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto calculator = new ComputeChecksum(this);
|
||||
calculator->setChecksumType(_expectedChecksumType);
|
||||
connect(calculator, SIGNAL(done(QByteArray,QByteArray)),
|
||||
SLOT(slotChecksumCalculated(QByteArray,QByteArray)));
|
||||
calculator->start(filePath);
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::slotDownloadChecksumCalculated()
|
||||
void ValidateChecksumHeader::slotChecksumCalculated(const QByteArray& checksumType,
|
||||
const QByteArray& checksum)
|
||||
{
|
||||
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);
|
||||
if( checksumType != _expectedChecksumType ) {
|
||||
emit validationFailed(tr("The checksum header contained an unknown checksum type '%1'").arg(
|
||||
QString::fromLatin1(_expectedChecksumType)));
|
||||
return;
|
||||
}
|
||||
if( checksum != _expectedChecksum ) {
|
||||
emit validationFailed(tr("The downloaded file does not match the checksum, it will be resumed."));
|
||||
return;
|
||||
}
|
||||
emit validated(checksumType, checksum);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,62 +23,84 @@
|
|||
|
||||
namespace OCC {
|
||||
|
||||
/// Creates a checksum header from type and value.
|
||||
QByteArray makeChecksumHeader(const QByteArray& checksumType, const QByteArray& checksum);
|
||||
|
||||
/// Parses a checksum header
|
||||
bool parseChecksumHeader(const QByteArray& header, QByteArray* type, QByteArray* checksum);
|
||||
|
||||
/// Checks OWNCLOUD_DISABLE_CHECKSUM_UPLOAD
|
||||
bool uploadChecksumEnabled();
|
||||
|
||||
/// Checks OWNCLOUD_DISABLE_CHECKSUM_DOWNLOAD
|
||||
bool downloadChecksumEnabled();
|
||||
|
||||
/**
|
||||
* @brief The TransmissionChecksumValidator class
|
||||
* @ingroup libsync
|
||||
* Computes the checksum of a file.
|
||||
* \ingroup libsync
|
||||
*/
|
||||
class OWNCLOUDSYNC_EXPORT TransmissionChecksumValidator : public QObject
|
||||
class OWNCLOUDSYNC_EXPORT ComputeChecksum : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TransmissionChecksumValidator(const QString& filePath, QObject *parent = 0);
|
||||
explicit ComputeChecksum(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.
|
||||
* Sets the checksum type to be used. The default is empty.
|
||||
*/
|
||||
void setChecksumType(const QByteArray& type);
|
||||
|
||||
QByteArray checksumType() const;
|
||||
|
||||
/**
|
||||
* Computes the checksum for the given file path.
|
||||
*
|
||||
* 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.
|
||||
* done() is emitted when the calculation finishes.
|
||||
*/
|
||||
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 );
|
||||
|
||||
/**
|
||||
* By default the checksum type is read from the config file, but can be overridden
|
||||
* with this method.
|
||||
*/
|
||||
void setChecksumType(const QString& type);
|
||||
|
||||
QString checksumType() const;
|
||||
void start(const QString& filePath);
|
||||
|
||||
signals:
|
||||
void validated(const QByteArray& checksum);
|
||||
void validationFailed( const QString& errMsg );
|
||||
void done(const QByteArray& checksumType, const QByteArray& checksum);
|
||||
|
||||
private slots:
|
||||
void slotUploadChecksumCalculated();
|
||||
void slotDownloadChecksumCalculated();
|
||||
void slotCalculationDone();
|
||||
|
||||
private:
|
||||
QString _checksumType;
|
||||
QByteArray _expectedHash;
|
||||
QByteArray _checksumHeader;
|
||||
|
||||
QString _filePath;
|
||||
QByteArray _checksumType;
|
||||
|
||||
// watcher for the checksum calculation thread
|
||||
QFutureWatcher<QByteArray> _watcher;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether a file's checksum matches the expected value.
|
||||
* @ingroup libsync
|
||||
*/
|
||||
class OWNCLOUDSYNC_EXPORT ValidateChecksumHeader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ValidateChecksumHeader(QObject *parent = 0);
|
||||
|
||||
/**
|
||||
* Check a file's actual checksum against the provided checksumHeader
|
||||
*
|
||||
* 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 start(const QString& filePath, const QByteArray& checksumHeader);
|
||||
|
||||
signals:
|
||||
void validated(const QByteArray& checksumType, const QByteArray& checksum);
|
||||
void validationFailed( const QString& errMsg );
|
||||
|
||||
private slots:
|
||||
void slotChecksumCalculated(const QByteArray& checksumType, const QByteArray& checksum);
|
||||
|
||||
private:
|
||||
QByteArray _expectedChecksumType;
|
||||
QByteArray _expectedChecksum;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -60,16 +60,63 @@ private slots:
|
|||
record._remotePerm = "744";
|
||||
record._mode = -17;
|
||||
record._fileSize = 213089055;
|
||||
record._transmissionChecksum = "mychecksum";
|
||||
record._transmissionChecksumType = "MD5";
|
||||
QVERIFY(_db.setFileRecord(record));
|
||||
|
||||
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo");
|
||||
QVERIFY(storedRecord == record);
|
||||
|
||||
// Update checksum
|
||||
record._transmissionChecksum = "newchecksum";
|
||||
record._transmissionChecksumType = "Adler32";
|
||||
_db.updateFileRecordChecksum("foo", record._transmissionChecksum, record._transmissionChecksumType);
|
||||
storedRecord = _db.getFileRecord("foo");
|
||||
QVERIFY(storedRecord == record);
|
||||
|
||||
// Update metadata
|
||||
record._inode = 12345;
|
||||
record._modtime = dropMsecs(QDateTime::currentDateTime().addDays(1));
|
||||
record._type = 7;
|
||||
record._etag = "789FFF";
|
||||
record._fileId = "efg";
|
||||
record._remotePerm = "777";
|
||||
record._mode = 12;
|
||||
record._fileSize = 289055;
|
||||
_db.updateFileRecordMetadata(record);
|
||||
storedRecord = _db.getFileRecord("foo");
|
||||
QVERIFY(storedRecord == record);
|
||||
|
||||
QVERIFY(_db.deleteFileRecord("foo"));
|
||||
record = _db.getFileRecord("foo");
|
||||
QVERIFY(!record.isValid());
|
||||
}
|
||||
|
||||
void testFileRecordChecksum()
|
||||
{
|
||||
// Try with and without a checksum
|
||||
{
|
||||
SyncJournalFileRecord record;
|
||||
record._path = "foo-checksum";
|
||||
record._remotePerm = "744";
|
||||
record._transmissionChecksum = "mychecksum";
|
||||
record._transmissionChecksumType = "MD5";
|
||||
QVERIFY(_db.setFileRecord(record));
|
||||
|
||||
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo-checksum");
|
||||
QVERIFY(storedRecord == record);
|
||||
}
|
||||
{
|
||||
SyncJournalFileRecord record;
|
||||
record._path = "foo-nochecksum";
|
||||
record._remotePerm = "744";
|
||||
QVERIFY(_db.setFileRecord(record));
|
||||
|
||||
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo-nochecksum");
|
||||
QVERIFY(storedRecord == record);
|
||||
}
|
||||
}
|
||||
|
||||
void testDownloadInfo()
|
||||
{
|
||||
typedef SyncJournalDb::DownloadInfo Info;
|
||||
|
|
|
@ -33,14 +33,16 @@ using namespace OCC;
|
|||
QString _testfile;
|
||||
QString _expectedError;
|
||||
QByteArray _expected;
|
||||
QByteArray _expectedType;
|
||||
bool _successDown;
|
||||
bool _errorSeen;
|
||||
|
||||
public slots:
|
||||
|
||||
void slotUpValidated(const QByteArray& checksum) {
|
||||
void slotUpValidated(const QByteArray& type, const QByteArray& checksum) {
|
||||
qDebug() << "Checksum: " << checksum;
|
||||
QVERIFY(_expected == checksum );
|
||||
QVERIFY(_expectedType == type );
|
||||
}
|
||||
|
||||
void slotDownValidated() {
|
||||
|
@ -62,23 +64,22 @@ using namespace OCC;
|
|||
rootDir.mkpath(_root );
|
||||
_testfile = _root+"/csFile";
|
||||
Utility::writeRandomFile( _testfile);
|
||||
|
||||
}
|
||||
|
||||
void testUploadChecksummingAdler() {
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType("Adler32");
|
||||
ComputeChecksum *vali = new ComputeChecksum(this);
|
||||
_expectedType = "Adler32";
|
||||
vali->setChecksumType(_expectedType);
|
||||
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
||||
connect(vali, SIGNAL(done(QByteArray,QByteArray)), SLOT(slotUpValidated(QByteArray,QByteArray)));
|
||||
|
||||
QString testfile = _testfile;
|
||||
_expected = "Adler32:"+FileSystem::calcAdler32( testfile );
|
||||
_expected = FileSystem::calcAdler32( _testfile );
|
||||
qDebug() << "XX Expected Checksum: " << _expected;
|
||||
vali->uploadValidation();
|
||||
vali->start(_testfile);
|
||||
|
||||
QEventLoop loop;
|
||||
connect(vali, SIGNAL(validated(QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
|
||||
connect(vali, SIGNAL(done(QByteArray,QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
|
||||
loop.exec();
|
||||
|
||||
delete vali;
|
||||
|
@ -86,16 +87,16 @@ using namespace OCC;
|
|||
|
||||
void testUploadChecksummingMd5() {
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType( OCC::checkSumMD5C );
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
||||
ComputeChecksum *vali = new ComputeChecksum(this);
|
||||
_expectedType = OCC::checkSumMD5C;
|
||||
vali->setChecksumType(_expectedType);
|
||||
connect(vali, SIGNAL(done(QByteArray,QByteArray)), this, SLOT(slotUpValidated(QByteArray,QByteArray)));
|
||||
|
||||
_expected = checkSumMD5C;
|
||||
_expected.append(":"+FileSystem::calcMd5( _testfile ));
|
||||
vali->uploadValidation();
|
||||
_expected = FileSystem::calcMd5( _testfile );
|
||||
vali->start(_testfile);
|
||||
|
||||
QEventLoop loop;
|
||||
connect(vali, SIGNAL(validated(QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
|
||||
connect(vali, SIGNAL(done(QByteArray,QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
|
||||
loop.exec();
|
||||
|
||||
delete vali;
|
||||
|
@ -103,17 +104,17 @@ using namespace OCC;
|
|||
|
||||
void testUploadChecksummingSha1() {
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType( OCC::checkSumSHA1C );
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
||||
ComputeChecksum *vali = new ComputeChecksum(this);
|
||||
_expectedType = OCC::checkSumSHA1C;
|
||||
vali->setChecksumType(_expectedType);
|
||||
connect(vali, SIGNAL(done(QByteArray,QByteArray)), this, SLOT(slotUpValidated(QByteArray,QByteArray)));
|
||||
|
||||
_expected = checkSumSHA1C;
|
||||
_expected.append(":"+FileSystem::calcSha1( _testfile ));
|
||||
_expected = FileSystem::calcSha1( _testfile );
|
||||
|
||||
vali->uploadValidation();
|
||||
vali->start(_testfile);
|
||||
|
||||
QEventLoop loop;
|
||||
connect(vali, SIGNAL(validated(QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
|
||||
connect(vali, SIGNAL(done(QByteArray,QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
|
||||
loop.exec();
|
||||
|
||||
delete vali;
|
||||
|
@ -126,22 +127,21 @@ using namespace OCC;
|
|||
adler.append(FileSystem::calcAdler32( _testfile ));
|
||||
_successDown = false;
|
||||
|
||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
||||
vali->setChecksumType("Adler32");
|
||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotDownValidated()));
|
||||
ValidateChecksumHeader *vali = new ValidateChecksumHeader(this);
|
||||
connect(vali, SIGNAL(validated(QByteArray,QByteArray)), this, SLOT(slotDownValidated()));
|
||||
connect(vali, SIGNAL(validationFailed(QString)), this, SLOT(slotDownError(QString)));
|
||||
vali->downloadValidation(adler);
|
||||
vali->start(_testfile, adler);
|
||||
|
||||
QTRY_VERIFY(_successDown);
|
||||
|
||||
_expectedError = QLatin1String("The downloaded file does not match the checksum, it will be resumed.");
|
||||
_errorSeen = false;
|
||||
vali->downloadValidation("Adler32:543345");
|
||||
vali->start(_testfile, "Adler32:543345");
|
||||
QTRY_VERIFY(_errorSeen);
|
||||
|
||||
_expectedError = QLatin1String("The checksum header is malformed.");
|
||||
_expectedError = QLatin1String("The checksum header contained an unknown checksum type 'Klaas32'");
|
||||
_errorSeen = false;
|
||||
vali->downloadValidation("Klaas32:543345");
|
||||
vali->start(_testfile, "Klaas32:543345");
|
||||
QTRY_VERIFY(_errorSeen);
|
||||
|
||||
delete vali;
|
||||
|
|
Загрузка…
Ссылка в новой задаче