33 #include <config-kleopatra.h>
37 #ifndef QT_NO_DIRMODEL
46 #include <kleo/stl_util.h>
47 #include <kleo/checksumdefinition.h>
53 #include <KConfigGroup>
59 #include <QProgressDialog>
63 #include <boost/bind.hpp>
64 #include <boost/function.hpp>
66 #include <gpg-error.h>
73 using namespace Kleo::Crypto;
74 using namespace Kleo::Crypto::Gui;
75 using namespace boost;
85 static const Qt::CaseSensitivity
fs_cs =
HAVE_UNIX ? Qt::CaseSensitive : Qt::CaseInsensitive ;
88 static QStringList
fs_sort( QStringList l ) {
89 int (*QString_compare)(
const QString&,
const QString&,Qt::CaseSensitivity) = &QString::compare;
90 kdtools::sort( l, boost::bind( QString_compare, _1, _2,
fs_cs ) < 0 );
94 static QStringList
fs_intersect( QStringList l1, QStringList l2 ) {
95 int (*QString_compare)(
const QString&,
const QString&,Qt::CaseSensitivity) = &QString::compare;
99 std::set_intersection( l1.begin(), l1.end(),
100 l2.begin(), l2.end(),
101 std::back_inserter( result ),
102 boost::bind( QString_compare, _1, _2,
fs_cs ) < 0 );
112 Q_FOREACH(
const QString & pattern, cd->patterns() )
113 result.push_back( QRegExp( pattern,
fs_cs ) );
118 struct matches_any : std::unary_function<QString,bool> {
120 explicit matches_any(
const QList<QRegExp> & regexps ) : m_regexps( regexps ) {}
121 bool operator()(
const QString & s )
const {
122 return kdtools::any( m_regexps, boost::bind( &QRegExp::exactMatch, _1, s ) );
125 struct matches_none_of : std::unary_function<QString,bool> {
127 explicit matches_none_of(
const QList<QRegExp> & regexps ) : m_regexps( regexps ) {}
128 bool operator()(
const QString & s )
const {
129 return kdtools::none_of( m_regexps, boost::bind( &QRegExp::exactMatch, _1, s ) );
134 class VerifyChecksumsController::Private :
public QThread {
136 friend class ::Kleo::Crypto::VerifyChecksumsController;
143 void baseDirectories(
const QStringList & );
144 void progress(
int,
int,
const QString & );
148 void slotOperationFinished() {
150 dialog->setProgress( 100, 100 );
151 dialog->setErrors( errors );
154 if ( !errors.empty() )
155 q->setLastError( gpg_error( GPG_ERR_GENERAL ),
156 errors.join( QLatin1String(
"\n") ) );
157 q->emitDoneOrError();
164 QPointer<VerifyChecksumsDialog> dialog;
165 mutable QMutex
mutex;
166 const std::vector< shared_ptr<ChecksumDefinition> > checksumDefinitions;
169 volatile bool canceled;
176 checksumDefinitions( ChecksumDefinition::getChecksumDefinitions() ),
181 connect(
this, SIGNAL(progress(
int,
int,QString)),
182 q, SIGNAL(progress(
int,
int,QString)) );
183 connect(
this, SIGNAL(finished()),
184 q, SLOT(slotOperationFinished()) );
187 VerifyChecksumsController::Private::~Private() { kDebug(); }
189 VerifyChecksumsController::VerifyChecksumsController( QObject * p )
208 const QMutexLocker locker( &d->mutex );
216 const QMutexLocker locker( &d->mutex );
219 d->dialog->setAttribute( Qt::WA_DeleteOnClose );
220 d->dialog->setWindowTitle( i18nc(
"@title:window",
"Verify Checksum Results") );
222 connect( d->dialog, SIGNAL(canceled()),
224 connect( d.
get(), SIGNAL(baseDirectories(QStringList)),
225 d->dialog, SLOT(setBaseDirectories(QStringList)) );
226 connect( d.
get(), SIGNAL(
progress(
int,
int,QString)),
227 d->dialog, SLOT(setProgress(
int,
int)) );
243 const QMutexLocker locker( &d->mutex );
259 l.erase( std::remove_if( l.begin(), l.end(),
260 matches_none_of( rxs ) ),
274 static QString
decode(
const QString & encoded ) {
276 decoded.reserve( encoded.size() );
278 Q_FOREACH(
const QChar ch, encoded )
280 switch ( ch.toLatin1() ) {
281 case '\\': decoded += QLatin1Char(
'\\' );
break;
282 case 'n': decoded += QLatin1Char(
'\n' );
break;
284 kDebug() <<
"invalid escape sequence" <<
'\\' << ch <<
"(interpreted as '" << ch <<
"')";
290 if ( ch == QLatin1Char(
'\\' ) )
299 std::vector<File> files;
301 if ( f.open( QIODevice::ReadOnly ) ) {
303 QRegExp rx( QLatin1String(
"(\\?)([a-f0-9A-F]+) ([ *])([^\n]+)\n*") );
304 while ( !s.atEnd() ) {
305 const QString line = s.readLine();
306 if ( rx.exactMatch( line ) ) {
307 assert( !rx.cap(4).endsWith( QLatin1Char(
'\n') ) );
309 rx.cap( 1 ) == QLatin1String(
"\\") ?
decode( rx.cap( 4 ) ) : rx.cap( 4 ),
310 rx.cap( 2 ).toLatin1(),
311 rx.cap( 3 ) == QLatin1String(
"*"),
313 files.push_back( file );
322 Q_FOREACH(
const QString & file, files )
323 n += QFileInfo( dir.absoluteFilePath( file ) ).size();
332 Q_FOREACH(
const QString & pattern, cd->patterns() )
333 if ( QRegExp( pattern,
fs_cs ).exactMatch( fileName ) )
339 struct less_dir : std::binary_function<QDir,QDir,bool> {
340 bool operator()(
const QDir & lhs,
const QDir & rhs )
const {
341 return QString::compare( lhs.absolutePath(), rhs.absolutePath(),
fs_cs ) < 0 ;
344 struct less_file : std::binary_function<QString,QString,bool> {
345 bool operator()(
const QString & lhs,
const QString & rhs )
const {
346 return QString::compare( lhs, rhs,
fs_cs ) < 0 ;
349 struct sumfile_contains_file : std::unary_function<QString,bool> {
351 const QString fileName;
352 sumfile_contains_file(
const QDir & dir_,
const QString & fileName_ )
353 :
dir( dir_ ), fileName( fileName_ ) {}
354 bool operator()(
const QString & sumFile )
const {
355 const std::vector<File> files =
parse_sum_file(
dir.absoluteFilePath( sumFile ) );
356 kDebug() <<
"find_sums_by_input_files: found " << files.size()
357 <<
" files listed in " << qPrintable(
dir.absoluteFilePath( sumFile ) );
358 Q_FOREACH(
const File & file, files ) {
359 const bool isSameFileName = ( QString::compare( file.name, fileName,
fs_cs ) == 0 );
360 kDebug() <<
"find_sums_by_input_files: "
361 << qPrintable( file.name ) <<
" == "
362 << qPrintable( fileName ) <<
" ? "
364 if ( isSameFileName )
387 std::set<QDir,less_dir> dirs;
388 Q_FOREACH(
const QString & file, files ) {
389 const QFileInfo fi( file );
390 const QDir
dir = fi.isDir() ? QDir( file ) : fi.dir() ;
399 std::set<QDir,less_dir>::iterator it = dirs.begin();
400 while ( it != dirs.end() ) {
402 if ( dir.cdUp() && dirs.count( dir ) ) {
411 return kdtools::transform<QStringList>( dirs, mem_fn( &QDir::absolutePath ) );
415 const function<
void(
int)> & progress,
420 const matches_any is_sum_file( patterns );
422 std::map<QDir,std::set<QString,less_file>,less_dir> dirs2sums;
426 std::deque<QString> inputs( files.begin(), files.end() );
429 while ( !inputs.empty() ) {
430 const QString file = inputs.front();
431 kDebug() <<
"find_sums_by_input_files: considering " << qPrintable( file );
433 const QFileInfo fi( file );
434 const QString fileName = fi.fileName();
436 kDebug() <<
"find_sums_by_input_files: it's a directory";
439 kDebug() <<
"find_sums_by_input_files: found " << sumfiles.size()
440 <<
" sum files: " << qPrintable( sumfiles.join(QLatin1String(
", ")) );
441 dirs2sums[
dir ].insert( sumfiles.begin(), sumfiles.end() );
442 const QStringList dirs = dir.entryList( QDir::Dirs|QDir::NoDotAndDotDot );
443 kDebug() <<
"find_sums_by_input_files: found " << dirs.size()
444 <<
" subdirs, prepending";
445 kdtools::transform( dirs,
446 std::inserter( inputs, inputs.begin() ),
447 boost::bind( &QDir::absoluteFilePath, cref(dir), _1 ) );
448 }
else if ( is_sum_file( fileName ) ) {
449 kDebug() <<
"find_sums_by_input_files: it's a sum file";
450 dirs2sums[fi.dir()].insert( fileName );
452 kDebug() <<
"find_sums_by_input_files: it's something else; checking whether we'll find a sumfile for it...";
453 const QDir
dir = fi.dir();
455 kDebug() <<
"find_sums_by_input_files: found " << sumfiles.size()
456 <<
" potential sumfiles: " << qPrintable( sumfiles.join(QLatin1String(
", ")) );
457 const QStringList::const_iterator it = kdtools::find_if( sumfiles, sumfile_contains_file( dir, fileName ) );
458 if ( it == sumfiles.end() )
459 errors.push_back( i18n(
"Cannot find checksums file for file %1", file ) );
461 dirs2sums[
dir].insert( *it );
463 if ( !progress.empty() )
469 std::vector<SumFile> sumfiles;
470 sumfiles.reserve( dirs2sums.size() );
472 for ( std::map<QDir,std::set<QString,less_file>,less_dir>::const_iterator it = dirs2sums.begin(), end = dirs2sums.end() ; it != end ; ++it ) {
474 if ( it->second.empty() )
477 const QDir &
dir = it->first;
479 Q_FOREACH(
const QString & sumFileName, it->second ) {
481 const std::vector<File> summedfiles =
parse_sum_file( dir.absoluteFilePath( sumFileName ) );
483 const SumFile sumFile = {
489 sumfiles.push_back( sumFile );
493 if ( !progress.empty() )
501 QStringList env = QProcess::systemEnvironment();
502 env.erase( std::remove_if( env.begin(), env.end(),
503 boost::bind( &QRegExp::exactMatch,
504 QRegExp( QLatin1String(
"^LANG=.*"),
fs_cs ), _1 ) ),
506 env.push_back( QLatin1String(
"LANG=C") );
510 static const struct {
514 {
"OK", VerifyChecksumsDialog::OK },
526 static QString
process(
const SumFile & sumFile,
bool * fatal,
const QStringList & env,
530 p.setEnvironment( env );
531 p.setWorkingDirectory( sumFile.dir.absolutePath() );
532 p.setReadChannel( QProcess::StandardOutput );
534 const QString absFilePath = sumFile.dir.absoluteFilePath( sumFile.sumFile );
536 const QString program = sumFile.checksumDefinition->verifyCommand();
537 sumFile.checksumDefinition->startVerifyCommand( &p, QStringList( absFilePath ) );
539 QByteArray remainder;
540 while ( p.state() != QProcess::NotRunning ) {
541 p.waitForReadyRead();
542 while ( p.canReadLine() ) {
543 const QByteArray line = p.readLine();
544 const int colonIdx = line.lastIndexOf(
':' );
545 if ( colonIdx < 0 ) {
549 const QString file = QFile::decodeName( remainder + line.left( colonIdx ) );
552 status( sumFile.dir.absoluteFilePath( file ), result );
555 kDebug() <<
"[" << &p <<
"] Exit code " << p.exitCode();
557 if ( p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0 ) {
558 if ( fatal && p.error() == QProcess::FailedToStart )
560 if ( p.error() == QProcess::UnknownError )
561 return i18n(
"Error while running %1: %2", program,
562 QString::fromLocal8Bit( p.readAllStandardError().trimmed().constData() ) );
564 return i18n(
"Failed to execute %1: %2", program, p.errorString() );
572 return s <<
"SumFile(" << sum.dir <<
"->" << sum.sumFile <<
"<-(" << sum.totalSize <<
')' <<
")\n";
576 void VerifyChecksumsController::Private::run() {
578 QMutexLocker locker( &
mutex );
580 const QStringList files = this->files;
581 const std::vector< shared_ptr<ChecksumDefinition> > checksumDefinitions = this->checksumDefinitions;
597 const QString scanning = i18n(
"Scanning directories...");
598 emit progress( 0, 0, scanning );
600 const function<void(int)> progressCb = boost::bind( &Private::progress,
this, _1, 0, scanning );
601 const function<void(const QString&,VerifyChecksumsDialog::Status)>
605 Q_FOREACH(
const SumFile & sumfile, sumfiles )
610 emit progress( 0, 0, i18n(
"Calculating total size...") );
613 = kdtools::accumulate_transform( sumfiles, mem_fn( &SumFile::totalSize ), Q_UINT64_C(0) );
624 const quint64 factor = total / std::numeric_limits<int>::max() + 1 ;
627 Q_FOREACH(
const SumFile & sumFile, sumfiles ) {
628 emit progress( done/factor, total/factor,
629 i18n(
"Verifying checksums (%2) in %1", sumFile.checksumDefinition->label(), sumFile.dir.path() ) );
631 const QString error =
process( sumFile, &fatal, env, statusCb );
632 if ( !error.isEmpty() )
633 errors.push_back( error );
634 done += sumFile.totalSize;
635 if ( fatal || canceled )
638 emit progress( done/factor, total/factor, i18n(
"Done.") );
645 this->errors = errors;
651 #include "moc_verifychecksumscontroller.cpp"
652 #include "verifychecksumscontroller.moc"
654 #endif // QT_NO_DIRMODEL
static const struct @2 statusStrings[]
static QStringList fs_sort(QStringList l)
int sum(const std::vector< ImportResult > &res, int(ImportResult::*fun)() const )
static QStringList c_lang_environment()
static QList< QRegExp > get_patterns(const std::vector< shared_ptr< ChecksumDefinition > > &checksumDefinitions)
static QStringList fs_intersect(QStringList l1, QStringList l2)
static const Qt::CaseSensitivity fs_cs
static const size_t numStatusStrings
static shared_ptr< ChecksumDefinition > filename2definition(const QString &fileName, const std::vector< shared_ptr< ChecksumDefinition > > &checksumDefinitions)
static QString process(const SumFile &sumFile, bool *fatal, const QStringList &env, const function< void(const QString &, VerifyChecksumsDialog::Status)> &status)
VerifyChecksumsDialog::Status status
static const QLatin1String CHECKSUM_DEFINITION_ID_ENTRY("checksum-definition-id")
static QString decode(const QString &encoded)
~VerifyChecksumsController()
static const bool HAVE_UNIX
static quint64 aggregate_size(const QDir &dir, const QStringList &files)
void progress(int current, int total, const QString &what)
#define kleo_assert(cond)
static QString dir(const QString &id)
static QStringList filter_checksum_files(QStringList l, const QList< QRegExp > &rxs)
VerifyChecksumsController(QObject *parent=0)
void setFiles(const QStringList &files)
static VerifyChecksumsDialog::Status string2status(const QByteArray &str)
static std::vector< File > parse_sum_file(const QString &fileName)
static QStringList find_base_directiories(const QStringList &files)
static std::vector< SumFile > find_sums_by_input_files(const QStringList &files, QStringList &errors, const function< void(int)> &progress, const std::vector< shared_ptr< ChecksumDefinition > > &checksumDefinitions)