2014-11-19 17:18:01 +00:00
/**************************************************************************
* Copyright ( C ) 2009 - 2011 Matthias Fuchs < mat69 @ gmx . net > *
* *
* 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 . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the *
* Free Software Foundation , Inc . , *
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "verifier_p.h"
# include "verificationmodel.h"
# include "../dbus/dbusverifierwrapper.h"
# include "verifieradaptor.h"
# include "settings.h"
# include <QtCore/QFile>
# include <QtCore/QScopedPointer>
2016-04-01 06:06:57 +00:00
# include <QtCore/QCryptographicHash>
2015-11-25 10:40:41 +00:00
# include <QtXml/qdom.h>
2014-11-19 17:18:01 +00:00
# include <KDebug>
//TODO use mutable to make some methods const?
2016-04-01 06:06:57 +00:00
// TODO: implement md4 support
const QStringList VerifierPrivate : : SUPPORTED = ( QStringList ( ) < < " sha1 " < < " md5 " < < " md4 " ) ;
2014-11-19 17:18:01 +00:00
const QString VerifierPrivate : : MD5 = QString ( " md5 " ) ;
const int VerifierPrivate : : MD5LENGTH = 32 ;
2016-04-01 06:06:57 +00:00
const QString VerifierPrivate : : SHA1 = QString ( " sha1 " ) ;
const int VerifierPrivate : : SHA1LENGTH = 40 ;
const int VerifierPrivate : : DIGGESTLENGTH [ ] = { 40 , 32 , 32 } ;
2014-11-19 17:18:01 +00:00
const int VerifierPrivate : : PARTSIZE = 500 * 1024 ;
VerifierPrivate : : ~ VerifierPrivate ( )
{
delete model ;
qDeleteAll ( partialSums . begin ( ) , partialSums . end ( ) ) ;
}
2016-04-01 06:06:57 +00:00
QString VerifierPrivate : : calculatePartialChecksum ( QFile * file , const QString & type , KIO : : fileoffset_t startOffset , int pieceLength , KIO : : filesize_t fileSize )
2014-11-19 17:18:01 +00:00
{
if ( ! file )
{
return QString ( ) ;
}
if ( ! fileSize )
{
fileSize = file - > size ( ) ;
}
//longer than the file, so adapt it
if ( static_cast < KIO : : fileoffset_t > ( fileSize ) < startOffset + pieceLength )
{
pieceLength = fileSize - startOffset ;
}
2016-04-01 06:06:57 +00:00
if ( type ! = MD5 & & type ! = SHA1 ) {
2014-11-19 17:18:01 +00:00
return QString ( ) ;
}
//we only read 512kb each time, to save RAM
int numData = pieceLength / PARTSIZE ;
KIO : : fileoffset_t dataRest = pieceLength % PARTSIZE ;
if ( ! numData & & ! dataRest )
{
2016-04-01 06:06:57 +00:00
return QString ( ) ;
2014-11-19 17:18:01 +00:00
}
2016-04-01 06:06:57 +00:00
QCryptographicHash * hash = 0 ;
if ( type = = MD5 ) {
hash = new QCryptographicHash ( QCryptographicHash : : Md5 ) ;
} else if ( type = = SHA1 ) {
hash = new QCryptographicHash ( QCryptographicHash : : Sha1 ) ;
}
2014-11-19 17:18:01 +00:00
int k = 0 ;
for ( k = 0 ; k < numData ; + + k )
{
if ( ! file - > seek ( startOffset + PARTSIZE * k ) )
{
return QString ( ) ;
}
QByteArray data = file - > read ( PARTSIZE ) ;
2016-04-01 06:06:57 +00:00
hash - > addData ( data ) ;
2014-11-19 17:18:01 +00:00
}
//now read the rest
if ( dataRest )
{
if ( ! file - > seek ( startOffset + PARTSIZE * k ) )
{
return QString ( ) ;
}
QByteArray data = file - > read ( dataRest ) ;
2016-04-01 06:06:57 +00:00
hash - > addData ( data ) ;
2014-11-19 17:18:01 +00:00
}
2016-04-01 06:06:57 +00:00
return QString ( hash - > result ( ) . toHex ( ) ) ;
2014-11-19 17:18:01 +00:00
}
QStringList VerifierPrivate : : orderChecksumTypes ( Verifier : : ChecksumStrength strength ) const
{
QStringList checksumTypes ;
if ( strength = = Verifier : : Weak ) {
for ( int i = SUPPORTED . count ( ) - 1 ; i > = 0 ; - - i ) {
checksumTypes . append ( SUPPORTED . at ( i ) ) ;
}
checksumTypes . move ( 0 , 1 ) ; //md4 second position
} else if ( strength = = Verifier : : Strong ) {
for ( int i = SUPPORTED . count ( ) - 1 ; i > = 0 ; - - i ) {
checksumTypes . append ( SUPPORTED . at ( i ) ) ;
}
checksumTypes . move ( 1 , checksumTypes . count ( ) - 1 ) ; //md5 second last position
checksumTypes . move ( 0 , checksumTypes . count ( ) - 1 ) ; //md4 last position
} else if ( strength = = Verifier : : Strongest ) {
checksumTypes = SUPPORTED ;
}
return checksumTypes ;
}
Verifier : : Verifier ( const KUrl & dest , QObject * parent )
: QObject ( parent ) ,
d ( new VerifierPrivate ( this ) )
{
d - > dest = dest ;
d - > status = NoResult ;
static int dBusObjIdx = 0 ;
d - > dBusObjectPath = " /KGet/Verifiers/ " + QString : : number ( dBusObjIdx + + ) ;
DBusVerifierWrapper * wrapper = new DBusVerifierWrapper ( this ) ;
new VerifierAdaptor ( wrapper ) ;
QDBusConnection : : sessionBus ( ) . registerObject ( d - > dBusObjectPath , wrapper ) ;
qRegisterMetaType < KIO : : filesize_t > ( " KIO::filesize_t " ) ;
qRegisterMetaType < KIO : : fileoffset_t > ( " KIO::fileoffset_t " ) ;
qRegisterMetaType < QList < KIO : : fileoffset_t > > ( " QList<KIO::fileoffset_t> " ) ;
d - > model = new VerificationModel ( ) ;
connect ( & d - > thread , SIGNAL ( verified ( QString , bool , KUrl ) ) , this , SLOT ( changeStatus ( QString , bool ) ) ) ;
connect ( & d - > thread , SIGNAL ( brokenPieces ( QList < KIO : : fileoffset_t > , KIO : : filesize_t ) ) , this , SIGNAL ( brokenPieces ( QList < KIO : : fileoffset_t > , KIO : : filesize_t ) ) ) ;
}
Verifier : : ~ Verifier ( )
{
delete d ;
}
QString Verifier : : dBusObjectPath ( ) const
{
return d - > dBusObjectPath ;
}
KUrl Verifier : : destination ( ) const
{
return d - > dest ;
}
void Verifier : : setDestination ( const KUrl & destination )
{
d - > dest = destination ;
}
Verifier : : VerificationStatus Verifier : : status ( ) const
{
return d - > status ;
}
VerificationModel * Verifier : : model ( )
{
return d - > model ;
}
QStringList Verifier : : supportedVerficationTypes ( )
{
QStringList supported ;
if ( ! supported . contains ( VerifierPrivate : : MD5 ) )
{
2016-04-01 06:06:57 +00:00
supported < < VerifierPrivate : : MD5 < < VerifierPrivate : : SHA1 ;
2014-11-19 17:18:01 +00:00
}
return supported ;
}
int Verifier : : diggestLength ( const QString & type )
{
2016-04-01 06:06:57 +00:00
if ( type = = VerifierPrivate : : MD5 ) {
2014-11-19 17:18:01 +00:00
return VerifierPrivate : : MD5LENGTH ;
2016-04-01 06:06:57 +00:00
} else if ( type = = VerifierPrivate : : SHA1 ) {
return VerifierPrivate : : SHA1LENGTH ;
2014-11-19 17:18:01 +00:00
}
return 0 ;
}
bool Verifier : : isChecksum ( const QString & type , const QString & checksum )
{
const int length = diggestLength ( type ) ;
const QString pattern = QString ( " [0-9a-z]{%1} " ) . arg ( length ) ;
//needs correct length and only word characters
if ( length & & ( checksum . length ( ) = = length ) & & checksum . toLower ( ) . contains ( QRegExp ( pattern ) ) )
{
return true ;
}
return false ;
}
QString Verifier : : cleanChecksumType ( const QString & type )
{
QString hashType = type . toUpper ( ) ;
if ( hashType . contains ( QRegExp ( " ^SHA \\ d+ " ) ) ) {
hashType . insert ( 3 , ' - ' ) ;
}
return hashType ;
}
bool Verifier : : isVerifyable ( ) const
{
return QFile : : exists ( d - > dest . pathOrUrl ( ) ) & & d - > model - > rowCount ( ) ;
}
bool Verifier : : isVerifyable ( const QModelIndex & index ) const
{
int row = - 1 ;
if ( index . isValid ( ) )
{
row = index . row ( ) ;
}
if ( QFile : : exists ( d - > dest . pathOrUrl ( ) ) & & ( row > = 0 ) & & ( row < d - > model - > rowCount ( ) ) )
{
return true ;
}
return false ;
}
Checksum Verifier : : availableChecksum ( Verifier : : ChecksumStrength strength ) const
{
Checksum pair ;
//check if there is at least one entry
QModelIndex index = d - > model - > index ( 0 , 0 ) ;
if ( ! index . isValid ( ) )
{
return pair ;
}
const QStringList available = supportedVerficationTypes ( ) ;
const QStringList supported = d - > orderChecksumTypes ( strength ) ;
for ( int i = 0 ; i < supported . count ( ) ; + + i ) {
QModelIndexList indexList = d - > model - > match ( index , Qt : : DisplayRole , supported . at ( i ) ) ;
if ( ! indexList . isEmpty ( ) & & available . contains ( supported . at ( i ) ) ) {
QModelIndex match = d - > model - > index ( indexList . first ( ) . row ( ) , VerificationModel : : Checksum ) ;
pair . first = supported . at ( i ) ;
pair . second = match . data ( ) . toString ( ) ;
break ;
}
}
return pair ;
}
QList < Checksum > Verifier : : availableChecksums ( ) const
{
QList < Checksum > checksums ;
for ( int i = 0 ; i < d - > model - > rowCount ( ) ; + + i ) {
const QString type = d - > model - > index ( i , VerificationModel : : Type ) . data ( ) . toString ( ) ;
const QString hash = d - > model - > index ( i , VerificationModel : : Checksum ) . data ( ) . toString ( ) ;
checksums < < qMakePair ( type , hash ) ;
}
return checksums ;
}
QPair < QString , PartialChecksums * > Verifier : : availablePartialChecksum ( Verifier : : ChecksumStrength strength ) const
{
QPair < QString , PartialChecksums * > pair ;
QString type ;
PartialChecksums * checksum = 0 ;
const QStringList available = supportedVerficationTypes ( ) ;
const QStringList supported = d - > orderChecksumTypes ( strength ) ;
for ( int i = 0 ; i < supported . size ( ) ; + + i ) {
if ( d - > partialSums . contains ( supported . at ( i ) ) & & available . contains ( supported . at ( i ) ) ) {
type = supported . at ( i ) ;
checksum = d - > partialSums [ type ] ;
break ;
}
}
return QPair < QString , PartialChecksums * > ( type , checksum ) ;
}
void Verifier : : changeStatus ( const QString & type , bool isVerified )
{
kDebug ( 5001 ) < < " Verified: " < < isVerified ;
d - > status = isVerified ? Verifier : : Verified : Verifier : : NotVerified ;
d - > model - > setVerificationStatus ( type , d - > status ) ;
emit verified ( isVerified ) ;
}
void Verifier : : verify ( const QModelIndex & index )
{
int row = - 1 ;
if ( index . isValid ( ) ) {
row = index . row ( ) ;
}
QString type ;
QString checksum ;
if ( row = = - 1 ) {
Checksum pair = availableChecksum ( static_cast < Verifier : : ChecksumStrength > ( Settings : : checksumStrength ( ) ) ) ;
type = pair . first ;
checksum = pair . second ;
} else if ( ( row > = 0 ) & & ( row < d - > model - > rowCount ( ) ) ) {
type = d - > model - > index ( row , VerificationModel : : Type ) . data ( ) . toString ( ) ;
checksum = d - > model - > index ( row , VerificationModel : : Checksum ) . data ( ) . toString ( ) ;
}
d - > thread . verifiy ( type , checksum , d - > dest ) ;
}
void Verifier : : brokenPieces ( ) const
{
QPair < QString , PartialChecksums * > pair = availablePartialChecksum ( static_cast < Verifier : : ChecksumStrength > ( Settings : : checksumStrength ( ) ) ) ;
QList < QString > checksums ;
KIO : : filesize_t length = 0 ;
if ( pair . second ) {
checksums = pair . second - > checksums ( ) ;
length = pair . second - > length ( ) ;
}
d - > thread . findBrokenPieces ( pair . first , checksums , length , d - > dest ) ;
}
2016-04-01 06:06:57 +00:00
QString Verifier : : checksum ( const KUrl & dest , const QString & type )
2014-11-19 17:18:01 +00:00
{
QStringList supported = supportedVerficationTypes ( ) ;
if ( ! supported . contains ( type ) )
{
return QString ( ) ;
}
QFile file ( dest . pathOrUrl ( ) ) ;
if ( ! file . open ( QIODevice : : ReadOnly ) )
{
return QString ( ) ;
}
if ( type = = VerifierPrivate : : MD5 ) {
2016-04-01 06:06:57 +00:00
QByteArray hash = QCryptographicHash : : hash ( file . readAll ( ) , QCryptographicHash : : Md5 ) ;
2014-11-19 17:18:01 +00:00
file . close ( ) ;
2016-04-01 06:06:57 +00:00
return hash . toHex ( ) ;
} else if ( type = = VerifierPrivate : : SHA1 ) {
QByteArray hash = QCryptographicHash : : hash ( file . readAll ( ) , QCryptographicHash : : Sha1 ) ;
file . close ( ) ;
return hash . toHex ( ) ;
2014-11-19 17:18:01 +00:00
}
return QString ( ) ;
}
2016-04-01 06:06:57 +00:00
PartialChecksums Verifier : : partialChecksums ( const KUrl & dest , const QString & type , KIO : : filesize_t length )
2014-11-19 17:18:01 +00:00
{
QStringList checksums ;
QStringList supported = supportedVerficationTypes ( ) ;
if ( ! supported . contains ( type ) )
{
return PartialChecksums ( ) ;
}
QFile file ( dest . pathOrUrl ( ) ) ;
if ( ! file . open ( QIODevice : : ReadOnly ) )
{
return PartialChecksums ( ) ;
}
const KIO : : filesize_t fileSize = file . size ( ) ;
if ( ! fileSize )
{
return PartialChecksums ( ) ;
}
int numPieces = 0 ;
//the piece length has been defined
if ( length )
{
numPieces = fileSize / length ;
}
else
{
length = VerifierPrivate : : PARTSIZE ;
numPieces = fileSize / length ;
if ( numPieces > 100 )
{
numPieces = 100 ;
length = fileSize / numPieces ;
}
}
//there is a rest, so increase numPieces by one
if ( fileSize % length )
{
+ + numPieces ;
}
PartialChecksums partialChecksums ;
//create all the checksums for the pieces
for ( int i = 0 ; i < numPieces ; + + i )
{
2016-04-01 06:06:57 +00:00
QString hash = VerifierPrivate : : calculatePartialChecksum ( & file , type , length * i , length , fileSize ) ;
2014-11-19 17:18:01 +00:00
if ( hash . isEmpty ( ) )
{
file . close ( ) ;
return PartialChecksums ( ) ;
}
checksums . append ( hash ) ;
}
partialChecksums . setLength ( length ) ;
partialChecksums . setChecksums ( checksums ) ;
file . close ( ) ;
return partialChecksums ;
}
void Verifier : : addChecksum ( const QString & type , const QString & checksum , int verified )
{
d - > model - > addChecksum ( type , checksum , verified ) ;
}
void Verifier : : addChecksums ( const QHash < QString , QString > & checksums )
{
d - > model - > addChecksums ( checksums ) ;
}
void Verifier : : addPartialChecksums ( const QString & type , KIO : : filesize_t length , const QStringList & checksums )
{
if ( ! d - > partialSums . contains ( type ) & & length & & ! checksums . isEmpty ( ) )
{
d - > partialSums [ type ] = new PartialChecksums ( length , checksums ) ;
}
}
KIO : : filesize_t Verifier : : partialChunkLength ( ) const
{
QStringList : : const_iterator it ;
QStringList : : const_iterator itEnd = VerifierPrivate : : SUPPORTED . constEnd ( ) ;
for ( it = VerifierPrivate : : SUPPORTED . constBegin ( ) ; it ! = itEnd ; + + it )
{
if ( d - > partialSums . contains ( * it ) )
{
return d - > partialSums [ * it ] - > length ( ) ;
}
}
return 0 ;
}
void Verifier : : save ( const QDomElement & element )
{
QDomElement e = element ;
e . setAttribute ( " verificationStatus " , d - > status ) ;
QDomElement verification = e . ownerDocument ( ) . createElement ( " verification " ) ;
for ( int i = 0 ; i < d - > model - > rowCount ( ) ; + + i )
{
QDomElement hash = e . ownerDocument ( ) . createElement ( " hash " ) ;
hash . setAttribute ( " type " , d - > model - > index ( i , VerificationModel : : Type ) . data ( ) . toString ( ) ) ;
hash . setAttribute ( " verified " , d - > model - > index ( i , VerificationModel : : Verified ) . data ( Qt : : EditRole ) . toInt ( ) ) ;
QDomText value = e . ownerDocument ( ) . createTextNode ( d - > model - > index ( i , VerificationModel : : Checksum ) . data ( ) . toString ( ) ) ;
hash . appendChild ( value ) ;
verification . appendChild ( hash ) ;
}
QHash < QString , PartialChecksums * > : : const_iterator it ;
QHash < QString , PartialChecksums * > : : const_iterator itEnd = d - > partialSums . constEnd ( ) ;
for ( it = d - > partialSums . constBegin ( ) ; it ! = itEnd ; + + it )
{
QDomElement pieces = e . ownerDocument ( ) . createElement ( " pieces " ) ;
pieces . setAttribute ( " type " , it . key ( ) ) ;
pieces . setAttribute ( " length " , ( * it ) - > length ( ) ) ;
QList < QString > checksums = ( * it ) - > checksums ( ) ;
for ( int i = 0 ; i < checksums . size ( ) ; + + i )
{
QDomElement hash = e . ownerDocument ( ) . createElement ( " hash " ) ;
hash . setAttribute ( " piece " , i ) ;
QDomText value = e . ownerDocument ( ) . createTextNode ( checksums [ i ] ) ;
hash . appendChild ( value ) ;
pieces . appendChild ( hash ) ;
}
verification . appendChild ( pieces ) ;
}
e . appendChild ( verification ) ;
}
void Verifier : : load ( const QDomElement & e )
{
if ( e . hasAttribute ( " verificationStatus " ) )
{
const int status = e . attribute ( " verificationStatus " ) . toInt ( ) ;
switch ( status )
{
case NoResult :
d - > status = NoResult ;
break ;
case NotVerified :
d - > status = NotVerified ;
break ;
case Verified :
d - > status = Verified ;
break ;
default :
d - > status = NotVerified ;
break ;
}
}
QDomElement verification = e . firstChildElement ( " verification " ) ;
QDomNodeList const hashList = verification . elementsByTagName ( " hash " ) ;
for ( uint i = 0 ; i < hashList . length ( ) ; + + i )
{
const QDomElement hash = hashList . item ( i ) . toElement ( ) ;
const QString value = hash . text ( ) ;
const QString type = hash . attribute ( " type " ) ;
const int verificationStatus = hash . attribute ( " verified " ) . toInt ( ) ;
if ( ! type . isEmpty ( ) & & ! value . isEmpty ( ) )
{
d - > model - > addChecksum ( type , value , verificationStatus ) ;
}
}
QDomNodeList const piecesList = verification . elementsByTagName ( " pieces " ) ;
for ( uint i = 0 ; i < piecesList . length ( ) ; + + i )
{
QDomElement pieces = piecesList . at ( i ) . toElement ( ) ;
const QString type = pieces . attribute ( " type " ) ;
const KIO : : filesize_t length = pieces . attribute ( " length " ) . toULongLong ( ) ;
QStringList partialChecksums ;
const QDomNodeList partialHashList = pieces . elementsByTagName ( " hash " ) ;
for ( int i = 0 ; i < partialHashList . size ( ) ; + + i ) //TODO give this function the size of the file, to calculate how many hashs are needed as an additional check, do that check in addPartialChecksums?!
{
const QString hash = partialHashList . at ( i ) . toElement ( ) . text ( ) ;
if ( hash . isEmpty ( ) )
{
break ;
}
partialChecksums . append ( hash ) ;
}
addPartialChecksums ( type , length , partialChecksums ) ;
}
}
2015-02-27 11:02:43 +00:00
# include "moc_verifier.cpp"