Commit b0412b67 authored by Toni Asensi Esteve's avatar Toni Asensi Esteve
Browse files

Reduce code duplication (using a base class)

REVIEW: 124565
parent fcb2bcc4
include_directories(${KF5_INCLUDES_DIRS} ${QT_INCLUDES})
set(kio_krarc_PART_SRCS krarc.cpp)
set(kio_krarc_PART_SRCS
krarc.cpp
krarcbasemanager.cpp
krlinecountingprocess.cpp
)
add_library(kio_krarc MODULE ${kio_krarc_PART_SRCS})
......
......@@ -1633,6 +1633,7 @@ bool kio_krarcProtocol::checkStatus(int exitCode)
{
KRDEBUG(exitCode);
// if this code is changed, the code of KRarcHandler::checkStatus() must be reviewed
if (arcType == "zip" || arcType == "rar" || arcType == "7z")
return exitCode == 0 || exitCode == 1;
else if (arcType == "ace" || arcType == "bzip2" || arcType == "lha" || arcType == "rpm" || arcType == "arj")
......@@ -1643,180 +1644,36 @@ bool kio_krarcProtocol::checkStatus(int exitCode)
return exitCode == 0;
}
struct AutoDetectParams {
QString type;
int location;
QByteArray detectionString;
};
QString kio_krarcProtocol::detectArchive(bool &encrypted, QString fileName)
void kio_krarcProtocol::checkIf7zIsEncrypted(bool &encrypted, QString fileName)
{
static AutoDetectParams autoDetectParams[] = {{"zip", 0, "PK\x03\x04"},
{"rar", 0, "Rar!\x1a" },
{"arj", 0, "\x60\xea" },
{"rpm", 0, "\xed\xab\xee\xdb"},
{"ace", 7, "**ACE**" },
{"bzip2", 0, "\x42\x5a\x68\x39\x31" },
{"gzip", 0, "\x1f\x8b"},
{"deb", 0, "!<arch>\ndebian-binary " },
{"7z", 0, "7z\xbc\xaf\x27\x1c" }/*,
{"xz", 0, "\xfd\x37\x7a\x58\x5a\x00"}*/
};
static int autoDetectElems = sizeof(autoDetectParams) / sizeof(AutoDetectParams);
encrypted = false;
QFile arcFile(fileName);
if (arcFile.open(QIODevice::ReadOnly)) {
char buffer[ 1024 ];
long sizeMax = arcFile.read(buffer, sizeof(buffer));
arcFile.close();
for (int i = 0; i < autoDetectElems; i++) {
QByteArray detectionString = autoDetectParams[ i ].detectionString;
int location = autoDetectParams[ i ].location;
int endPtr = detectionString.length() + location;
if (endPtr > sizeMax)
continue;
int j = 0;
for (; j != detectionString.length(); j++) {
if (detectionString[ j ] == '?')
continue;
if (buffer[ location + j ] != detectionString[ j ])
break;
}
if (j == detectionString.length()) {
QString type = autoDetectParams[ i ].type;
if (type == "bzip2" || type == "gzip") {
KTar tapeArchive(fileName);
if (tapeArchive.open(QIODevice::ReadOnly)) {
tapeArchive.close();
if (type == "bzip2")
type = "tbz";
else
type = "tgz";
}
} else if (type == "zip")
encrypted = (buffer[6] & 1);
else if (type == "arj") {
if (sizeMax > 4) {
long headerSize = ((unsigned char *)buffer)[ 2 ] + 256 * ((unsigned char *)buffer)[ 3 ];
long fileHeader = headerSize + 10;
if (fileHeader + 9 < sizeMax && buffer[ fileHeader ] == (char)0x60 && buffer[ fileHeader + 1 ] == (char)0xea)
encrypted = (buffer[ fileHeader + 8 ] & 1);
}
} else if (type == "rar") {
if (sizeMax > 13 && buffer[ 9 ] == (char)0x73) {
if (buffer[ 10 ] & 0x80) { // the header is encrypted?
encrypted = true;
} else {
long offset = 7;
long mainHeaderSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ];
offset += mainHeaderSize;
while (offset + 10 < sizeMax) {
long headerSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ];
bool isDir = (buffer[ offset+7 ] == '\0') && (buffer[ offset+8 ] == '\0') &&
(buffer[ offset+9 ] == '\0') && (buffer[ offset+10 ] == '\0');
if (buffer[ offset + 2 ] != (char)0x74)
break;
if (!isDir) {
encrypted = (buffer[ offset + 3 ] & 4) != 0;
break;
}
offset += headerSize;
}
}
}
} else if (type == "ace") {
long offset = 0;
long mainHeaderSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4;
offset += mainHeaderSize;
while (offset + 10 < sizeMax) {
long headerSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4;
bool isDir = (buffer[ offset+11 ] == '\0') && (buffer[ offset+12 ] == '\0') &&
(buffer[ offset+13 ] == '\0') && (buffer[ offset+14 ] == '\0');
if (buffer[ offset + 4 ] != (char)0x01)
break;
if (!isDir) {
encrypted = (buffer[ offset + 6 ] & 64) != 0;
break;
}
offset += headerSize;
}
} else if (type == "7z") {
if (encryptedArchPath == fileName)
encrypted = true;
else { // we try to find whether the 7z archive is encrypted
// this is hard as the headers are also compresseds
QString tester = fullPathName("7z");
if (QStandardPaths::findExecutable(tester).isEmpty()) {
tester = fullPathName("7za");
if (QStandardPaths::findExecutable(tester).isEmpty()) {
return type;
}
}
QString testCmd = tester + " t -y ";
lastData = encryptedArchPath = "";
KrLinecountingProcess proc;
proc << testCmd << fileName;
connect(&proc, SIGNAL(newOutputData(KProcess *, QByteArray &)),
this, SLOT(checkOutputForPassword(KProcess *, QByteArray &)));
proc.start();
proc.waitForFinished();
encrypted = this->encrypted;
if (encrypted)
encryptedArchPath = fileName;
}
}
return type;
if (encryptedArchPath == fileName)
encrypted = true;
else { // we try to find whether the 7z archive is encrypted
// this is hard as the headers are also compressed
QString tester = fullPathName("7z");
if (QStandardPaths::findExecutable(tester).isEmpty()) {
KRDEBUG("A 7z program was not found");
tester = fullPathName("7za");
if (QStandardPaths::findExecutable(tester).isEmpty()) {
KRDEBUG("A 7za program was not found");
return;
}
}
if (sizeMax >= 512) {
/* checking if it's a tar file */
unsigned checksum = 32 * 8;
char chksum[ 9 ];
for (int i = 0; i != 512; i++)
checksum += ((unsigned char *)buffer)[ i ];
for (int i = 148; i != 156; i++)
checksum -= ((unsigned char *)buffer)[ i ];
sprintf(chksum, "0%o", checksum);
if (!memcmp(buffer + 148, chksum, strlen(chksum))) {
int k = strlen(chksum);
for (; k < 8; k++)
if (buffer[148+k] != 0 && buffer[148+k] != 32)
break;
if (k == 8)
return "tar";
}
}
}
QString testCmd = tester + " t -y ";
lastData = encryptedArchPath = "";
if (fileName.endsWith(QLatin1String(".tar.lzma")) ||
fileName.endsWith(QLatin1String(".tlz"))) {
return "tlz";
}
if (fileName.endsWith(QLatin1String(".lzma"))) {
return "lzma";
}
KrLinecountingProcess proc;
proc << testCmd << fileName;
connect(&proc, SIGNAL(newOutputData(KProcess *, QByteArray &)),
this, SLOT(checkOutputForPassword(KProcess *, QByteArray &)));
proc.start();
proc.waitForFinished();
encrypted = this->encrypted;
if (fileName.endsWith(QLatin1String(".tar.xz")) ||
fileName.endsWith(QLatin1String(".txz"))) {
return "txz";
if (encrypted)
encryptedArchPath = fileName;
}
if (fileName.endsWith(QLatin1String(".xz"))) {
return "xz";
}
return QString();
}
void kio_krarcProtocol::checkOutputForPassword(KProcess * proc, QByteArray & buf)
......@@ -1839,6 +1696,7 @@ void kio_krarcProtocol::checkOutputForPassword(KProcess * proc, QByteArray & buf
KRDEBUG("Encrypted 7z archive found!");
encrypted = true;
proc->kill();
return;
}
}
}
......
......@@ -28,11 +28,14 @@
#include <KConfigCore/KConfig>
#include <KConfigCore/KConfigGroup>
#include "krarcbasemanager.h"
#include "krlinecountingprocess.h"
class KFileItem;
class QByteArray;
class QTextCodec;
class kio_krarcProtocol : public QObject, public KIO::SlaveBase
class kio_krarcProtocol : public QObject, public KIO::SlaveBase, public KrArcBaseManager
{
Q_OBJECT
public:
......@@ -53,7 +56,7 @@ public slots:
protected:
virtual bool initDirDict(const QUrl &url, bool forced = false);
virtual bool initArcParameters();
QString detectArchive(bool &encrypted, QString fileName);
void checkIf7zIsEncrypted(bool &, QString);
virtual void parseLine(int lineNo, QString line);
virtual bool setArcFile(const QUrl &url);
virtual QString getPassword();
......@@ -113,61 +116,6 @@ private:
QTextCodec * codec;
};
class KrLinecountingProcess : public KProcess
{
Q_OBJECT
public:
KrLinecountingProcess() : KProcess() {
setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect!
connect(this, SIGNAL(readyReadStandardError()), SLOT(receivedError()));
connect(this, SIGNAL(readyReadStandardOutput()), SLOT(receivedOutput()));
mergedOutput = true;
}
void setMerge(bool value) {
mergedOutput = value;
}
QString getErrorMsg() {
if (errorData.trimmed().isEmpty())
return QString::fromLocal8Bit(outputData);
else
return QString::fromLocal8Bit(errorData);
}
public slots:
void receivedError() {
QByteArray newData(this->readAllStandardError());
emit newErrorLines(newData.count('\n'));
errorData += newData;
if (errorData.length() > 500)
errorData = errorData.right(500);
if (mergedOutput)
receivedOutput(newData);
}
void receivedOutput(QByteArray newData = QByteArray()) {
if (newData.isEmpty())
newData = this->readAllStandardOutput();
emit newOutputLines(newData.count('\n'));
emit newOutputData(this, newData);
outputData += newData;
if (outputData.length() > 500)
outputData = outputData.right(500);
}
signals:
void newOutputLines(int count);
void newErrorLines(int count);
void newOutputData(KProcess *, QByteArray &);
private:
QByteArray errorData;
QByteArray outputData;
bool mergedOutput;
};
#ifdef Q_WS_WIN
#define DIR_SEPARATOR "/"
#define DIR_SEPARATOR2 "\\"
......
#include "krarcbasemanager.h"
#include <KArchive/KTar>
KrArcBaseManager::AutoDetectParams KrArcBaseManager::autoDetectParams[] = {{"zip", 0, "PK\x03\x04"},
{"rar", 0, "Rar!\x1a" },
{"arj", 0, "\x60\xea" },
{"rpm", 0, "\xed\xab\xee\xdb"},
{"ace", 7, "**ACE**" },
{"bzip2", 0, "\x42\x5a\x68\x39\x31" },
{"gzip", 0, "\x1f\x8b"},
{"deb", 0, "!<arch>\ndebian-binary " },
{"7z", 0, "7z\xbc\xaf\x27\x1c" }/*,
{"xz", 0, "\xfd\x37\x7a\x58\x5a\x00"}*/
};
int KrArcBaseManager::autoDetectElems = sizeof(autoDetectParams) / sizeof(AutoDetectParams);
QString KrArcBaseManager::detectArchive(bool &encrypted, QString fileName, bool checkEncrypted, bool fast)
{
encrypted = false;
QFile arcFile(fileName);
if (arcFile.open(QIODevice::ReadOnly)) {
char buffer[ 1024 ];
long sizeMax = arcFile.read(buffer, sizeof(buffer));
arcFile.close();
for (int i = 0; i < autoDetectElems; i++) {
QByteArray detectionString = autoDetectParams[ i ].detectionString;
int location = autoDetectParams[ i ].location;
int endPtr = detectionString.length() + location;
if (endPtr > sizeMax)
continue;
int j = 0;
for (; j != detectionString.length(); j++) {
if (detectionString[ j ] == '?')
continue;
if (buffer[ location + j ] != detectionString[ j ])
break;
}
if (j == detectionString.length()) {
QString type = autoDetectParams[ i ].type;
if (type == "bzip2" || type == "gzip") {
if (fast) {
if (fileName.endsWith(QLatin1String(".tar.gz")))
type = "tgz";
else if (fileName.endsWith(QLatin1String(".tar.bz2")))
type = "tbz";
} else {
KTar tapeArchive(fileName);
if (tapeArchive.open(QIODevice::ReadOnly)) {
tapeArchive.close();
if (type == "gzip")
type = "tgz";
else if (type == "bzip2")
type = "tbz";
}
}
} else if (type == "zip")
encrypted = (buffer[6] & 1);
else if (type == "arj") {
if (sizeMax > 4) {
long headerSize = ((unsigned char *)buffer)[ 2 ] + 256 * ((unsigned char *)buffer)[ 3 ];
long fileHeader = headerSize + 10;
if (fileHeader + 9 < sizeMax && buffer[ fileHeader ] == (char)0x60 && buffer[ fileHeader + 1 ] == (char)0xea)
encrypted = (buffer[ fileHeader + 8 ] & 1);
}
} else if (type == "rar") {
if (sizeMax > 13 && buffer[ 9 ] == (char)0x73) {
if (buffer[ 10 ] & 0x80) { // the header is encrypted?
encrypted = true;
} else {
long offset = 7;
long mainHeaderSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ];
offset += mainHeaderSize;
while (offset + 10 < sizeMax) {
long headerSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ];
bool isDir = (buffer[ offset+7 ] == '\0') && (buffer[ offset+8 ] == '\0') &&
(buffer[ offset+9 ] == '\0') && (buffer[ offset+10 ] == '\0');
if (buffer[ offset + 2 ] != (char)0x74)
break;
if (!isDir) {
encrypted = (buffer[ offset + 3 ] & 4) != 0;
break;
}
offset += headerSize;
}
}
}
} else if (type == "ace") {
long offset = 0;
long mainHeaderSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4;
offset += mainHeaderSize;
while (offset + 10 < sizeMax) {
long headerSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4;
bool isDir = (buffer[ offset+11 ] == '\0') && (buffer[ offset+12 ] == '\0') &&
(buffer[ offset+13 ] == '\0') && (buffer[ offset+14 ] == '\0');
if (buffer[ offset + 4 ] != (char)0x01)
break;
if (!isDir) {
encrypted = (buffer[ offset + 6 ] & 64) != 0;
break;
}
offset += headerSize;
}
} else if (type == "7z") {
// encryption check is expensive, check only if it's necessary
if (checkEncrypted)
checkIf7zIsEncrypted(encrypted, fileName);
}
return type;
}
}
if (sizeMax >= 512) {
/* checking if it's a tar file */
unsigned checksum = 32 * 8;
char chksum[ 9 ];
for (int i = 0; i != 512; i++)
checksum += ((unsigned char *)buffer)[ i ];
for (int i = 148; i != 156; i++)
checksum -= ((unsigned char *)buffer)[ i ];
sprintf(chksum, "0%o", checksum);
if (!memcmp(buffer + 148, chksum, strlen(chksum))) {
int k = strlen(chksum);
for (; k < 8; k++)
if (buffer[148+k] != 0 && buffer[148+k] != 32)
break;
if (k == 8)
return "tar";
}
}
}
if (fileName.endsWith(QLatin1String(".tar.lzma")) ||
fileName.endsWith(QLatin1String(".tlz"))) {
return "tlz";
}
if (fileName.endsWith(QLatin1String(".lzma"))) {
return "lzma";
}
if (fileName.endsWith(QLatin1String(".tar.xz")) ||
fileName.endsWith(QLatin1String(".txz"))) {
return "txz";
}
if (fileName.endsWith(QLatin1String(".xz"))) {
return "xz";
}
return QString();
}
#ifndef KRARCBASEMANAGER_H
#define KRARCBASEMANAGER_H
#include <QtCore/QFile>
/*!
* \brief An abstract base class for managing archives.
*/
class KrArcBaseManager
{
private:
//! Information about a type of archive and the bytes that are used to detect it.
struct AutoDetectParams {
QString type;
int location;
QByteArray detectionString;
};
static AutoDetectParams autoDetectParams[]; //! Information used to detect if a file is an archive
static int autoDetectElems; //!< The size of autoDetectParams[]
public:
KrArcBaseManager() {}
QString detectArchive(bool &, QString, bool = true, bool = false);
virtual void checkIf7zIsEncrypted(bool &, QString) = 0;
virtual ~KrArcBaseManager() {}
};
#endif // KRARCBASEMANAGER_H
#include "krlinecountingprocess.h"
KrLinecountingProcess::KrLinecountingProcess() : KProcess()
{
setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect!
connect(this, SIGNAL(readyReadStandardError()), SLOT(receivedError()));
connect(this, SIGNAL(readyReadStandardOutput()), SLOT(receivedOutput()));
mergedOutput = true;
}
void KrLinecountingProcess::setMerge(bool value)
{
mergedOutput = value;
}
QString KrLinecountingProcess::getErrorMsg()
{
if (errorData.trimmed().isEmpty())
return QString::fromLocal8Bit(outputData);
else
return QString::fromLocal8Bit(errorData);
}
void KrLinecountingProcess::receivedError()
{
QByteArray newData(this->readAllStandardError());
emit newErrorLines(newData.count('\n'));
errorData += newData;
if (errorData.length() > 500)
errorData = errorData.right(500);
if (mergedOutput)
receivedOutput(newData);
}
void KrLinecountingProcess::receivedOutput(QByteArray newData)
{
if (newData.isEmpty())
newData = this->readAllStandardOutput();
emit newOutputLines(newData.count('\n'));
emit newOutputData(this, newData);
outputData += newData;
if (outputData.length() > 500)
outputData = outputData.right(500);
}
#ifndef KRLINECOUNTINGPROCESS_H
#define KRLINECOUNTINGPROCESS_H
#include <KCoreAddons/KProcess>
/**
* A KProcess which emits how many lines it is writing to stdout or stderr.
*/
class KrLinecountingProcess : public KProcess
{
Q_OBJECT
public:
KrLinecountingProcess();
void setMerge(bool);
QString getErrorMsg();
public slots:
void receivedError();
void receivedOutput(QByteArray = QByteArray());
signals:
void newOutputLines(int);
void newErrorLines(int);
void newOutputData(KProcess *, QByteArray &);
private:
QByteArray errorData;
QByteArray outputData;
bool mergedOutput;
};
#endif // KRLINECOUNTINGPROCESS_H
......@@ -39,7 +39,6 @@
#include <KIO/Global>
#include "../VFS/krquery.h"
#include "../VFS/vfile.h"
#include "../VFS/krpermhandler.h"
......@@ -47,6 +46,8 @@