okular/generators/comicbook/unrar.cpp
Ahmad Samir 4a4456abd7 Port QRegExp to QRegularExpression in a couple of locations
Some instances of QRegExp are still left:
generators/mobipocket and generators/epub
2020-01-26 11:40:33 +00:00

309 lines
8.3 KiB
C++

/***************************************************************************
* 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 <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;
for ( const QString &f : qAsConst(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
}