33 #include <config-kleopatra.h>
42 #include <kleo/stl_util.h>
43 #include <kleo/checksumdefinition.h>
51 #include <QDialogButtonBox>
53 #include <QListWidget>
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 boost;
78 class ResultDialog :
public QDialog {
81 ResultDialog(
const QStringList & created,
const QStringList & errors,
QWidget * parent=0, Qt::WindowFlags f=0 )
83 createdLB( created.empty()
84 ? i18nc(
"@info",
"No checksum files have been created.")
85 : i18nc(
"@info",
"These checksum files have been successfully created:"), this ),
87 errorsLB( errors.empty()
88 ? i18nc(
"@info",
"There were no errors.")
89 : i18nc(
"@info",
"The following errors were encountered:"), this ),
91 buttonBox( QDialogButtonBox::
Ok, Qt::Horizontal, this ),
101 createdLW.addItems( created );
103 for(
int i = 0; i < created.size(); ++i )
104 r = r.united( createdLW.visualRect( createdLW.model()->index( 0, i ) ) );
105 createdLW.setMinimumWidth( qMin( 1024, r.width() + 4 * createdLW.frameWidth() ) );
107 errorsLW.addItems( errors );
109 vlay.addWidget( &createdLB );
110 vlay.addWidget( &createdLW, 1 );
111 vlay.addWidget( &errorsLB );
112 vlay.addWidget( &errorsLW, 1 );
113 vlay.addWidget( &buttonBox );
115 if ( created.empty() )
117 if ( errors.empty() )
120 connect( &buttonBox, SIGNAL(accepted()),
this, SLOT(accept()) );
121 connect( &buttonBox, SIGNAL(rejected()),
this, SLOT(reject()) );
126 QListWidget createdLW;
128 QListWidget errorsLW;
129 QDialogButtonBox buttonBox;
141 static const Qt::CaseSensitivity
fs_cs =
HAVE_UNIX ? Qt::CaseSensitive : Qt::CaseInsensitive ;
144 int (*QString_compare)(
const QString&,
const QString&,Qt::CaseSensitivity) = &QString::compare;
145 kdtools::sort( l, boost::bind( QString_compare, _1, _2,
fs_cs ) < 0 );
150 int (*QString_compare)(
const QString&,
const QString&,Qt::CaseSensitivity) = &QString::compare;
154 std::set_intersection( l1.begin(), l1.end(),
155 l2.begin(), l2.end(),
156 std::back_inserter( result ),
157 boost::bind( QString_compare, _1, _2,
fs_cs ) < 0 );
166 Q_FOREACH(
const QString & pattern, cd->patterns() )
167 result.push_back( QRegExp( pattern,
fs_cs ) );
172 struct matches_any : std::unary_function<QString,bool> {
174 explicit matches_any(
const QList<QRegExp> & regexps ) : m_regexps( regexps ) {}
175 bool operator()(
const QString & s )
const {
176 return kdtools::any( m_regexps, boost::bind( &QRegExp::exactMatch, _1, s ) );
181 class CreateChecksumsController::Private :
public QThread {
183 friend class ::Kleo::Crypto::CreateChecksumsController;
190 void progress(
int,
int,
const QString & );
193 void slotOperationFinished() {
194 #ifndef QT_NO_PROGRESSDIALOG
195 if ( progressDialog ) {
196 progressDialog->setValue( progressDialog->maximum() );
197 progressDialog->close();
199 #endif // QT_NO_PROGRESSDIALOG
200 ResultDialog *
const dlg =
new ResultDialog( created, errors );
201 q->bringToForeground( dlg );
202 if ( !errors.empty() )
203 q->setLastError( gpg_error( GPG_ERR_GENERAL ),
204 errors.join( QLatin1String(
"\n") ) );
205 q->emitDoneOrError();
207 void slotProgress(
int current,
int total,
const QString & what ) {
208 kDebug() <<
"progress: " << current <<
"/" << total <<
": " << qPrintable( what );
209 #ifndef QT_NO_PROGRESSDIALOG
210 if ( !progressDialog )
212 progressDialog->setMaximum( total );
213 progressDialog->setValue( current );
214 progressDialog->setLabelText( what );
215 #endif // QT_NO_PROGRESSDIALOG
222 #ifndef QT_NO_PROGRESSDIALOG
223 QPointer<QProgressDialog> progressDialog;
225 mutable QMutex
mutex;
226 const std::vector< shared_ptr<ChecksumDefinition> > checksumDefinitions;
229 QStringList errors, created;
231 volatile bool canceled;
236 #ifndef QT_NO_PROGRESSDIALOG
240 checksumDefinitions( ChecksumDefinition::getChecksumDefinitions() ),
241 checksumDefinition( ChecksumDefinition::getDefaultChecksumDefinition( checksumDefinitions ) ),
245 allowAddition( false ),
248 connect(
this, SIGNAL(progress(
int,
int,QString)),
249 q, SLOT(slotProgress(
int,
int,QString)) );
250 connect(
this, SIGNAL(progress(
int,
int,QString)),
251 q, SIGNAL(progress(
int,
int,QString)) );
252 connect(
this, SIGNAL(finished()),
253 q, SLOT(slotOperationFinished()) );
256 CreateChecksumsController::Private::~Private() { kDebug(); }
258 CreateChecksumsController::CreateChecksumsController( QObject * p )
278 if ( !kdtools::all( files, matches_any( patterns ) ) &&
279 !kdtools::none_of( files, matches_any( patterns ) ) )
280 throw Exception( gpg_error( GPG_ERR_INV_ARG ), i18n(
"Create Checksums: input files must be either all checksum files or all files to be checksummed, not a mixture of both.") );
281 const QMutexLocker locker( &d->mutex );
287 const QMutexLocker locker( &d->mutex );
288 d->allowAddition = allow;
292 const QMutexLocker locker( &d->mutex );
293 return d->allowAddition;
299 const QMutexLocker locker( &d->mutex );
301 #ifndef QT_NO_PROGRESSDIALOG
302 d->progressDialog =
new QProgressDialog( i18n(
"Initializing..."), i18n(
"Cancel"), 0, 0 );
304 d->progressDialog->setAttribute( Qt::WA_DeleteOnClose );
305 d->progressDialog->setMinimumDuration( 1000 );
306 d->progressDialog->setWindowTitle( i18nc(
"@title:window",
"Create Checksum Progress") );
307 connect( d->progressDialog, SIGNAL(canceled()),
this, SLOT(
cancel()) );
308 #endif // QT_NO_PROGRESSDIALOG
321 const QMutexLocker locker( &d->mutex );
330 QStringList inputFiles;
338 QStringList::iterator end = l.end();
339 Q_FOREACH(
const QRegExp & rx, rxs )
340 end = std::remove_if( l.begin(), end,
341 boost::bind( &QRegExp::exactMatch, rx, _1 ) );
342 l.erase( end, l.end() );
354 static QString
decode(
const QString & encoded ) {
356 decoded.reserve( encoded.size() );
358 Q_FOREACH(
const QChar ch, encoded )
360 switch ( ch.toLatin1() ) {
361 case '\\': decoded += QLatin1Char(
'\\' );
break;
362 case 'n': decoded += QLatin1Char(
'\n' );
break;
364 kDebug() <<
"invalid escape sequence" <<
'\\' << ch <<
"(interpreted as '" << ch <<
"')";
370 if ( ch == QLatin1Char(
'\\' ) )
379 std::vector<File> files;
381 if ( f.open( QIODevice::ReadOnly ) ) {
383 QRegExp rx( QLatin1String(
"(\\?)([a-f0-9A-F]+) ([ *])([^\n]+)\n*") );
384 while ( !s.atEnd() ) {
385 const QString line = s.readLine();
386 if ( rx.exactMatch( line ) ) {
387 assert( !rx.cap(4).endsWith( QLatin1Char(
'\n') ) );
389 rx.cap( 1 ) == QLatin1String(
"\\") ?
decode( rx.cap( 4 ) ) : rx.cap( 4 ),
390 rx.cap( 2 ).toLatin1(),
391 rx.cap( 3 ) == QLatin1String(
"*"),
393 files.push_back( file );
402 Q_FOREACH(
const QString & file, files )
403 n += QFileInfo( dir.absoluteFilePath( file ) ).size();
412 Q_FOREACH(
const QString & pattern, cd->patterns() )
413 if ( QRegExp( pattern,
fs_cs ).exactMatch( fileName ) )
419 const function<
void(
int)> & progress,
425 std::vector<Dir> dirs;
426 dirs.reserve( files.size() );
430 Q_FOREACH(
const QString & file, files ) {
432 const QFileInfo fi( file );
433 const QDir
dir = fi.dir();
436 QStringList inputFiles;
437 if ( allowAddition ) {
438 inputFiles = entries;
440 const std::vector<File> parsed =
parse_sum_file( fi.absoluteFilePath() );
441 const QStringList oldInputFiles =
442 kdtools::transform<QStringList>( parsed, mem_fn( &
File::name ) );
454 dirs.push_back( item );
456 if ( !progress.empty() )
464 struct less_dir : std::binary_function<QDir,QDir,bool> {
465 bool operator()(
const QDir & lhs,
const QDir & rhs )
const {
466 return QString::compare( lhs.absolutePath(), rhs.absolutePath(),
fs_cs ) < 0 ;
472 const function<
void(
int)> & progress,
475 Q_UNUSED( allowAddition );
476 if ( !checksumDefinition )
477 return std::vector<Dir>();
481 std::map<QDir,QStringList,less_dir> dirs2files;
485 std::deque<QString> inputs( files.begin(), files.end() );
488 while ( !inputs.empty() ) {
489 const QString file = inputs.front();
491 const QFileInfo fi( file );
495 kdtools::transform( dir.entryList( QDir::Dirs|QDir::NoDotAndDotDot ),
496 std::inserter( inputs, inputs.begin() ),
497 boost::bind( &QDir::absoluteFilePath, cref(dir), _1 ) );
499 dirs2files[fi.dir()].push_back( file );
501 if ( !progress.empty() )
507 std::vector<Dir> dirs;
508 dirs.reserve( dirs2files.size() );
510 for ( std::map<QDir,QStringList,less_dir>::const_iterator it = dirs2files.begin(), end = dirs2files.end() ; it != end ; ++it ) {
513 if ( inputFiles.empty() )
518 checksumDefinition->outputFileName(),
523 dirs.push_back( dir );
525 if ( !progress.empty() )
533 const QString absFilePath = dir.dir.absoluteFilePath( dir.sumFile );
534 KSaveFile file( absFilePath );
536 return i18n(
"Failed to open file \"%1\" for reading and writing: %2",
537 dir.dir.absoluteFilePath( file.fileName() ),
538 file.errorString() );
540 p.setWorkingDirectory( dir.dir.absolutePath() );
541 p.setStandardOutputFile( dir.dir.absoluteFilePath( file.QFile::fileName() ) );
542 const QString program = dir.checksumDefinition->createCommand();
543 dir.checksumDefinition->startCreateCommand( &p, dir.inputFiles );
545 kDebug() <<
"[" << &p <<
"] Exit code " << p.exitCode();
547 if ( p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0 ) {
549 if ( fatal && p.error() == QProcess::FailedToStart )
551 if ( p.error() == QProcess::UnknownError )
552 return i18n(
"Error while running %1: %2", program,
553 QString::fromLocal8Bit( p.readAllStandardError().trimmed().constData() ) );
555 return i18n(
"Failed to execute %1: %2", program, p.errorString() );
558 if ( !file.finalize() )
559 return i18n(
"Failed to move file %1 to its final destination, %2: %3",
560 file.fileName(), dir.sumFile, file.errorString() );
567 return s <<
"Dir(" << dir.dir <<
"->" << dir.sumFile <<
"<-(" << dir.totalSize <<
')' << dir.inputFiles <<
")\n";
571 void CreateChecksumsController::Private::run() {
573 QMutexLocker locker( &
mutex );
575 const QStringList files = this->files;
576 const std::vector< shared_ptr<ChecksumDefinition> > checksumDefinitions = this->checksumDefinitions;
578 const bool allowAddition = this->allowAddition;
585 if ( !checksumDefinition ) {
586 errors.push_back( i18n(
"No checksum programs defined.") );
588 this->errors = errors;
591 kDebug() <<
"using checksum-definition" << checksumDefinition->id();
598 const QString scanning = i18n(
"Scanning directories...");
599 emit progress( 0, 0, scanning );
601 const bool haveSumFiles
602 = kdtools::all( files, matches_any(
get_patterns( checksumDefinitions ) ) );
603 const function<void(int)> progressCb = boost::bind( &Private::progress,
this, _1, 0, scanning );
604 const std::vector<Dir> dirs = haveSumFiles
608 Q_FOREACH(
const Dir &
dir, dirs )
613 emit progress( 0, 0, i18n(
"Calculating total size...") );
616 = kdtools::accumulate_transform( dirs, mem_fn( &Dir::totalSize ), Q_UINT64_C(0) );
625 const quint64 factor = total / std::numeric_limits<int>::max() + 1 ;
628 Q_FOREACH(
const Dir & dir, dirs ) {
629 emit progress( done/factor, total/factor,
630 i18n(
"Checksumming (%2) in %1", dir.checksumDefinition->label(), dir.dir.path() ) );
632 const QString error =
process( dir, &fatal );
633 if ( !error.isEmpty() )
634 errors.push_back( error );
636 created.push_back( dir.dir.absoluteFilePath( dir.sumFile ) );
637 done += dir.totalSize;
638 if ( fatal || canceled )
641 emit progress( done/factor, total/factor, i18n(
"Done.") );
648 this->errors = errors;
649 this->created = created;
655 #include "moc_createchecksumscontroller.cpp"
656 #include "createchecksumscontroller.moc"
static const bool HAVE_UNIX
static QStringList fs_sort(QStringList l)
static std::vector< File > parse_sum_file(const QString &fileName)
void applyWindowID(QWidget *wid)
static QList< QRegExp > get_patterns(const std::vector< shared_ptr< ChecksumDefinition > > &checksumDefinitions)
static QStringList fs_intersect(QStringList l1, QStringList l2)
CreateChecksumsController(QObject *parent=0)
static const Qt::CaseSensitivity fs_cs
bool allowAddition() const
static quint64 aggregate_size(const QDir &dir, const QStringList &files)
static QString process(const Dir &dir, bool *fatal)
static QStringList remove_checksum_files(QStringList l, const QList< QRegExp > &rxs)
static std::vector< Dir > find_dirs_by_input_files(const QStringList &files, const shared_ptr< ChecksumDefinition > &checksumDefinition, bool allowAddition, const function< void(int)> &progress, const std::vector< shared_ptr< ChecksumDefinition > > &checksumDefinitions)
#define kleo_assert(cond)
static QString dir(const QString &id)
void setAllowAddition(bool allow)
static std::vector< Dir > find_dirs_by_sum_files(const QStringList &files, bool allowAddition, const function< void(int)> &progress, const std::vector< shared_ptr< ChecksumDefinition > > &checksumDefinitions)
void setFiles(const QStringList &files)
~CreateChecksumsController()
static shared_ptr< ChecksumDefinition > filename2definition(const QString &fileName, const std::vector< shared_ptr< ChecksumDefinition > > &checksumDefinitions)
static QString decode(const QString &encoded)