You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
8.3 KiB
309 lines
8.3 KiB
/*************************************************************************** |
|
* Copyright (C) 2007 by Tobias Koenig <tokoe@kde.org> * |
|
* * |
|
* 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. * |
|
***************************************************************************/ |
|
|
|
#include "unrar.h" |
|
|
|
#include <QEventLoop> |
|
#include <QFile> |
|
#include <QFileInfo> |
|
#include <QRegExp> |
|
#include <QGlobalStatic> |
|
#include <QTemporaryDir> |
|
|
|
#include <QLoggingCategory> |
|
#if defined(WITH_KPTY) |
|
#include <KPty/kptyprocess.h> |
|
#include <KPty/kptydevice.h> |
|
#endif |
|
|
|
#include "debug_comicbook.h" |
|
|
|
#include <memory> |
|
#include <QStandardPaths> |
|
|
|
struct UnrarHelper |
|
{ |
|
UnrarHelper(); |
|
~UnrarHelper(); |
|
|
|
UnrarHelper(const UnrarHelper &) = delete; |
|
UnrarHelper &operator=(const UnrarHelper &) = delete; |
|
|
|
UnrarFlavour *kind; |
|
QString unrarPath; |
|
QString lsarPath; |
|
}; |
|
|
|
Q_GLOBAL_STATIC( UnrarHelper, helper ) |
|
|
|
static UnrarFlavour* detectUnrar( const QString &unrarPath, const QString &versionCommand ) |
|
{ |
|
UnrarFlavour* kind = nullptr; |
|
QProcess proc; |
|
proc.start( unrarPath, QStringList() << versionCommand ); |
|
bool ok = proc.waitForFinished( -1 ); |
|
Q_UNUSED( ok ) |
|
const QStringList lines = QString::fromLocal8Bit( proc.readAllStandardOutput() ).split( QLatin1Char('\n'), QString::SkipEmptyParts ); |
|
if ( !lines.isEmpty() ) |
|
{ |
|
if ( lines.first().startsWith( QLatin1String("UNRAR ") ) ) |
|
kind = new NonFreeUnrarFlavour(); |
|
else if ( lines.first().startsWith( QLatin1String("RAR ") ) ) |
|
kind = new NonFreeUnrarFlavour(); |
|
else if ( lines.first().startsWith( QLatin1String("unrar ") ) ) |
|
kind = new FreeUnrarFlavour(); |
|
else if ( lines.first().startsWith( QLatin1String("v") ) ) |
|
kind = new UnarFlavour(); |
|
} |
|
return kind; |
|
} |
|
|
|
UnrarHelper::UnrarHelper() |
|
: kind( nullptr ) |
|
{ |
|
QString path = QStandardPaths::findExecutable( QStringLiteral("lsar") ); |
|
|
|
if ( !path.isEmpty() ) |
|
{ |
|
lsarPath = path; |
|
} |
|
|
|
path = QStandardPaths::findExecutable( QStringLiteral("unrar-nonfree") ); |
|
|
|
if ( path.isEmpty() ) |
|
path = QStandardPaths::findExecutable( QStringLiteral("unrar") ); |
|
if ( path.isEmpty() ) |
|
path = QStandardPaths::findExecutable( QStringLiteral("rar") ); |
|
if ( path.isEmpty() ) |
|
path = QStandardPaths::findExecutable( QStringLiteral("unar") ); |
|
|
|
if ( !path.isEmpty() ) |
|
kind = detectUnrar( path, QStringLiteral("--version") ); |
|
|
|
if ( !kind ) |
|
kind = detectUnrar( path, QStringLiteral("-v") ); |
|
|
|
if ( !kind ) |
|
{ |
|
// no luck, print that |
|
qWarning() << "Neither unrar nor unarchiver were found."; |
|
} |
|
else |
|
{ |
|
unrarPath = path; |
|
qCDebug(OkularComicbookDebug) << "detected:" << path << "(" << kind->name() << ")"; |
|
} |
|
} |
|
|
|
UnrarHelper::~UnrarHelper() |
|
{ |
|
delete kind; |
|
} |
|
|
|
|
|
Unrar::Unrar() |
|
: QObject( nullptr ), mLoop( nullptr ), mTempDir( nullptr ) |
|
{ |
|
} |
|
|
|
Unrar::~Unrar() |
|
{ |
|
delete mTempDir; |
|
} |
|
|
|
bool Unrar::open( const QString &fileName ) |
|
{ |
|
if ( !isSuitableVersionAvailable() ) |
|
return false; |
|
|
|
delete mTempDir; |
|
mTempDir = new QTemporaryDir(); |
|
|
|
mFileName = fileName; |
|
|
|
/** |
|
* Extract the archive to a temporary directory |
|
*/ |
|
mStdOutData.clear(); |
|
mStdErrData.clear(); |
|
|
|
const int ret = startSyncProcess( helper->kind->processOpenArchiveArgs( mFileName, mTempDir->path() ) ); |
|
bool ok = ret == 0; |
|
|
|
return ok; |
|
} |
|
|
|
QStringList Unrar::list() |
|
{ |
|
mStdOutData.clear(); |
|
mStdErrData.clear(); |
|
|
|
if ( !isSuitableVersionAvailable() ) |
|
return QStringList(); |
|
|
|
startSyncProcess( helper->kind->processListArgs( mFileName ) ); |
|
|
|
QStringList listFiles = helper->kind->processListing( QString::fromLocal8Bit( mStdOutData ).split( QLatin1Char('\n'), QString::SkipEmptyParts ) ); |
|
|
|
QString subDir; |
|
|
|
if ( listFiles.last().endsWith( QLatin1Char('/') ) && helper->kind->name() == QLatin1String("unar") ) { |
|
// Subfolder detected. The unarchiver is unable to extract all files into a single folder |
|
subDir = listFiles.last(); |
|
listFiles.removeLast(); |
|
} |
|
|
|
QStringList newList; |
|
Q_FOREACH ( const QString &f, listFiles ) { |
|
// Extract all the files to mTempDir regardless of their path inside the archive |
|
// This will break if ever an arvhice with two files with the same name in different subfolders |
|
QFileInfo fi( f ); |
|
if ( QFile::exists( mTempDir->path() + QLatin1Char('/') + subDir + fi.fileName() ) ) { |
|
newList.append( subDir + fi.fileName() ); |
|
} |
|
} |
|
return newList; |
|
} |
|
|
|
QByteArray Unrar::contentOf( const QString &fileName ) const |
|
{ |
|
if ( !isSuitableVersionAvailable() ) |
|
return QByteArray(); |
|
|
|
QFile file( mTempDir->path() + QLatin1Char('/') + fileName ); |
|
if ( !file.open( QIODevice::ReadOnly ) ) |
|
return QByteArray(); |
|
|
|
return file.readAll(); |
|
} |
|
|
|
QIODevice* Unrar::createDevice( const QString &fileName ) const |
|
{ |
|
if ( !isSuitableVersionAvailable() ) |
|
return nullptr; |
|
|
|
std::unique_ptr< QFile> file( new QFile( mTempDir->path() + QLatin1Char('/') + fileName ) ); |
|
if ( !file->open( QIODevice::ReadOnly ) ) |
|
return nullptr; |
|
|
|
return file.release(); |
|
} |
|
|
|
bool Unrar::isAvailable() |
|
{ |
|
return helper->kind; |
|
} |
|
|
|
bool Unrar::isSuitableVersionAvailable() |
|
{ |
|
if ( !isAvailable() ) |
|
return false; |
|
|
|
if (dynamic_cast< NonFreeUnrarFlavour * >( helper->kind ) || dynamic_cast< UnarFlavour * >( helper->kind )) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
void Unrar::readFromStdout() |
|
{ |
|
if ( !mProcess ) |
|
return; |
|
|
|
mStdOutData += mProcess->readAllStandardOutput(); |
|
} |
|
|
|
void Unrar::readFromStderr() |
|
{ |
|
if ( !mProcess ) |
|
return; |
|
|
|
mStdErrData += mProcess->readAllStandardError(); |
|
if ( !mStdErrData.isEmpty() ) |
|
{ |
|
mProcess->kill(); |
|
return; |
|
} |
|
} |
|
|
|
void Unrar::finished( int exitCode, QProcess::ExitStatus exitStatus ) |
|
{ |
|
Q_UNUSED( exitCode ) |
|
if ( mLoop ) |
|
{ |
|
mLoop->exit( exitStatus == QProcess::CrashExit ? 1 : 0 ); |
|
} |
|
} |
|
|
|
int Unrar::startSyncProcess( const ProcessArgs &args ) |
|
{ |
|
int ret = 0; |
|
|
|
#if !defined(WITH_KPTY) |
|
mProcess = new QProcess( this ); |
|
connect(mProcess, &QProcess::readyReadStandardOutput, this, &Unrar::readFromStdout); |
|
connect(mProcess, &QProcess::readyReadStandardError, this, &Unrar::readFromStderr); |
|
connect(mProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &Unrar::finished); |
|
|
|
#else |
|
mProcess = new KPtyProcess( this ); |
|
mProcess->setOutputChannelMode( KProcess::SeparateChannels ); |
|
connect(mProcess, &KPtyProcess::readyReadStandardOutput, this, &Unrar::readFromStdout); |
|
connect(mProcess, &KPtyProcess::readyReadStandardError, this, &Unrar::readFromStderr); |
|
connect(mProcess, static_cast<void (KPtyProcess::*)(int, QProcess::ExitStatus)>(&KPtyProcess::finished), this, &Unrar::finished); |
|
|
|
#endif |
|
|
|
#if !defined(WITH_KPTY) |
|
if ( helper->kind->name() == QLatin1String( "unar" ) && args.useLsar ) |
|
{ |
|
mProcess->start( helper->lsarPath, args.appArgs, QIODevice::ReadWrite | QIODevice::Unbuffered ); |
|
} |
|
else |
|
{ |
|
mProcess->start( helper->unrarPath, args.appArgs, QIODevice::ReadWrite | QIODevice::Unbuffered ); |
|
} |
|
|
|
ret = mProcess->waitForFinished( -1 ) ? 0 : 1; |
|
#else |
|
if ( helper->kind->name() == QLatin1String( "unar" ) && args.useLsar ) |
|
{ |
|
mProcess->setProgram( helper->lsarPath, args.appArgs ); |
|
} |
|
else |
|
{ |
|
mProcess->setProgram( helper->unrarPath, args.appArgs ); |
|
} |
|
|
|
mProcess->setNextOpenMode( QIODevice::ReadWrite | QIODevice::Unbuffered ); |
|
mProcess->start(); |
|
QEventLoop loop; |
|
mLoop = &loop; |
|
ret = loop.exec( QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents ); |
|
mLoop = nullptr; |
|
#endif |
|
|
|
delete mProcess; |
|
mProcess = nullptr; |
|
|
|
return ret; |
|
} |
|
|
|
void Unrar::writeToProcess( const QByteArray &data ) |
|
{ |
|
if ( !mProcess || data.isNull() ) |
|
return; |
|
|
|
#if !defined(WITH_KPTY) |
|
mProcess->write( data ); |
|
#else |
|
mProcess->pty()->write( data ); |
|
#endif |
|
} |
|
|
|
|