summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladyslav Batyrenko <mvlabat@gmail.com>2016-08-15 16:54:13 (GMT)
committerVladyslav Batyrenko <mvlabat@gmail.com>2016-08-15 17:04:22 (GMT)
commit7eb3304db549df12afca7e25698cdf369e6e4a7e (patch)
treed69d2394df74eb4d070b2d39dc7bfc6d34a8f783
parentace277330b373098e4ad96a2d4f57178e9c10a97 (diff)
parent14a4666635e4485327bff76a1ccb96ac29534223 (diff)
[GSoC] Merge master into gsoc2016/mastergsoc2016/master
-rw-r--r--CMakeLists.txt2
-rw-r--r--app/CMakeLists.txt6
-rw-r--r--app/batchextract.cpp6
-rw-r--r--app/main.cpp25
-rw-r--r--app/mainwindow.cpp19
-rw-r--r--app/org.kde.ark.appdata.xml6
-rwxr-xr-xapp/org.kde.ark.desktop.cmake4
-rw-r--r--autotests/CMakeLists.txt1
-rw-r--r--autotests/app/CMakeLists.txt9
-rw-r--r--autotests/app/batchextracttest.cpp94
-rw-r--r--autotests/app/data/simple%archive.tar.gzbin0 -> 197 bytes
-rw-r--r--autotests/kerfuffle/addtoarchivetest.cpp113
-rw-r--r--autotests/kerfuffle/data/archive-multivolume.7z.001bin0 -> 15360 bytes
-rw-r--r--autotests/kerfuffle/data/archive-multivolume.7z.002bin0 -> 15360 bytes
-rw-r--r--autotests/kerfuffle/data/archive-multivolume.7z.003bin0 -> 196 bytes
-rw-r--r--autotests/kerfuffle/data/archive-multivolume.part1.rarbin0 -> 15360 bytes
-rw-r--r--autotests/kerfuffle/data/archive-multivolume.part2.rarbin0 -> 15360 bytes
-rw-r--r--autotests/kerfuffle/data/archive-multivolume.part3.rarbin0 -> 437 bytes
-rwxr-xr-xautotests/kerfuffle/data/hello-1.0-x86_64.AppImagebin0 -> 196608 bytes
-rw-r--r--autotests/kerfuffle/data/hello-appimage-runtime.c376
-rw-r--r--autotests/kerfuffle/data/test%dir/testfile1.txt1
-rw-r--r--autotests/kerfuffle/data/test%dir/testfile2.txt1
-rw-r--r--autotests/kerfuffle/data/testdirwithemptysubdir/testfile1.txt1
-rw-r--r--autotests/kerfuffle/data/testdirwithemptysubdir/testfile2.txt1
-rw-r--r--autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/subdir3/testfile4.txt1
-rw-r--r--autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/testfile3.txt1
-rw-r--r--autotests/kerfuffle/data/testdirwithsubdirs/subdir1/testfile2.txt1
-rw-r--r--autotests/kerfuffle/data/testdirwithsubdirs/testfile1.txt1
-rw-r--r--autotests/kerfuffle/extracttest.cpp83
-rw-r--r--autotests/kerfuffle/jobstest.cpp5
-rw-r--r--autotests/kerfuffle/mimetypetest.cpp2
-rw-r--r--autotests/plugins/cli7zplugin/cli7ztest.cpp56
-rw-r--r--autotests/plugins/cli7zplugin/data/archive-encrypted-1602.txt59
-rw-r--r--autotests/plugins/cli7zplugin/data/archive-multivol-1602.txt48
-rw-r--r--autotests/plugins/cli7zplugin/data/archive-with-symlink-1602.txt119
-rw-r--r--autotests/plugins/clirarplugin/clirartest.cpp62
-rw-r--r--autotests/plugins/clirarplugin/data/archive-multivol-unrar4.txt68
-rw-r--r--autotests/plugins/clirarplugin/data/archive-multivol-unrar5.txt84
-rw-r--r--autotests/plugins/cliunarchiverplugin/cliunarchivertest.cpp8
-rw-r--r--autotests/plugins/cliunarchiverplugin/data/hidden_files.rarbin0 -> 294 bytes
-rw-r--r--autotests/plugins/clizipplugin/cliziptest.cpp2
-rw-r--r--doc/create-archive.pngbin0 -> 25773 bytes
-rw-r--r--doc/create-protected-archive.pngbin21168 -> 45505 bytes
-rw-r--r--doc/index.docbook52
-rw-r--r--doc/man-ark.1.docbook19
-rw-r--r--kerfuffle/adddialog.cpp2
-rw-r--r--kerfuffle/addtoarchive.cpp6
-rw-r--r--kerfuffle/archive_kerfuffle.cpp74
-rw-r--r--kerfuffle/archive_kerfuffle.h15
-rw-r--r--kerfuffle/archiveformat.cpp14
-rw-r--r--kerfuffle/archiveformat.h5
-rw-r--r--kerfuffle/archiveinterface.cpp22
-rw-r--r--kerfuffle/archiveinterface.h7
-rw-r--r--kerfuffle/cliinterface.cpp128
-rw-r--r--kerfuffle/cliinterface.h71
-rw-r--r--kerfuffle/compressionoptionswidget.cpp48
-rw-r--r--kerfuffle/compressionoptionswidget.h4
-rw-r--r--kerfuffle/compressionoptionswidget.ui75
-rw-r--r--kerfuffle/createdialog.cpp5
-rw-r--r--kerfuffle/createdialog.h1
-rw-r--r--kerfuffle/extractiondialog.cpp6
-rw-r--r--kerfuffle/jobs.cpp71
-rw-r--r--kerfuffle/jobs.h25
-rw-r--r--kerfuffle/kerfufflePlugin.desktop2
-rw-r--r--kerfuffle/mime/XmlMessages.sh (renamed from kerfuffle/mime/XMLMessages.sh)0
-rw-r--r--kerfuffle/mime/kerfuffle.xml86
-rw-r--r--kerfuffle/propertiesdialog.cpp64
-rw-r--r--kerfuffle/propertiesdialog.h15
-rw-r--r--kerfuffle/propertiesdialog.ui58
-rw-r--r--part/archivemodel.cpp82
-rw-r--r--part/archivemodel.h13
-rw-r--r--part/part.cpp87
-rw-r--r--part/part.h4
-rw-r--r--plugins/CMakeLists.txt7
-rw-r--r--plugins/cli7zplugin/CMakeLists.txt9
-rw-r--r--plugins/cli7zplugin/cliplugin.cpp14
-rw-r--r--plugins/cli7zplugin/kerfuffle_cli7z.json.cmake6
-rw-r--r--plugins/cliplugin-example/CMakeLists.txt6
-rw-r--r--plugins/clirarplugin/CMakeLists.txt7
-rw-r--r--plugins/clirarplugin/cliplugin.cpp31
-rw-r--r--plugins/clirarplugin/cliplugin.h1
-rw-r--r--plugins/clirarplugin/kerfuffle_clirar.json.cmake3
-rw-r--r--plugins/cliunarchiverplugin/CMakeLists.txt7
-rw-r--r--plugins/cliunarchiverplugin/cliplugin.cpp112
-rw-r--r--plugins/cliunarchiverplugin/cliplugin.h5
-rw-r--r--plugins/clizipplugin/CMakeLists.txt6
-rw-r--r--plugins/clizipplugin/cliplugin.cpp3
-rw-r--r--plugins/libarchive/CMakeLists.txt16
-rw-r--r--plugins/libarchive/kerfuffle_libarchive.json.cmake5
-rw-r--r--plugins/libsinglefileplugin/CMakeLists.txt28
90 files changed, 2164 insertions, 428 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 321e8a0..64773f2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@ set(KF5_MIN_VERSION 5.16.0)
# KDE Application Version, managed by release script
set (KDE_APPLICATIONS_VERSION_MAJOR "16")
-set (KDE_APPLICATIONS_VERSION_MINOR "07")
+set (KDE_APPLICATIONS_VERSION_MINOR "11")
set (KDE_APPLICATIONS_VERSION_MICRO "70")
set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}")
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index e57f7c9..eced952 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -48,11 +48,13 @@ install(FILES arkui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/ark)
set(extracthere_SRCS batchextract.cpp extractHereDndPlugin.cpp ark_debug.cpp)
-add_library(extracthere MODULE ${extracthere_SRCS})
+kcoreaddons_add_plugin(extracthere
+ SOURCES ${extracthere_SRCS}
+ INSTALL_NAMESPACE kf5/kio_dnd)
+
kcoreaddons_desktop_to_json(extracthere ${CMAKE_CURRENT_BINARY_DIR}/ark_dndextract.desktop DEFAULT_SERVICE_TYPE)
target_link_libraries(extracthere kerfuffle KF5::Parts KF5::KIOWidgets KF5::KIOFileWidgets)
-install(TARGETS extracthere DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kio_dnd)
# compressfileitemaction plugin
diff --git a/app/batchextract.cpp b/app/batchextract.cpp
index f8f8fab..f14a70a 100644
--- a/app/batchextract.cpp
+++ b/app/batchextract.cpp
@@ -213,12 +213,12 @@ void BatchExtract::forwardProgress(KJob *job, unsigned long percent)
bool BatchExtract::addInput(const QUrl& url)
{
- qCDebug(ARK) << "Adding archive" << url.toDisplayString(QUrl::PreferLocalFile);
+ qCDebug(ARK) << "Adding archive" << url.toLocalFile();
- Kerfuffle::Archive *archive = Kerfuffle::Archive::create(url.toDisplayString(QUrl::PreferLocalFile), this);
+ Kerfuffle::Archive *archive = Kerfuffle::Archive::create(url.toLocalFile(), this);
Q_ASSERT(archive);
- if (!QFileInfo::exists(url.toDisplayString(QUrl::PreferLocalFile))) {
+ if (!QFileInfo::exists(url.toLocalFile())) {
m_failedFiles.append(url.fileName());
return false;
}
diff --git a/app/main.cpp b/app/main.cpp
index a9539b5..36e2a1c 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -67,10 +67,9 @@ int main(int argc, char **argv)
i18n("KDE Archiving tool"),
KAboutLicense::GPL,
i18n("(c) 1997-2016, The Ark Developers"),
- QStringLiteral(),
- QStringLiteral("http://utils.kde.org/projects/ark"),
- QStringLiteral()
- );
+ QString(),
+ QStringLiteral("http://utils.kde.org/projects/ark")
+ );
aboutData.setOrganizationDomain("kde.org");
@@ -93,22 +92,22 @@ int main(int argc, char **argv)
i18n("Former maintainer"),
QStringLiteral("helio@kde.org"));
aboutData.addAuthor(i18n("Georg Robbers"),
- QStringLiteral(),
+ QString(),
QStringLiteral("Georg.Robbers@urz.uni-hd.de"));
aboutData.addAuthor(i18n("Roberto Selbach Teixeira"),
- QStringLiteral(),
+ QString(),
QStringLiteral("maragato@kde.org"));
aboutData.addAuthor(i18n("Francois-Xavier Duranceau"),
- QStringLiteral(),
+ QString(),
QStringLiteral("duranceau@kde.org"));
aboutData.addAuthor(i18n("Emily Ezust (Corel Corporation)"),
- QStringLiteral(),
+ QString(),
QStringLiteral("emilye@corel.com"));
aboutData.addAuthor(i18n("Michael Jarrett (Corel Corporation)"),
- QStringLiteral(),
+ QString(),
QStringLiteral("michaelj@corel.com"));
aboutData.addAuthor(i18n("Robert Palmbos"),
- QStringLiteral(),
+ QString(),
QStringLiteral("palm9744@kettering.edu"));
aboutData.addCredit(i18n("Bryce Corkins"),
@@ -119,7 +118,7 @@ int main(int argc, char **argv)
QStringLiteral("smitty@absamail.co.za"));
aboutData.addCredit(i18n("Andrew Smith"),
i18n("bkisofs code"),
- QStringLiteral(),
+ QString(),
QStringLiteral("http://littlesvr.ca/misc/contactandrew.php"));
application.setWindowIcon(QIcon::fromTheme(QStringLiteral("ark")));
@@ -200,7 +199,7 @@ int main(int argc, char **argv)
AddToArchive *addToArchiveJob = new AddToArchive(&application);
application.setQuitOnLastWindowClosed(false);
- application.connect(addToArchiveJob, SIGNAL(result(KJob*)), SLOT(quit()), Qt::QueuedConnection);
+ QObject::connect(addToArchiveJob, &KJob::result, &application, &QCoreApplication::quit, Qt::QueuedConnection);
if (parser.isSet(QStringLiteral("changetofirstpath"))) {
qCDebug(ARK) << "Setting changetofirstpath";
@@ -238,7 +237,7 @@ int main(int argc, char **argv)
BatchExtract *batchJob = new BatchExtract(&application);
application.setQuitOnLastWindowClosed(false);
- application.connect(batchJob, SIGNAL(result(KJob*)), SLOT(quit()), Qt::QueuedConnection);
+ QObject::connect(batchJob, &KJob::result, &application, &QCoreApplication::quit, Qt::QueuedConnection);
for (int i = 0; i < urls.count(); ++i) {
qCDebug(ARK) << "Adding url" << QUrl::fromUserInput(urls.at(i), QString(), QUrl::AssumeLocalFile);
diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp
index e990db7..a38e912 100644
--- a/app/mainwindow.cpp
+++ b/app/mainwindow.cpp
@@ -127,14 +127,19 @@ void MainWindow::dragMoveEvent(QDragMoveEvent * event)
bool MainWindow::loadPart()
{
- KPluginFactory *factory = 0;
- KService::Ptr service = KService::serviceByDesktopName(QStringLiteral("ark_part"));
+ KPluginFactory *factory = Q_NULLPTR;
- if (service) {
- factory = KPluginLoader(service->library()).factory();
+ const auto plugins = KPluginLoader::findPlugins(QString(), [](const KPluginMetaData& metaData) {
+ return metaData.pluginId() == QStringLiteral("arkpart") &&
+ metaData.serviceTypes().contains(QStringLiteral("KParts/ReadOnlyPart")) &&
+ metaData.serviceTypes().contains(QStringLiteral("Browser/View"));
+ });
+
+ if (!plugins.isEmpty()) {
+ factory = KPluginLoader(plugins.first().fileName()).factory();
}
- m_part = factory ? static_cast<KParts::ReadWritePart*>(factory->create<KParts::ReadWritePart>(this)) : 0;
+ m_part = factory ? static_cast<KParts::ReadWritePart*>(factory->create<KParts::ReadWritePart>(this)) : Q_NULLPTR;
if (!m_part) {
KMessageBox::error(this, i18n("Unable to find Ark's KPart component, please check your installation."));
@@ -295,6 +300,10 @@ void MainWindow::newArchive()
if (dialog.data()->compressionLevel() > -1) {
m_openArgs.metaData()[QStringLiteral("compressionLevel")] = QString::number(dialog.data()->compressionLevel());
}
+ if (dialog.data()->volumeSize() > 0) {
+ qCDebug(ARK) << "Setting volume size:" << QString::number(dialog.data()->volumeSize());
+ m_openArgs.metaData()[QStringLiteral("volumeSize")] = QString::number(dialog.data()->volumeSize());
+ }
m_openArgs.metaData()[QStringLiteral("encryptionPassword")] = password;
if (dialog.data()->isHeaderEncryptionEnabled()) {
diff --git a/app/org.kde.ark.appdata.xml b/app/org.kde.ark.appdata.xml
index 0e10f60..25bc5fd 100644
--- a/app/org.kde.ark.appdata.xml
+++ b/app/org.kde.ark.appdata.xml
@@ -20,6 +20,7 @@
<name xml:lang="fi">Ark</name>
<name xml:lang="fr">Ark</name>
<name xml:lang="gl">Ark</name>
+ <name xml:lang="he">Ark</name>
<name xml:lang="hu">Ark</name>
<name xml:lang="it">Ark</name>
<name xml:lang="ko">Ark</name>
@@ -61,6 +62,7 @@
<summary xml:lang="fi">Pakkausohjelma</summary>
<summary xml:lang="fr">Outil d'archivage</summary>
<summary xml:lang="gl">Ferramenta para arquivar</summary>
+ <summary xml:lang="he">כלי לניהול ארכיונים</summary>
<summary xml:lang="hu">Fájltömörítő</summary>
<summary xml:lang="it">Strumento di archiviazione</summary>
<summary xml:lang="ko">압축 도구</summary>
@@ -107,6 +109,7 @@
<p xml:lang="fi">Ark on graafinen tiedostojen pakkaus-/purkuohjelma, joka tukee useita tiedostomuotoja kuten tar, gzip, bzip2, rar and zip sekä myös CD-ROM-levykuvia. Arkilla voi selata, purkaa, luoda ja muuttaa arkistoja.</p>
<p xml:lang="fr">Ark est un utilitaire graphique de compression/décompression de fichier prenant en charge de multiples formats, notamment tar, gzip, bzip2, rar et zip, ainsi que les images de CD-ROM. Ark peut être utilisé pour parcourir, extraire, créer et modifier des archives.</p>
<p xml:lang="gl">Ark é unha ferramenta gráfica de compresión e descompresión de ficheiros compatíbel con múltiplos formatos, como «tar», «gzip», «bzip2», «rar» e «zip», así como imaxes de CD-ROM. Ark pode usarse para navegar, extraer, crear e modificar arquivos.</p>
+ <p xml:lang="he">‏Ark הוא יישום גרפי לדחיסה וחילוץ ארכיונים עם תמיכה בפורמטים שונים, כולל tar, gzip, bzip2, rar ו־zip, כמו כן בקובצי תמונת דיסק. Ark משומש לדפדוף, חילוץ, יצירה ושינוי ארכיונים.</p>
<p xml:lang="hu">Az Ark egy grafikus fájltömörítő és kibontó segédprogram többféle formátum támogatásával, beleértve a tar, gzip, bzip2, rar és zip formátumokat, valamint a CD-ROM képfájlokat. Az Ark használható archívumok böngészéséhez, kibontásához, létrehozásához és módosításához.</p>
<p xml:lang="it">Ark è uno strumento grafico per la compressione/decompressione dei file che supporta molti formati, tra i quali tar, gzip, bzip2, rar e zip, così come le immagini dei CD-ROM. Ark può essere utilizzato per sfogliare, estrarre, creare e modificare archivi.</p>
<p xml:lang="ko">Ark는 그래픽 압축 파일 관리 도구이며, tar, gzip, bzip2, rar, zip, CD-ROM 이미지를 포함한 여러 파일 형식을 지원합니다. Ark를 사용하여 압축 파일을 열고, 생성하고, 수정하고, 압축을 풀 수 있습니다.</p>
@@ -146,6 +149,7 @@
<p xml:lang="fi">Ominaisuudet:</p>
<p xml:lang="fr">Fonctionnalités :</p>
<p xml:lang="gl">Funcionalidades:</p>
+ <p xml:lang="he">תכונות:</p>
<p xml:lang="hu">Szolgáltatások:</p>
<p xml:lang="it">Funzionalità:</p>
<p xml:lang="ko">기능:</p>
@@ -188,6 +192,7 @@
<li xml:lang="fi">Tukee useita tiedostomuotoja: gzip, bzip2, zip, rar, 7z ja monia muita</li>
<li xml:lang="fr">Plusieurs formats sont pris en charge : gzip, bzip2, zip, rar, 7z et d'autres</li>
<li xml:lang="gl">Compatíbel con moitos formatos: gzip, bzip2, zip, rar, 7z e máis.</li>
+ <li xml:lang="he">מספר פורמטים נתמכים: gzip, bzip2, zip, rar, 7z ועוד</li>
<li xml:lang="hu">Számos formátum támogatott: gzip, bzip2, zip, rar, 7z és továbbiak</li>
<li xml:lang="it">Diversi formati supportati: gzip, bzip2, zip, rar, 7z e altri</li>
<li xml:lang="ko">다양한 형식 지원: gzip, bzip2, zip, rar, 7z 등</li>
@@ -227,6 +232,7 @@
<li xml:lang="fi">Tiedoston sisällön esikatselu purkamatta tiedostoa</li>
<li xml:lang="fr">Affichage du contenu des fichiers sans les extraire</li>
<li xml:lang="gl">Obteña unha vista previa dos ficheiros comprimidos sen extraelos.</li>
+ <li xml:lang="he">הצג את תוכן הקובץ ללא חילוץ הקבצים</li>
<li xml:lang="hu">Fájltartalom előnézete a fájlok kibontása nélkül</li>
<li xml:lang="it">Anteprima dei file senza estrarre i file</li>
<li xml:lang="ko">압축 풀지 않고 파일 내용 미리 보기</li>
diff --git a/app/org.kde.ark.desktop.cmake b/app/org.kde.ark.desktop.cmake
index c68523b..e6bede5 100755
--- a/app/org.kde.ark.desktop.cmake
+++ b/app/org.kde.ark.desktop.cmake
@@ -154,7 +154,6 @@ Type=Application
Terminal=false
X-DBUS-StartupType=Multi
X-DBUS-ServiceName=org.kde.ark
-X-KDE-HasTempFileOption=true
Categories=Qt;KDE;Utility;Archiving;Compression;X-KDE-Utilities-File;
InitialPreference=3
Comment=Work with file archives
@@ -164,6 +163,7 @@ Comment[ca@valencia]=Treball amb arxius de fitxer
Comment[cs]=Práce s archivy souborů
Comment[da]=Arbejd med filarkiver
Comment[de]=Arbeiten mit Dateiarchiven
+Comment[el]=Εργασία με αρχειοθήκες
Comment[en_GB]=Work with file archives
Comment[es]=Trabajar con archivos comprimidos
Comment[et]=Arhiivifailide käitlemine
@@ -178,6 +178,7 @@ Comment[nl]=Mert bestandsarchieven werken
Comment[pl]=Pracuj z archiwami plików
Comment[pt]=Lidar com pacotes em ficheiros
Comment[pt_BR]=Manipulação de arquivos compactados
+Comment[ru]=Работа с архивами файлов
Comment[sk]=Práca s archívmi súborov
Comment[sl]=Delajte z datotečnimi arhivi
Comment[sr]=Рад са фајловима архива
@@ -189,3 +190,4 @@ Comment[tr]=Arşiv dosyaları ile çalış
Comment[uk]=Робота з файловими архівами
Comment[x-test]=xxWork with file archivesxx
Comment[zh_CN]=处理归档文件
+Comment[zh_TW]=檔案壓縮工具
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
index 2ea2753..f477472 100644
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -1,5 +1,6 @@
include(ECMAddTests)
+add_subdirectory(app)
add_subdirectory(testhelper)
add_subdirectory(kerfuffle)
add_subdirectory(plugins)
diff --git a/autotests/app/CMakeLists.txt b/autotests/app/CMakeLists.txt
new file mode 100644
index 0000000..10fb84a
--- /dev/null
+++ b/autotests/app/CMakeLists.txt
@@ -0,0 +1,9 @@
+include_directories(${CMAKE_SOURCE_DIR}/app)
+
+ecm_add_test(
+ batchextracttest.cpp
+ ${CMAKE_SOURCE_DIR}/app/batchextract.cpp
+ ${CMAKE_BINARY_DIR}/app/ark_debug.cpp
+ LINK_LIBRARIES Qt5::Test KF5::KIOFileWidgets kerfuffle
+ TEST_NAME batchextracttest
+ NAME_PREFIX app-)
diff --git a/autotests/app/batchextracttest.cpp b/autotests/app/batchextracttest.cpp
new file mode 100644
index 0000000..d49252e
--- /dev/null
+++ b/autotests/app/batchextracttest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 Elvis Angelaccio <elvis.angelaccio@kdemail.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "batchextract.h"
+
+#include <QDirIterator>
+#include <QTest>
+
+class BatchExtractTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testBatchExtraction_data();
+ void testBatchExtraction();
+};
+
+QTEST_GUILESS_MAIN(BatchExtractTest)
+
+void BatchExtractTest::testBatchExtraction_data()
+{
+ QTest::addColumn<QString>("archivePath");
+ QTest::addColumn<bool>("autoSubfolder");
+ QTest::addColumn<int>("expectedExtractedEntriesCount");
+
+ QString archivePath = QFINDTESTDATA("data/simple%archive.tar.gz");
+ QTest::newRow("extract the whole simple%archive.tar.gz (bug #365798)")
+ << archivePath
+ << true
+ << 4;
+}
+
+void BatchExtractTest::testBatchExtraction()
+{
+ auto batchJob = new BatchExtract(this);
+
+ QFETCH(QString, archivePath);
+ batchJob->addInput(QUrl::fromUserInput(archivePath));
+
+ QFETCH(bool, autoSubfolder);
+ batchJob->setAutoSubfolder(autoSubfolder);
+
+ QTemporaryDir destDir;
+ if (!destDir.isValid()) {
+ QSKIP("Could not create a temporary directory for extraction. Skipping test.", SkipSingle);
+ }
+
+ batchJob->setDestinationFolder(destDir.path());
+
+ QEventLoop eventLoop(this);
+ connect(batchJob, &KJob::result, &eventLoop, &QEventLoop::quit);
+ batchJob->start();
+ eventLoop.exec(); // krazy:exclude=crashy
+
+ QFETCH(int, expectedExtractedEntriesCount);
+ int extractedEntriesCount = 0;
+
+ QDirIterator dirIt(destDir.path(), QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (dirIt.hasNext()) {
+ extractedEntriesCount++;
+ dirIt.next();
+ }
+
+ if (autoSubfolder) {
+ // Also take into account the automatically created subfolder.
+ QCOMPARE(extractedEntriesCount, expectedExtractedEntriesCount + 1);
+ } else {
+ QCOMPARE(extractedEntriesCount, expectedExtractedEntriesCount);
+ }
+}
+
+#include "batchextracttest.moc"
diff --git a/autotests/app/data/simple%archive.tar.gz b/autotests/app/data/simple%archive.tar.gz
new file mode 100644
index 0000000..382144c
--- /dev/null
+++ b/autotests/app/data/simple%archive.tar.gz
Binary files differ
diff --git a/autotests/kerfuffle/addtoarchivetest.cpp b/autotests/kerfuffle/addtoarchivetest.cpp
index 410bb96..abaa0b4 100644
--- a/autotests/kerfuffle/addtoarchivetest.cpp
+++ b/autotests/kerfuffle/addtoarchivetest.cpp
@@ -25,8 +25,10 @@
#include "kerfuffle/addtoarchive.h"
#include "kerfuffle/archive_kerfuffle.h"
+#include "kerfuffle/pluginmanager.h"
#include <QEventLoop>
+#include <QMimeDatabase>
#include <QStandardPaths>
#include <QTest>
@@ -38,29 +40,53 @@ class AddToArchiveTest : public QObject
private Q_SLOTS:
+ void init();
void testCompressHere_data();
void testCompressHere();
void testCreateEncryptedArchive();
};
+void AddToArchiveTest::init()
+{
+ // The test needs an empty subfolder, but git doesn't support tracking of empty directories.
+ QDir(QFINDTESTDATA("data/testdirwithemptysubdir")).mkdir(QStringLiteral("emptydir"));
+}
+
void AddToArchiveTest::testCompressHere_data()
{
QTest::addColumn<QString>("expectedSuffix");
QTest::addColumn<QStringList>("inputFiles");
QTest::addColumn<QString>("expectedArchiveName");
QTest::addColumn<qulonglong>("expectedNumberOfFiles");
+ QTest::addColumn<qulonglong>("expectedNumberOfFolders");
- QTest::newRow("compress here (as TAR) - whole folder")
+ QTest::newRow("compress here (as TAR) - dir with files")
<< QStringLiteral("tar.gz")
<< QStringList {QFINDTESTDATA("data/testdir")}
<< QStringLiteral("testdir.tar.gz")
+ << 2ULL
+ << 1ULL;
+
+ QTest::newRow("compress here (as TAR) - dir with subdirs")
+ << QStringLiteral("tar.gz")
+ << QStringList {QFINDTESTDATA("data/testdirwithsubdirs")}
+ << QStringLiteral("testdirwithsubdirs.tar.gz")
+ << 4ULL
+ << 4ULL;
+
+ QTest::newRow("compress here (as TAR) - dir with empty subdir")
+ << QStringLiteral("tar.gz")
+ << QStringList {QFINDTESTDATA("data/testdirwithemptysubdir")}
+ << QStringLiteral("testdirwithemptysubdir.tar.gz")
+ << 2ULL
<< 2ULL;
QTest::newRow("compress here (as TAR) - single file")
<< QStringLiteral("tar.gz")
<< QStringList {QFINDTESTDATA("data/testfile.txt")}
<< QStringLiteral("testfile.tar.gz")
- << 1ULL;
+ << 1ULL
+ << 0ULL;
QTest::newRow("compress here (as TAR) - file + folder")
<< QStringLiteral("tar.gz")
@@ -69,26 +95,44 @@ void AddToArchiveTest::testCompressHere_data()
QFINDTESTDATA("data/testfile.txt")
}
<< QStringLiteral("data.tar.gz")
- << 3ULL;
+ << 3ULL
+ << 1ULL;
QTest::newRow("compress here (as TAR) - bug #362690")
<< QStringLiteral("tar.gz")
<< QStringList {QFINDTESTDATA("data/test-3.4.0")}
<< QStringLiteral("test-3.4.0.tar.gz")
+ << 1ULL
<< 1ULL;
- if (!QStandardPaths::findExecutable(QStringLiteral("zip")).isEmpty()) {
- QTest::newRow("compress here (as ZIP) - whole folder")
+ if (!PluginManager().preferredWritePluginsFor(QMimeDatabase().mimeTypeForName(QStringLiteral("application/zip"))).isEmpty()) {
+ QTest::newRow("compress here (as ZIP) - dir with files")
<< QStringLiteral("zip")
<< QStringList {QFINDTESTDATA("data/testdir")}
<< QStringLiteral("testdir.zip")
+ << 2ULL
+ << 1ULL;
+
+ QTest::newRow("compress here (as ZIP) - dir with subdirs")
+ << QStringLiteral("zip")
+ << QStringList {QFINDTESTDATA("data/testdirwithsubdirs")}
+ << QStringLiteral("testdirwithsubdirs.zip")
+ << 4ULL
+ << 4ULL;
+
+ QTest::newRow("compress here (as ZIP) - dir with empty subdir")
+ << QStringLiteral("zip")
+ << QStringList {QFINDTESTDATA("data/testdirwithemptysubdir")}
+ << QStringLiteral("testdirwithemptysubdir.zip")
+ << 2ULL
<< 2ULL;
QTest::newRow("compress here (as ZIP) - single file")
<< QStringLiteral("zip")
<< QStringList {QFINDTESTDATA("data/testfile.txt")}
<< QStringLiteral("testfile.zip")
- << 1ULL;
+ << 1ULL
+ << 0ULL;
QTest::newRow("compress here (as ZIP) - file + folder")
<< QStringLiteral("zip")
@@ -97,9 +141,59 @@ void AddToArchiveTest::testCompressHere_data()
QFINDTESTDATA("data/testfile.txt")
}
<< QStringLiteral("data.zip")
- << 3ULL;
+ << 3ULL
+ << 1ULL;
+
+ QTest::newRow("compress here (as TAR) - dir with special name (see #365798)")
+ << QStringLiteral("tar.gz")
+ << QStringList {QFINDTESTDATA("data/test%dir")}
+ << QStringLiteral("test%dir.tar.gz")
+ << 2ULL
+ << 1ULL;
} else {
- qDebug() << "zip executable not found in path. Skipping compress-here-(ZIP) tests.";
+ qDebug() << "7z/zip executable not found in path. Skipping compress-here-(ZIP) tests.";
+ }
+
+ if (!PluginManager().preferredWritePluginsFor(QMimeDatabase().mimeTypeForName(QStringLiteral("application/x-rar"))).isEmpty()) {
+ QTest::newRow("compress here (as RAR) - dir with files")
+ << QStringLiteral("rar")
+ << QStringList {QFINDTESTDATA("data/testdir")}
+ << QStringLiteral("testdir.rar")
+ << 2ULL
+ << 1ULL;
+
+ QTest::newRow("compress here (as RAR) - dir with subdirs")
+ << QStringLiteral("rar")
+ << QStringList {QFINDTESTDATA("data/testdirwithsubdirs")}
+ << QStringLiteral("testdirwithsubdirs.rar")
+ << 4ULL
+ << 4ULL;
+
+ QTest::newRow("compress here (as RAR) - dir with empty subdir")
+ << QStringLiteral("rar")
+ << QStringList {QFINDTESTDATA("data/testdirwithemptysubdir")}
+ << QStringLiteral("testdirwithemptysubdir.rar")
+ << 2ULL
+ << 2ULL;
+
+ QTest::newRow("compress here (as RAR) - single file")
+ << QStringLiteral("rar")
+ << QStringList {QFINDTESTDATA("data/testfile.txt")}
+ << QStringLiteral("testfile.rar")
+ << 1ULL
+ << 0ULL;
+
+ QTest::newRow("compress here (as RAR) - file + folder")
+ << QStringLiteral("rar")
+ << QStringList {
+ QFINDTESTDATA("data/testdir"),
+ QFINDTESTDATA("data/testfile.txt")
+ }
+ << QStringLiteral("data.rar")
+ << 3ULL
+ << 1ULL;
+ } else {
+ qDebug() << "rar executable not found in path. Skipping compress-here-(RAR) tests.";
}
}
@@ -133,6 +227,9 @@ void AddToArchiveTest::testCompressHere()
QFETCH(qulonglong, expectedNumberOfFiles);
QCOMPARE(archive->numberOfFiles(), expectedNumberOfFiles);
+ QFETCH(qulonglong, expectedNumberOfFolders);
+ QCOMPARE(archive->numberOfFolders(), expectedNumberOfFolders);
+
QVERIFY(QFile(archive->fileName()).remove());
}
diff --git a/autotests/kerfuffle/data/archive-multivolume.7z.001 b/autotests/kerfuffle/data/archive-multivolume.7z.001
new file mode 100644
index 0000000..a1c6bf1
--- /dev/null
+++ b/autotests/kerfuffle/data/archive-multivolume.7z.001
Binary files differ
diff --git a/autotests/kerfuffle/data/archive-multivolume.7z.002 b/autotests/kerfuffle/data/archive-multivolume.7z.002
new file mode 100644
index 0000000..8f1c57c
--- /dev/null
+++ b/autotests/kerfuffle/data/archive-multivolume.7z.002
Binary files differ
diff --git a/autotests/kerfuffle/data/archive-multivolume.7z.003 b/autotests/kerfuffle/data/archive-multivolume.7z.003
new file mode 100644
index 0000000..3ca4ff4
--- /dev/null
+++ b/autotests/kerfuffle/data/archive-multivolume.7z.003
Binary files differ
diff --git a/autotests/kerfuffle/data/archive-multivolume.part1.rar b/autotests/kerfuffle/data/archive-multivolume.part1.rar
new file mode 100644
index 0000000..e946798
--- /dev/null
+++ b/autotests/kerfuffle/data/archive-multivolume.part1.rar
Binary files differ
diff --git a/autotests/kerfuffle/data/archive-multivolume.part2.rar b/autotests/kerfuffle/data/archive-multivolume.part2.rar
new file mode 100644
index 0000000..f7fcd185
--- /dev/null
+++ b/autotests/kerfuffle/data/archive-multivolume.part2.rar
Binary files differ
diff --git a/autotests/kerfuffle/data/archive-multivolume.part3.rar b/autotests/kerfuffle/data/archive-multivolume.part3.rar
new file mode 100644
index 0000000..4021c2a
--- /dev/null
+++ b/autotests/kerfuffle/data/archive-multivolume.part3.rar
Binary files differ
diff --git a/autotests/kerfuffle/data/hello-1.0-x86_64.AppImage b/autotests/kerfuffle/data/hello-1.0-x86_64.AppImage
new file mode 100755
index 0000000..b71160a
--- /dev/null
+++ b/autotests/kerfuffle/data/hello-1.0-x86_64.AppImage
Binary files differ
diff --git a/autotests/kerfuffle/data/hello-appimage-runtime.c b/autotests/kerfuffle/data/hello-appimage-runtime.c
new file mode 100644
index 0000000..8a26989
--- /dev/null
+++ b/autotests/kerfuffle/data/hello-appimage-runtime.c
@@ -0,0 +1,376 @@
+/**************************************************************************
+
+Copyright (c) 2004-16 Simon Peter
+Copyright (c) 2007 Alexander Larsson
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+**************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+
+
+/* ======================================================== Start helper functions for icon extraction */
+/*
+Constructs the name of the thumbnail image for $HOME/.thumbnails for the executable that is itself
+See http://people.freedesktop.org/~vuntz/thumbnail-spec-cache/
+Partly borrowed from
+http://www.google.com/codesearch#n76pnUnMG18/trunk/blender/imbuf/intern/thumbs.c&q=.thumbnails/normal%20lang:c%20md5&type=cs
+*/
+
+#include "md5.h"
+#include "md5.c"
+#include <ctype.h>
+#include <time.h>
+
+#define FILE_MAX 240
+#define URI_MAX FILE_MAX*3 + 8
+
+/* --- begin of adapted code from glib ---
+ * The following code is adapted from function g_escape_uri_string from the gnome glib
+ * Source: http://svn.gnome.org/viewcvs/glib/trunk/glib/gconvert.c?view=markup
+ * released under the Gnu General Public License.
+ * NOTE THIS DOESN'T WORK PROPERLY FOR öäüß - FIXME
+ */
+
+typedef enum {
+ UNSAFE_ALL = 0x1, /* Escape all unsafe characters */
+ UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+' */
+ UNSAFE_PATH = 0x8, /* Allows '/', '&', '=', ':', '@', '+', '$' and ',' */
+ UNSAFE_HOST = 0x10, /* Allows '/' and ':' and '@' */
+ UNSAFE_SLASHES = 0x20 /* Allows all characters except for '/' and '%' */
+} UnsafeCharacterSet;
+
+static const unsigned char acceptable[96] = {
+ /* A table of the ASCII chars from space (32) to DEL (127) */
+ 0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
+ 0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
+ 0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
+};
+
+static const char hex[17] = "0123456789abcdef";
+
+void escape_uri_string (const char *string, char* escaped_string, int len,UnsafeCharacterSet mask)
+{
+#define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
+
+ const char *p;
+ char *q;
+ int c;
+ UnsafeCharacterSet use_mask;
+ use_mask = mask;
+
+ for (q = escaped_string, p = string; (*p != '\0') && len; p++) {
+ c = (unsigned char) *p;
+ len--;
+
+ if (!ACCEPTABLE (c)) {
+ *q++ = '%'; /* means hex coming */
+ *q++ = hex[c >> 4];
+ *q++ = hex[c & 15];
+ } else {
+ *q++ = *p;
+ }
+ }
+
+ *q = '\0';
+}
+
+void to_hex_char(char* hexbytes, const unsigned char* bytes, int len)
+{
+ const unsigned char *p;
+ char *q;
+
+ for (q = hexbytes, p = bytes; len; p++) {
+ const unsigned char c = (unsigned char) *p;
+ len--;
+ *q++ = hex[c >> 4];
+ *q++ = hex[c & 15];
+ }
+}
+
+/* --- end of adapted code from glib --- */
+
+static int uri_from_filename( const char *dir, char *newuri )
+{
+ char uri[URI_MAX];
+ sprintf (uri, "file://%s", dir);
+ char newstring[URI_MAX];
+ strncpy(newstring, uri, URI_MAX);
+ newstring[URI_MAX - 1] = 0;
+ unsigned int i = 0;
+ escape_uri_string(newstring, newuri, FILE_MAX*3+8, UNSAFE_PATH);
+ return 1;
+}
+
+
+static void thumbname_from_uri(const char* uri, char* thumb)
+{
+ char hexdigest[33];
+ unsigned char digest[16];
+ md5_buffer( uri, strlen(uri), digest);
+ hexdigest[0] = '\0';
+ to_hex_char(hexdigest, digest, 16);
+ hexdigest[32] = '\0';
+ sprintf(thumb, "%s.png", hexdigest);
+
+}
+
+/* ======================================================== End helper functions for icon extraction */
+
+extern int ext2_main(int argc, char *argv[], void (*mounted) (void));
+extern void ext2_quit(void);
+
+static pid_t fuse_pid;
+static int keepalive_pipe[2];
+
+static void *
+write_pipe_thread (void *arg)
+{
+ char c[32];
+ int res;
+// sprintf(stderr, "Called write_pipe_thread");
+ memset (c, 'x', sizeof (c));
+ while (1) {
+ /* Write until we block, on broken pipe, exit */
+ res = write (keepalive_pipe[1], c, sizeof (c));
+ if (res == -1) {
+ kill (fuse_pid, SIGHUP);
+ break;
+ }
+ }
+ return NULL;
+}
+
+void
+run_when_fuse_fs_mounted (void)
+{
+
+// sprintf(stderr, "Called run_when_fuse_fs_mounted");
+ pthread_t thread;
+ int res;
+
+ fuse_pid = getpid();
+ res = pthread_create(&thread, NULL, write_pipe_thread, keepalive_pipe);
+}
+
+char* getArg(int argc, char *argv[],char chr)
+ {
+ int i;
+ for (i=1; i<argc; ++i)
+ if ((argv[i][0]=='-') && (argv[i][1]==chr))
+ return &(argv[i][2]);
+ return NULL;
+ }
+
+
+int
+main (int argc, char *argv[])
+{
+ int dir_fd, res;
+ char mount_dir[] = "/tmp/.mount_XXXXXX"; /* create mountpoint */
+ char filename[100]; /* enough for mount_dir + "/AppRun" */
+ pid_t pid;
+ char **real_argv;
+ int i;
+
+ // We are using glib anyway for fuseiso, so we can use it here too to make our life easier
+ char *xdg_cache_home;
+ char thumbnails_medium_dir[FILE_MAX];
+ xdg_cache_home = (getenv("XDG_CACHE_HOME") == NULL
+ ? g_build_filename(g_get_home_dir(), ".cache", NULL)
+ : g_strdup(getenv("XDG_CACHE_HOME")));
+ sprintf(thumbnails_medium_dir, "%s/thumbnails/normal/", xdg_cache_home);
+ /* printf("%s\n", thumbnails_medium_dir); */
+
+ if (mkdtemp(mount_dir) == NULL) {
+ exit (1);
+ }
+
+ if (pipe (keepalive_pipe) == -1) {
+ perror ("pipe error: ");
+ exit (1);
+ }
+
+ pid = fork ();
+ if (pid == -1) {
+ perror ("fork error: ");
+ exit (1);
+ }
+
+ if (pid == 0) {
+ /* in child */
+
+ char *child_argv[5];
+
+ /* close read pipe */
+ close (keepalive_pipe[0]);
+
+
+ char *dir = realpath( "/proc/self/exe", NULL );
+
+ child_argv[0] = dir;
+ child_argv[1] = mount_dir;
+ child_argv[2] = "-o";
+ child_argv[3] = "ro";
+ child_argv[4] = NULL;
+
+ ext2_main (4, child_argv, NULL);
+ } else {
+ /* in parent, child is $pid */
+ int c;
+
+ /* close write pipe */
+ close (keepalive_pipe[1]);
+
+ /* Pause until mounted */
+ read (keepalive_pipe[0], &c, 1);
+
+
+ dir_fd = open (mount_dir, O_RDONLY);
+ if (dir_fd == -1) {
+ // perror ("open dir error: ");
+ printf("Could not mount AppImage\n");
+ printf("Please see https://github.com/probonopd/AppImageKit/wiki/FUSE\n");
+ exit (1);
+ }
+
+ res = dup2 (dir_fd, 1023);
+ if (res == -1) {
+ perror ("dup2 error: ");
+ exit (1);
+ }
+ close (dir_fd);
+
+ strcpy (filename, mount_dir);
+ strcat (filename, "/AppRun");
+
+ real_argv = malloc (sizeof (char *) * (argc + 1));
+ for (i = 0; i < argc; i++) {
+ real_argv[i] = argv[i];
+ }
+ real_argv[i] = NULL;
+
+
+
+ /* ======================================================== Start icon extraction */
+
+ int length;
+ char fullpath[FILE_MAX];
+ length = readlink("/proc/self/exe", fullpath, sizeof(fullpath));
+ fullpath[length] = '\0';
+ /* printf("%s\n", fullpath); */
+ char theuri[URI_MAX];
+ uri_from_filename(fullpath, theuri);
+ /* printf("%s\n", theuri); */
+ char path_to_thumbnail[URI_MAX];
+ char thumbname[URI_MAX];
+ thumbname_from_uri(theuri, thumbname);
+ sprintf(path_to_thumbnail, "%s%s", thumbnails_medium_dir, thumbname);
+
+ FILE *from, *to;
+ char ch;
+
+ char diricon[FILE_MAX];
+ sprintf (diricon, "%s/.DirIcon", mount_dir);
+
+ /* open source file */
+ if((from = fopen(diricon, "rb"))==NULL) {
+ printf("Cannot open %s\n", diricon);
+ exit(1);
+ }
+
+ /* open destination file */
+ char mkcmd[FILE_MAX];
+ char iconsdir[FILE_MAX];
+
+ sprintf(mkcmd, "mkdir -p '%s'", thumbnails_medium_dir);
+ system(mkcmd);
+ if((to = fopen(path_to_thumbnail, "wb"))==NULL) {
+ printf("Cannot open %s for writing\n", path_to_thumbnail);
+
+ } else {
+
+ /* copy the file */
+ while(!feof(from)) {
+ ch = fgetc(from);
+ if(ferror(from)) {
+ printf("Error reading source file\n");
+ exit(1);
+ }
+ if(!feof(from)) fputc(ch, to);
+ if(ferror(to)) {
+ printf("Error writing destination file\n");
+ exit(1);
+ }
+ }
+
+ if(fclose(from)==EOF) {
+ printf("Error closing source file\n");
+ exit(1);
+ }
+
+ if(fclose(to)==EOF) {
+ printf("Error closing destination file\n");
+ exit(1);
+ }
+ }
+
+ /* If called with --icon, then do not run the main app, just print print a message and exit after extracting the icon */
+ char * arg;
+ arg=getArg(argc,argv,'-');
+ if (arg && strcmp(arg,"icon")==0) {
+ printf("Written %s\n", path_to_thumbnail);
+ exit(0);
+ }
+
+ /* ======================================================== End icon extraction */
+
+ /* Setting some environment variables that the app "inside" might use */
+ setenv( "APPIMAGE", fullpath, 1 );
+ setenv( "APPDIR", mount_dir, 1 );
+
+ /* Original working directory */
+ char cwd[1024];
+ if (getcwd(cwd, sizeof(cwd)) != NULL) {
+ setenv( "OWD", cwd, 1 );
+ }
+
+ execv (filename, real_argv);
+ /* Error if we continue here */
+ perror ("execv error: ");
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/autotests/kerfuffle/data/test%dir/testfile1.txt b/autotests/kerfuffle/data/test%dir/testfile1.txt
new file mode 100644
index 0000000..b72e979
--- /dev/null
+++ b/autotests/kerfuffle/data/test%dir/testfile1.txt
@@ -0,0 +1 @@
+A simple text file.
diff --git a/autotests/kerfuffle/data/test%dir/testfile2.txt b/autotests/kerfuffle/data/test%dir/testfile2.txt
new file mode 100644
index 0000000..b72e979
--- /dev/null
+++ b/autotests/kerfuffle/data/test%dir/testfile2.txt
@@ -0,0 +1 @@
+A simple text file.
diff --git a/autotests/kerfuffle/data/testdirwithemptysubdir/testfile1.txt b/autotests/kerfuffle/data/testdirwithemptysubdir/testfile1.txt
new file mode 100644
index 0000000..1b146ae
--- /dev/null
+++ b/autotests/kerfuffle/data/testdirwithemptysubdir/testfile1.txt
@@ -0,0 +1 @@
+testfile1.txt
diff --git a/autotests/kerfuffle/data/testdirwithemptysubdir/testfile2.txt b/autotests/kerfuffle/data/testdirwithemptysubdir/testfile2.txt
new file mode 100644
index 0000000..f15564b
--- /dev/null
+++ b/autotests/kerfuffle/data/testdirwithemptysubdir/testfile2.txt
@@ -0,0 +1 @@
+testfile2.txt
diff --git a/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/subdir3/testfile4.txt b/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/subdir3/testfile4.txt
new file mode 100644
index 0000000..54a0dda
--- /dev/null
+++ b/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/subdir3/testfile4.txt
@@ -0,0 +1 @@
+testfile4.txt
diff --git a/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/testfile3.txt b/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/testfile3.txt
new file mode 100644
index 0000000..a864f6c
--- /dev/null
+++ b/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/subdir2/testfile3.txt
@@ -0,0 +1 @@
+testfile3.txt
diff --git a/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/testfile2.txt b/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/testfile2.txt
new file mode 100644
index 0000000..f15564b
--- /dev/null
+++ b/autotests/kerfuffle/data/testdirwithsubdirs/subdir1/testfile2.txt
@@ -0,0 +1 @@
+testfile2.txt
diff --git a/autotests/kerfuffle/data/testdirwithsubdirs/testfile1.txt b/autotests/kerfuffle/data/testdirwithsubdirs/testfile1.txt
new file mode 100644
index 0000000..1b146ae
--- /dev/null
+++ b/autotests/kerfuffle/data/testdirwithsubdirs/testfile1.txt
@@ -0,0 +1 @@
+testfile1.txt
diff --git a/autotests/kerfuffle/extracttest.cpp b/autotests/kerfuffle/extracttest.cpp
index ca18801..02aac41 100644
--- a/autotests/kerfuffle/extracttest.cpp
+++ b/autotests/kerfuffle/extracttest.cpp
@@ -53,6 +53,8 @@ void ExtractTest::testProperties_data()
QTest::addColumn<bool>("isReadOnly");
QTest::addColumn<bool>("canFallbackOnReadOnly");
QTest::addColumn<bool>("isSingleFolder");
+ QTest::addColumn<bool>("isMultiVolume");
+ QTest::addColumn<int>("numberOfVolumes");
QTest::addColumn<Archive::EncryptionType>("expectedEncryptionType");
QTest::addColumn<QString>("expectedSubfolderName");
@@ -60,81 +62,81 @@ void ExtractTest::testProperties_data()
QTest::newRow("non-existent tar archive")
<< QStringLiteral("/tmp/foo.tar.gz")
<< QStringLiteral("foo")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QString();
// Test non-archive file
QTest::newRow("not an archive")
<< QStringLiteral("/tmp/foo.pdf")
<< QString()
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QString();
// Test dummy source code tarball.
QTest::newRow("dummy source code tarball")
<< QFINDTESTDATA("data/code-x.y.z.tar.gz")
<< QStringLiteral("code-x.y.z")
- << false << false << true << Archive::Unencrypted
+ << false << false << true << false << 0 << Archive::Unencrypted
<< QStringLiteral("awesome_project");
QTest::newRow("simple compressed tar archive")
<< QFINDTESTDATA("data/simplearchive.tar.gz")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
QTest::newRow("encrypted zip, single entry")
<< QFINDTESTDATA("data/archivetest_encrypted.zip")
<< QStringLiteral("archivetest_encrypted")
- << false << true << false << Archive::Encrypted
+ << false << true << false << false << 0 << Archive::Encrypted
<< QStringLiteral("archivetest_encrypted");
QTest::newRow("simple zip, one unencrypted entry")
<< QFINDTESTDATA("data/archivetest_unencrypted.zip")
<< QStringLiteral("archivetest_unencrypted")
- << false << true << false << Archive::Unencrypted
+ << false << true << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("archivetest_unencrypted");
QTest::newRow("rpm archive, no single folder")
<< QFINDTESTDATA("data/wget.rpm")
<< QStringLiteral("wget")
- << true << false << false << Archive::Unencrypted
+ << true << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("wget");
QTest::newRow("bzip2-compressed tarball")
<< QFINDTESTDATA("data/simplearchive.tar.bz2")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
QTest::newRow("xz-compressed tarball")
<< QFINDTESTDATA("data/simplearchive.tar.xz")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
QTest::newRow("lzma-compressed tarball")
<< QFINDTESTDATA("data/simplearchive.tar.lzma")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
QTest::newRow("compress (.Z) tarball")
<< QFINDTESTDATA("data/simplearchive.tar.Z")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
QTest::newRow("lzipped tarball")
<< QFINDTESTDATA("data/simplearchive.tar.lz")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
QTest::newRow("lzop-compressed tarball")
<< QFINDTESTDATA("data/simplearchive.tar.lzo")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
// Only run test for lrzipped tar if lrzip executable is found in path.
@@ -142,7 +144,7 @@ void ExtractTest::testProperties_data()
QTest::newRow("lrzipped tarball")
<< QFINDTESTDATA("data/simplearchive.tar.lrz")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
} else {
qDebug() << "lrzip executable not found in path. Skipping lrzip test.";
@@ -153,7 +155,7 @@ void ExtractTest::testProperties_data()
QTest::newRow("lz4-compressed tarball")
<< QFINDTESTDATA("data/simplearchive.tar.lz4")
<< QStringLiteral("simplearchive")
- << false << false << false << Archive::Unencrypted
+ << false << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
} else {
qDebug() << "lz4 executable not found in path. Skipping lz4 test.";
@@ -162,14 +164,32 @@ void ExtractTest::testProperties_data()
QTest::newRow("xar archive")
<< QFINDTESTDATA("data/simplearchive.xar")
<< QStringLiteral("simplearchive")
- << true << false << false << Archive::Unencrypted
+ << true << false << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("simplearchive");
QTest::newRow("mimetype child of application/zip")
<< QFINDTESTDATA("data/test.odt")
<< QStringLiteral("test")
- << false << true << false << Archive::Unencrypted
+ << false << true << false << false << 0 << Archive::Unencrypted
<< QStringLiteral("test");
+
+ QTest::newRow("AppImage")
+ << QFINDTESTDATA("data/hello-1.0-x86_64.AppImage")
+ << QStringLiteral("hello-1.0-x86_64")
+ << true << false << false << false << 0 << Archive::Unencrypted
+ << QStringLiteral("hello-1.0-x86_64");
+
+ QTest::newRow("7z multivolume")
+ << QFINDTESTDATA("data/archive-multivolume.7z.001")
+ << QStringLiteral("archive-multivolume")
+ << true << false << false << true << 3 << Archive::Unencrypted
+ << QStringLiteral("archive-multivolume");
+
+ QTest::newRow("rar multivolume")
+ << QFINDTESTDATA("data/archive-multivolume.part1.rar")
+ << QStringLiteral("archive-multivolume")
+ << true << false << false << true << 3 << Archive::Unencrypted
+ << QStringLiteral("archive-multivolume");
}
void ExtractTest::testProperties()
@@ -200,6 +220,12 @@ void ExtractTest::testProperties()
QFETCH(bool, isSingleFolder);
QCOMPARE(archive->isSingleFolderArchive(), isSingleFolder);
+ QFETCH(bool, isMultiVolume);
+ QCOMPARE(archive->isMultiVolume(), isMultiVolume);
+
+ QFETCH(int, numberOfVolumes);
+ QCOMPARE(archive->numberOfVolumes(), numberOfVolumes);
+
QFETCH(Archive::EncryptionType, expectedEncryptionType);
QCOMPARE(archive->encryptionType(), expectedEncryptionType);
@@ -509,6 +535,27 @@ void ExtractTest::testExtraction_data()
<< QList<Archive::Entry*>()
<< optionsPreservePaths
<< 6;
+
+ archivePath = QFINDTESTDATA("data/hello-1.0-x86_64.AppImage");
+ QTest::newRow("extract all entries from an AppImage with path")
+ << archivePath
+ << QList<Archive::Entry*>()
+ << optionsPreservePaths
+ << 7;
+
+ archivePath = QFINDTESTDATA("data/archive-multivolume.7z.001");
+ QTest::newRow("extract all entries from a multivolume 7z archive with path")
+ << archivePath
+ << QList<Archive::Entry*>()
+ << optionsPreservePaths
+ << 3;
+
+ archivePath = QFINDTESTDATA("data/archive-multivolume.part1.rar");
+ QTest::newRow("extract all entries from a multivolume rar archive with path")
+ << archivePath
+ << QList<Archive::Entry*>()
+ << optionsPreservePaths
+ << 3;
}
void ExtractTest::testExtraction()
@@ -538,7 +585,7 @@ void ExtractTest::testExtraction()
QFETCH(int, expectedExtractedEntriesCount);
int extractedEntriesCount = 0;
- QDirIterator dirIt(destDir.path(), QDir::AllEntries | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ QDirIterator dirIt(destDir.path(), QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (dirIt.hasNext()) {
extractedEntriesCount++;
dirIt.next();
diff --git a/autotests/kerfuffle/jobstest.cpp b/autotests/kerfuffle/jobstest.cpp
index 6392d8e..073fe3f 100644
--- a/autotests/kerfuffle/jobstest.cpp
+++ b/autotests/kerfuffle/jobstest.cpp
@@ -255,6 +255,8 @@ void JobsTest::testTempExtractJob()
JSONArchiveInterface *iface = createArchiveInterface(QFINDTESTDATA("data/archive-malicious.json"));
PreviewJob *job = new PreviewJob(new Archive::Entry(this, QStringLiteral("anotherDir/../../file.txt")), false, iface);
+ const QString tempDirPath = job->tempDir()->path();
+ QVERIFY(QFileInfo::exists(tempDirPath));
QVERIFY(job->validatedFilePath().endsWith(QLatin1String("anotherDir/file.txt")));
QVERIFY(job->extractionOptions()[QStringLiteral("PreservePaths")].toBool());
@@ -264,6 +266,9 @@ void JobsTest::testTempExtractJob()
QVERIFY(job->validatedFilePath().endsWith(QLatin1String("anotherDir/file.txt")));
QVERIFY(job->extractionOptions()[QStringLiteral("PreservePaths")].toBool());
+ delete job->tempDir();
+ QVERIFY(!QFileInfo::exists(tempDirPath));
+
delete job;
}
diff --git a/autotests/kerfuffle/mimetypetest.cpp b/autotests/kerfuffle/mimetypetest.cpp
index 7cb0903..98f035b 100644
--- a/autotests/kerfuffle/mimetypetest.cpp
+++ b/autotests/kerfuffle/mimetypetest.cpp
@@ -60,6 +60,7 @@ void MimeTypeTest::testMimeTypeDetection_data()
const QString isoMimeType = QStringLiteral("application/x-cd-image");
const QString debMimeType = QMimeDatabase().mimeTypeForFile(QStringLiteral("dummy.deb"), QMimeDatabase::MatchExtension).name();
const QString xarMimeType = QStringLiteral("application/x-xar");
+ const QString appImageMimeType = QStringLiteral("application/x-iso9660-appimage");
QTest::newRow("empty name") << QString() << QStringLiteral("application/octet-stream");
QTest::newRow("tar.gz") << QFINDTESTDATA("data/simplearchive.tar.gz") << compressedGzipTarMime;
@@ -73,6 +74,7 @@ void MimeTypeTest::testMimeTypeDetection_data()
QTest::newRow("tar.lz4") << QFINDTESTDATA("data/simplearchive.tar.lz4") << compressedLz4TarMime;
QTest::newRow("deb") << QFINDTESTDATA("data/smallarchive.deb") << debMimeType;
QTest::newRow("xar") << QFINDTESTDATA("data/simplearchive.xar") << xarMimeType;
+ QTest::newRow("AppImage") << QFINDTESTDATA("data/hello-1.0-x86_64.AppImage") << appImageMimeType;
QTest::newRow("zip with wrong extension") << QFINDTESTDATA("data/zip_with_wrong_extension.rar") << QStringLiteral("application/zip");
QTest::newRow("tar with special char in the extension") << QStringLiteral("foo.tar~1.gz") << compressedGzipTarMime;
diff --git a/autotests/plugins/cli7zplugin/cli7ztest.cpp b/autotests/plugins/cli7zplugin/cli7ztest.cpp
index 3cb3e4f..655980f 100644
--- a/autotests/plugins/cli7zplugin/cli7ztest.cpp
+++ b/autotests/plugins/cli7zplugin/cli7ztest.cpp
@@ -99,6 +99,9 @@ void Cli7zTest::testList_data()
{
QTest::addColumn<QString>("outputTextFile");
QTest::addColumn<int>("expectedEntriesCount");
+ QTest::addColumn<bool>("isMultiVolume");
+ // Is zero for non-multi-volume archives:
+ QTest::addColumn<int>("numberOfVolumes");
// Index of some entry to be tested.
QTest::addColumn<int>("someEntryIndex");
// Entry metadata.
@@ -108,34 +111,48 @@ void Cli7zTest::testList_data()
QTest::addColumn<qulonglong>("expectedSize");
QTest::addColumn<QString>("expectedTimestamp");
+ // p7zip version 16.02 tests
+
+ QTest::newRow("normal-file-1602")
+ << QFINDTESTDATA("data/archive-with-symlink-1602.txt") << 10 << false << 0
+ << 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T20:41:48");
+
+ QTest::newRow("encrypted-1602")
+ << QFINDTESTDATA("data/archive-encrypted-1602.txt") << 4 << false << 0
+ << 1 << QStringLiteral("file2.txt") << false << true << (qulonglong) 14 << QStringLiteral("2016-03-02T22:37:55");
+
+ QTest::newRow("multi-volume-1602")
+ << QFINDTESTDATA("data/archive-multivol-1602.txt") << 2 << true << 5
+ << 1 << QStringLiteral("largefile2") << false << false << (qulonglong) 2097152 << QStringLiteral("2016-07-17T11:26:19");
+
// p7zip version 15.14 tests
QTest::newRow("normal-file-1514")
- << QFINDTESTDATA("data/archive-with-symlink-1514.txt") << 10
+ << QFINDTESTDATA("data/archive-with-symlink-1514.txt") << 10 << false << 0
<< 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48");
QTest::newRow("encrypted-1514")
- << QFINDTESTDATA("data/archive-encrypted-1514.txt") << 9
+ << QFINDTESTDATA("data/archive-encrypted-1514.txt") << 9 << false << 0
<< 3 << QStringLiteral("testarchive/dir1/file1.txt") << false << true << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48");
// p7zip version 15.09 tests
QTest::newRow("normal-file-1509")
- << QFINDTESTDATA("data/archive-with-symlink-1509.txt") << 10
+ << QFINDTESTDATA("data/archive-with-symlink-1509.txt") << 10 << false << 0
<< 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48");
QTest::newRow("encrypted-1509")
- << QFINDTESTDATA("data/archive-encrypted-1509.txt") << 9
+ << QFINDTESTDATA("data/archive-encrypted-1509.txt") << 9 << false << 0
<< 3 << QStringLiteral("testarchive/dir1/file1.txt") << false << true << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48");
// p7zip version 9.38.1 tests
QTest::newRow("normal-file-9381")
- << QFINDTESTDATA("data/archive-with-symlink-9381.txt") << 10
+ << QFINDTESTDATA("data/archive-with-symlink-9381.txt") << 10 << false << 0
<< 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48");
QTest::newRow("encrypted-9381")
- << QFINDTESTDATA("data/archive-encrypted-9381.txt") << 9
+ << QFINDTESTDATA("data/archive-encrypted-9381.txt") << 9 << false << 0
<< 3 << QStringLiteral("testarchive/dir1/file1.txt") << false << true << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48");
}
@@ -159,6 +176,12 @@ void Cli7zTest::testList()
QCOMPARE(signalSpy.count(), expectedEntriesCount);
+ QFETCH(bool, isMultiVolume);
+ QCOMPARE(plugin->isMultiVolume(), isMultiVolume);
+
+ QFETCH(int, numberOfVolumes);
+ QCOMPARE(plugin->numberOfVolumes(), numberOfVolumes);
+
QFETCH(int, someEntryIndex);
QVERIFY(someEntryIndex < signalSpy.count());
Archive::Entry *entry = signalSpy.at(someEntryIndex).at(0).value<Archive::Entry*>();
@@ -233,11 +256,12 @@ void Cli7zTest::testAddArgs_data()
QTest::addColumn<QString>("password");
QTest::addColumn<bool>("encryptHeader");
QTest::addColumn<int>("compressionLevel");
+ QTest::addColumn<ulong>("volumeSize");
QTest::addColumn<QStringList>("expectedArgs");
QTest::newRow("unencrypted")
<< QStringLiteral("/tmp/foo.7z")
- << QString() << false << 5
+ << QString() << false << 5 << 0UL
<< QStringList {
QStringLiteral("a"),
QStringLiteral("/tmp/foo.7z"),
@@ -246,7 +270,7 @@ void Cli7zTest::testAddArgs_data()
QTest::newRow("encrypted")
<< QStringLiteral("/tmp/foo.7z")
- << QStringLiteral("1234") << false << 5
+ << QStringLiteral("1234") << false << 5 << 0UL
<< QStringList {
QStringLiteral("a"),
QStringLiteral("/tmp/foo.7z"),
@@ -256,7 +280,7 @@ void Cli7zTest::testAddArgs_data()
QTest::newRow("header-encrypted")
<< QStringLiteral("/tmp/foo.7z")
- << QStringLiteral("1234") << true << 5
+ << QStringLiteral("1234") << true << 5 << 0UL
<< QStringList {
QStringLiteral("a"),
QStringLiteral("/tmp/foo.7z"),
@@ -264,6 +288,16 @@ void Cli7zTest::testAddArgs_data()
QStringLiteral("-mhe=on"),
QStringLiteral("-mx=5")
};
+
+ QTest::newRow("multi-volume")
+ << QStringLiteral("/tmp/foo.7z")
+ << QString() << false << 5 << 2500UL
+ << QStringList {
+ QStringLiteral("a"),
+ QStringLiteral("/tmp/foo.7z"),
+ QStringLiteral("-mx=5"),
+ QStringLiteral("-v2500k")
+ };
}
void Cli7zTest::testAddArgs()
@@ -276,13 +310,15 @@ void Cli7zTest::testAddArgs()
QStringLiteral("$Archive"),
QStringLiteral("$PasswordSwitch"),
QStringLiteral("$CompressionLevelSwitch"),
+ QStringLiteral("$MultiVolumeSwitch"),
QStringLiteral("$Files") };
QFETCH(QString, password);
QFETCH(bool, encryptHeader);
QFETCH(int, compressionLevel);
+ QFETCH(ulong, volumeSize);
- QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel);
+ QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel, volumeSize);
QFETCH(QStringList, expectedArgs);
QCOMPARE(replacedArgs, expectedArgs);
diff --git a/autotests/plugins/cli7zplugin/data/archive-encrypted-1602.txt b/autotests/plugins/cli7zplugin/data/archive-encrypted-1602.txt
new file mode 100644
index 0000000..b83f84a
--- /dev/null
+++ b/autotests/plugins/cli7zplugin/data/archive-encrypted-1602.txt
@@ -0,0 +1,59 @@
+
+7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
+p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64)
+
+Scanning the drive for archives:
+1 file, 276 bytes (1 KiB)
+
+Listing archive: testpassfiles.7z
+
+--
+Path = testpassfiles.7z
+Type = 7z
+Physical Size = 276
+Headers Size = 212
+Method = LZMA2:12 7zAES
+Solid = +
+Blocks = 1
+
+----------
+Path = file1.txt
+Size = 32
+Packed Size = 64
+Modified = 2016-03-02 22:37:55
+Attributes = A_ -rw-rw-r--
+CRC = 034EE5C7
+Encrypted = +
+Method = LZMA2:12 7zAES:19
+Block = 0
+
+Path = file2.txt
+Size = 14
+Packed Size =
+Modified = 2016-03-02 22:37:55
+Attributes = A_ -rw-rw-r--
+CRC = A5BA4A7B
+Encrypted = +
+Method = LZMA2:12 7zAES:19
+Block = 0
+
+Path = file3.txt
+Size = 32
+Packed Size =
+Modified = 2016-03-02 22:37:54
+Attributes = A_ -rw-rw-r--
+CRC = 99D12E31
+Encrypted = +
+Method = LZMA2:12 7zAES:19
+Block = 0
+
+Path = file4.txt
+Size = 32
+Packed Size =
+Modified = 2016-03-02 22:37:50
+Attributes = A_ -rw-rw-r--
+CRC = A04F9191
+Encrypted = +
+Method = LZMA2:12 7zAES:19
+Block = 0
+
diff --git a/autotests/plugins/cli7zplugin/data/archive-multivol-1602.txt b/autotests/plugins/cli7zplugin/data/archive-multivol-1602.txt
new file mode 100644
index 0000000..4cba3f2
--- /dev/null
+++ b/autotests/plugins/cli7zplugin/data/archive-multivol-1602.txt
@@ -0,0 +1,48 @@
+
+7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
+p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64)
+
+Scanning the drive for archives:
+1 file, 1048576 bytes (1024 KiB)
+
+Listing archive: testmultivol.7z.001
+
+--
+Path = testmultivol.7z.001
+Type = Split
+Physical Size = 1048576
+Volumes = 5
+Total Physical Size = 4194711
+----
+Path = testmultivol.7z
+Size = 4194711
+--
+Path = testmultivol.7z
+Type = 7z
+Physical Size = 4194711
+Headers Size = 181
+Method = LZMA2:22
+Solid = +
+Blocks = 1
+
+----------
+Path = largefile1
+Size = 2097152
+Packed Size = 4194530
+Modified = 2016-07-17 11:25:56
+Attributes = A_ -rw-rw-r--
+CRC = 147E8FFD
+Encrypted = -
+Method = LZMA2:22
+Block = 0
+
+Path = largefile2
+Size = 2097152
+Packed Size =
+Modified = 2016-07-17 11:26:19
+Attributes = A_ -rw-rw-r--
+CRC = 934DAE71
+Encrypted = -
+Method = LZMA2:22
+Block = 0
+
diff --git a/autotests/plugins/cli7zplugin/data/archive-with-symlink-1602.txt b/autotests/plugins/cli7zplugin/data/archive-with-symlink-1602.txt
new file mode 100644
index 0000000..5e2aee5
--- /dev/null
+++ b/autotests/plugins/cli7zplugin/data/archive-with-symlink-1602.txt
@@ -0,0 +1,119 @@
+
+7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
+p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64)
+
+Scanning the drive for archives:
+1 file, 371 bytes (1 KiB)
+
+Listing archive: testlink.7z
+
+--
+Path = testlink.7z
+Type = 7z
+Physical Size = 371
+Headers Size = 310
+Method = LZMA2:12
+Solid = +
+Blocks = 1
+
+----------
+Path = testarchive
+Size = 0
+Packed Size = 0
+Modified = 2016-03-23 08:46:21
+Attributes = D_ drwxrwxr-x
+CRC =
+Encrypted = -
+Method =
+Block =
+
+Path = testarchive/dir1
+Size = 0
+Packed Size = 0
+Modified = 2015-05-14 01:45:24
+Attributes = D_ drwxrwxr-x
+CRC =
+Encrypted = -
+Method =
+Block =
+
+Path = testarchive/dir2
+Size = 0
+Packed Size = 0
+Modified = 2015-05-14 01:43:41
+Attributes = D_ drwxrwxr-x
+CRC =
+Encrypted = -
+Method =
+Block =
+
+Path = testarchive/dir1/file1.txt
+Size = 32
+Packed Size = 61
+Modified = 2015-05-17 20:41:48
+Attributes = A_ -rw-rw-r--
+CRC = 034EE5C7
+Encrypted = -
+Method = LZMA2:12
+Block = 0
+
+Path = testarchive/dir2/file2.txt
+Size = 32
+Packed Size =
+Modified = 2015-05-17 20:41:48
+Attributes = A_ -rw-rw-r--
+CRC = D49ECBCA
+Encrypted = -
+Method = LZMA2:12
+Block = 0
+
+Path = testarchive/file1.txt
+Size = 43
+Packed Size =
+Modified = 2015-09-13 14:05:45
+Attributes = A_ -rw-rw-r--
+CRC = 94E02716
+Encrypted = -
+Method = LZMA2:12
+Block = 0
+
+Path = testarchive/file2.txt
+Size = 32
+Packed Size =
+Modified = 2015-05-18 14:57:37
+Attributes = A_ -rw-rw-r--
+CRC = D49ECBCA
+Encrypted = -
+Method = LZMA2:12
+Block = 0
+
+Path = testarchive/file3.txt
+Size = 32
+Packed Size =
+Modified = 2015-05-13 17:26:42
+Attributes = A_ -rw-rw-r--
+CRC = 99D12E31
+Encrypted = -
+Method = LZMA2:12
+Block = 0
+
+Path = testarchive/file4.txt
+Size = 32
+Packed Size =
+Modified = 2015-07-26 18:53:21
+Attributes = A_ -rw-rw-r--
+CRC = A04F9191
+Encrypted = -
+Method = LZMA2:12
+Block = 0
+
+Path = testarchive/linktofile1.txt
+Size = 9
+Packed Size =
+Modified = 2016-03-23 08:46:21
+Attributes = A_ lrwxrwxrwx
+CRC = 2D212004
+Encrypted = -
+Method = LZMA2:12
+Block = 0
+
diff --git a/autotests/plugins/clirarplugin/clirartest.cpp b/autotests/plugins/clirarplugin/clirartest.cpp
index 4640e8a..5775b2b 100644
--- a/autotests/plugins/clirarplugin/clirartest.cpp
+++ b/autotests/plugins/clirarplugin/clirartest.cpp
@@ -100,6 +100,9 @@ void CliRarTest::testList_data()
{
QTest::addColumn<QString>("outputTextFile");
QTest::addColumn<int>("expectedEntriesCount");
+ QTest::addColumn<bool>("isMultiVolume");
+ // Is zero for non-multi-volume archives:
+ QTest::addColumn<int>("numberOfVolumes");
// Index of some entry to be tested.
QTest::addColumn<int>("someEntryIndex");
// Entry metadata.
@@ -114,47 +117,57 @@ void CliRarTest::testList_data()
// Unrar 5 tests
QTest::newRow("normal-file-unrar5")
- << QFINDTESTDATA("data/archive-with-symlink-unrar5.txt") << 8
+ << QFINDTESTDATA("data/archive-with-symlink-unrar5.txt") << 8 << false << 0
<< 2 << QStringLiteral("rartest/file2.txt") << false << false << QString() << (qulonglong) 14 << (qulonglong) 23 << QStringLiteral("2016-03-21T08:57:36");
QTest::newRow("symlink-unrar5")
- << QFINDTESTDATA("data/archive-with-symlink-unrar5.txt") << 8
+ << QFINDTESTDATA("data/archive-with-symlink-unrar5.txt") << 8 << false << 0
<< 3 << QStringLiteral("rartest/linktofile1.txt") << false << false << QStringLiteral("file1.txt") << (qulonglong) 9 << (qulonglong) 9 << QStringLiteral("2016-03-21T08:58:16");
QTest::newRow("encrypted-unrar5")
- << QFINDTESTDATA("data/archive-encrypted-unrar5.txt") << 7
+ << QFINDTESTDATA("data/archive-encrypted-unrar5.txt") << 7 << false << 0
<< 2 << QStringLiteral("rartest/file2.txt") << false << true << QString() << (qulonglong) 14 << (qulonglong) 32 << QStringLiteral("2016-03-21T17:03:36");
QTest::newRow("recovery-record-unrar5")
- << QFINDTESTDATA("data/archive-recovery-record-unrar5.txt") << 3
+ << QFINDTESTDATA("data/archive-recovery-record-unrar5.txt") << 3 << false << 0
<< 0 << QStringLiteral("file1.txt") << false << false << QString() << (qulonglong) 32 << (qulonglong) 33 << QStringLiteral("2015-07-26T19:04:38");
QTest::newRow("corrupt-archive-unrar5")
- << QFINDTESTDATA("data/archive-corrupt-file-header-unrar5.txt") << 8
+ << QFINDTESTDATA("data/archive-corrupt-file-header-unrar5.txt") << 8 << false << 0
<< 6 << QStringLiteral("dir1/") << true << false << QString() << (qulonglong) 0 << (qulonglong) 0 << QStringLiteral("2015-05-14T01:45:24");
+ //Note: The number of entries will be the total number of all entries in all volumes, i.e. if a file spans 3 volumes it will count as 3 entries.
+ QTest::newRow("multivolume-archive-unrar5")
+ << QFINDTESTDATA("data/archive-multivol-unrar5.txt") << 6 << true << 5
+ << 5 << QStringLiteral("largefile2") << false << false << QString() << (qulonglong) 2097152 << (qulonglong) 11231 << QStringLiteral("2016-07-17T11:26:19");
+
// Unrar 4 tests
QTest::newRow("normal-file-unrar4")
- << QFINDTESTDATA("data/archive-with-symlink-unrar4.txt") << 8
+ << QFINDTESTDATA("data/archive-with-symlink-unrar4.txt") << 8 << false << 0
<< 2 << QStringLiteral("rartest/file2.txt") << false << false << QString() << (qulonglong) 14 << (qulonglong) 23 << QStringLiteral("2016-03-21T08:57:00");
QTest::newRow("symlink-unrar4")
- << QFINDTESTDATA("data/archive-with-symlink-unrar4.txt") << 8
+ << QFINDTESTDATA("data/archive-with-symlink-unrar4.txt") << 8 << false << 0
<< 3 << QStringLiteral("rartest/linktofile1.txt") << false << false << QStringLiteral("file1.txt") << (qulonglong) 9 << (qulonglong) 9 << QStringLiteral("2016-03-21T08:58:00");
QTest::newRow("encrypted-unrar4")
- << QFINDTESTDATA("data/archive-encrypted-unrar4.txt") << 7
+ << QFINDTESTDATA("data/archive-encrypted-unrar4.txt") << 7 << false << 0
<< 2 << QStringLiteral("rartest/file2.txt") << false << true << QString() << (qulonglong) 14 << (qulonglong) 32 << QStringLiteral("2016-03-21T17:03:00");
QTest::newRow("recovery-record-unrar4")
- << QFINDTESTDATA("data/archive-recovery-record-unrar4.txt") << 3
+ << QFINDTESTDATA("data/archive-recovery-record-unrar4.txt") << 3 << false << 0
<< 0 << QStringLiteral("file1.txt") << false << false << QString() << (qulonglong) 32 << (qulonglong) 33 << QStringLiteral("2015-07-26T19:04:00");
QTest::newRow("corrupt-archive-unrar4")
- << QFINDTESTDATA("data/archive-corrupt-file-header-unrar4.txt") << 8
+ << QFINDTESTDATA("data/archive-corrupt-file-header-unrar4.txt") << 8 << false << 0
<< 6 << QStringLiteral("dir1/") << true << false << QString() << (qulonglong) 0 << (qulonglong) 0 << QStringLiteral("2015-05-14T01:45:00");
+ //Note: The number of entries will be the total number of all entries in all volumes, i.e. if a file spans 3 volumes it will count as 3 entries.
+ QTest::newRow("multivolume-archive-unrar4")
+ << QFINDTESTDATA("data/archive-multivol-unrar4.txt") << 6 << true << 5
+ << 5 << QStringLiteral("largefile2") << false << false << QString() << (qulonglong) 2097152 << (qulonglong) 11231 << QStringLiteral("2016-07-17T11:26:00");
+
/*
* Check that the plugin will not crash when reading corrupted archives, which
* have lines such as "Unexpected end of archive" or "??? - the file header is
@@ -163,7 +176,7 @@ void CliRarTest::testList_data()
* See bug 262857 and commit 2042997013432cdc6974f5b26d39893a21e21011.
*/
QTest::newRow("corrupt-archive-unrar3")
- << QFINDTESTDATA("data/archive-corrupt-file-header-unrar3.txt") << 1
+ << QFINDTESTDATA("data/archive-corrupt-file-header-unrar3.txt") << 1 << true << 1
<< 0 << QStringLiteral("some-file.ext") << false << false << QString() << (qulonglong) 732522496 << (qulonglong) 14851208 << QStringLiteral("2010-10-29T20:47:00");
}
@@ -187,6 +200,12 @@ void CliRarTest::testList()
QCOMPARE(signalSpy.count(), expectedEntriesCount);
+ QFETCH(bool, isMultiVolume);
+ QCOMPARE(rarPlugin->isMultiVolume(), isMultiVolume);
+
+ QFETCH(int, numberOfVolumes);
+ QCOMPARE(rarPlugin->numberOfVolumes(), numberOfVolumes);
+
QFETCH(int, someEntryIndex);
QVERIFY(someEntryIndex < signalSpy.count());
Archive::Entry *entry = signalSpy.at(someEntryIndex).at(0).value<Archive::Entry*>();
@@ -267,11 +286,12 @@ void CliRarTest::testAddArgs_data()
QTest::addColumn<QString>("password");
QTest::addColumn<bool>("encryptHeader");
QTest::addColumn<int>("compressionLevel");
+ QTest::addColumn<ulong>("volumeSize");
QTest::addColumn<QStringList>("expectedArgs");
QTest::newRow("unencrypted")
<< QStringLiteral("/tmp/foo.rar")
- << QString() << false << 3
+ << QString() << false << 3 << 0UL
<< QStringList {
QStringLiteral("a"),
QStringLiteral("/tmp/foo.rar"),
@@ -280,7 +300,7 @@ void CliRarTest::testAddArgs_data()
QTest::newRow("encrypted")
<< QStringLiteral("/tmp/foo.rar")
- << QStringLiteral("1234") << false << 3
+ << QStringLiteral("1234") << false << 3 << 0UL
<< QStringList {
QStringLiteral("a"),
QStringLiteral("/tmp/foo.rar"),
@@ -290,13 +310,23 @@ void CliRarTest::testAddArgs_data()
QTest::newRow("header-encrypted")
<< QStringLiteral("/tmp/foo.rar")
- << QStringLiteral("1234") << true << 3
+ << QStringLiteral("1234") << true << 3 << 0UL
<< QStringList {
QStringLiteral("a"),
QStringLiteral("/tmp/foo.rar"),
QStringLiteral("-hp1234"),
QStringLiteral("-m3")
};
+
+ QTest::newRow("multi-volume")
+ << QStringLiteral("/tmp/foo.rar")
+ << QString() << false << 3 << 2500UL
+ << QStringList {
+ QStringLiteral("a"),
+ QStringLiteral("/tmp/foo.rar"),
+ QStringLiteral("-m3"),
+ QStringLiteral("-v2500k")
+ };
}
void CliRarTest::testAddArgs()
@@ -309,13 +339,15 @@ void CliRarTest::testAddArgs()
QStringLiteral("$Archive"),
QStringLiteral("$PasswordSwitch"),
QStringLiteral("$CompressionLevelSwitch"),
+ QStringLiteral("$MultiVolumeSwitch"),
QStringLiteral("$Files") };
QFETCH(QString, password);
QFETCH(bool, encryptHeader);
QFETCH(int, compressionLevel);
+ QFETCH(ulong, volumeSize);
- QStringList replacedArgs = rarPlugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel);
+ QStringList replacedArgs = rarPlugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel, volumeSize);
QFETCH(QStringList, expectedArgs);
QCOMPARE(replacedArgs, expectedArgs);
diff --git a/autotests/plugins/clirarplugin/data/archive-multivol-unrar4.txt b/autotests/plugins/clirarplugin/data/archive-multivol-unrar4.txt
new file mode 100644
index 0000000..42442a8
--- /dev/null
+++ b/autotests/plugins/clirarplugin/data/archive-multivol-unrar4.txt
@@ -0,0 +1,68 @@
+
+UNRAR 4.20 freeware Copyright (c) 1993-2012 Alexander Roshal
+
+Volume testmultivol.part1.rar
+
+Pathname/Comment
+ Size Packed Ratio Date Time Attr CRC Meth Ver
+ Host OS Solid Old
+-------------------------------------------------------------------------------
+ largefile1
+ 2097152 1048494 --> 17-07-16 11:25 -rw-rw-r-- D1D888DB m3g 2.9
+ Unix No No
+-------------------------------------------------------------------------------
+ 1 2097152 1048494 49% volume 1
+
+Volume testmultivol.part2.rar
+
+Pathname/Comment
+ Size Packed Ratio Date Time Attr CRC Meth Ver
+ Host OS Solid Old
+-------------------------------------------------------------------------------
+ largefile1
+ 2097152 1048494 <-> 17-07-16 11:25 -rw-rw-r-- 034C76D1 m3g 2.9
+ Unix No No
+-------------------------------------------------------------------------------
+ 0 0 1048494 0% volume 2
+
+Volume testmultivol.part3.rar
+
+Pathname/Comment
+ Size Packed Ratio Date Time Attr CRC Meth Ver
+ Host OS Solid Old
+-------------------------------------------------------------------------------
+ largefile1
+ 2097152 5613 <-- 17-07-16 11:25 -rw-rw-r-- 147E8FFD m3g 2.9
+ Unix No No
+ largefile2
+ 2097152 1042837 --> 17-07-16 11:26 -rw-rw-r-- 0B319F93 m3g 2.9
+ Unix No No
+-------------------------------------------------------------------------------
+ 1 2097152 1048450 49% volume 3
+
+Volume testmultivol.part4.rar
+
+Pathname/Comment
+ Size Packed Ratio Date Time Attr CRC Meth Ver
+ Host OS Solid Old
+-------------------------------------------------------------------------------
+ largefile2
+ 2097152 1048492 <-> 17-07-16 11:26 -rw-rw-r-- BE13353A m3g 2.9
+ Unix No No
+-------------------------------------------------------------------------------
+ 0 0 1048492 0% volume 4
+
+Volume testmultivol.part5.rar
+
+Pathname/Comment
+ Size Packed Ratio Date Time Attr CRC Meth Ver
+ Host OS Solid Old
+-------------------------------------------------------------------------------
+ largefile2
+ 2097152 11231 <-- 17-07-16 11:26 -rw-rw-r-- 934DAE71 m3g 2.9
+ Unix No No
+-------------------------------------------------------------------------------
+ 0 0 11231 0% volume 5
+
+ 2 4194304 4205161 100%
+
diff --git a/autotests/plugins/clirarplugin/data/archive-multivol-unrar5.txt b/autotests/plugins/clirarplugin/data/archive-multivol-unrar5.txt
new file mode 100644
index 0000000..fdae294
--- /dev/null
+++ b/autotests/plugins/clirarplugin/data/archive-multivol-unrar5.txt
@@ -0,0 +1,84 @@
+
+UNRAR 5.40 beta 2 freeware Copyright (c) 1993-2016 Alexander Roshal
+
+Archive: testmultivol.part1.rar
+Details: RAR 4, volume
+
+ Name: largefile1
+ Type: File
+ Size: 2097152
+ Packed size: 1048494
+ Ratio: -->
+ mtime: 2016-07-17 11:25:56,000
+ Attributes: -rw-rw-r--
+ Pack-CRC32: D1D888DB
+ Host OS: Unix
+ Compression: RAR 3.0(v29) -m3 -md=4M
+
+Archive: testmultivol.part2.rar
+Details: RAR 4, volume
+
+ Name: largefile1
+ Type: File
+ Size: 2097152
+ Packed size: 1048494
+ Ratio: <->
+ mtime: 2016-07-17 11:25:56,000
+ Attributes: -rw-rw-r--
+ Pack-CRC32: 034C76D1
+ Host OS: Unix
+ Compression: RAR 3.0(v29) -m3 -md=4M
+
+Archive: testmultivol.part3.rar
+Details: RAR 4, volume
+
+ Name: largefile1
+ Type: File
+ Size: 2097152
+ Packed size: 5613
+ Ratio: <--
+ mtime: 2016-07-17 11:25:56,000
+ Attributes: -rw-rw-r--
+ CRC32: 147E8FFD
+ Host OS: Unix
+ Compression: RAR 3.0(v29) -m3 -md=4M
+
+ Name: largefile2
+ Type: File
+ Size: 2097152
+ Packed size: 1042837
+ Ratio: -->
+ mtime: 2016-07-17 11:26:19,000
+ Attributes: -rw-rw-r--
+ Pack-CRC32: 0B319F93
+ Host OS: Unix
+ Compression: RAR 3.0(v29) -m3 -md=4M
+
+Archive: testmultivol.part4.rar
+Details: RAR 4, volume
+
+ Name: largefile2
+ Type: File
+ Size: 2097152
+ Packed size: 1048492
+ Ratio: <->
+ mtime: 2016-07-17 11:26:19,000
+ Attributes: -rw-rw-r--
+ Pack-CRC32: BE13353A
+ Host OS: Unix
+ Compression: RAR 3.0(v29) -m3 -md=4M
+
+Archive: testmultivol.part5.rar
+Details: RAR 4, volume
+
+ Name: largefile2
+ Type: File
+ Size: 2097152
+ Packed size: 11231
+ Ratio: <--
+ mtime: 2016-07-17 11:26:19,000
+ Attributes: -rw-rw-r--
+ CRC32: 934DAE71
+ Host OS: Unix
+ Compression: RAR 3.0(v29) -m3 -md=4M
+
diff --git a/autotests/plugins/cliunarchiverplugin/cliunarchivertest.cpp b/autotests/plugins/cliunarchiverplugin/cliunarchivertest.cpp
index eb22cfe..2b2fd1d 100644
--- a/autotests/plugins/cliunarchiverplugin/cliunarchivertest.cpp
+++ b/autotests/plugins/cliunarchiverplugin/cliunarchivertest.cpp
@@ -276,6 +276,12 @@ void CliUnarchiverTest::testExtraction_data()
<< QList<Archive::Entry*>()
<< optionsPreservePaths
<< 5;
+
+ QTest::newRow("rar with hidden folder and files")
+ << QFINDTESTDATA("data/hidden_files.rar")
+ << QList<Archive::Entry*>()
+ << optionsPreservePaths
+ << 4;
}
// TODO: we can remove this test (which is duplicated from kerfuffle/archivetest)
@@ -311,7 +317,7 @@ void CliUnarchiverTest::testExtraction()
QFETCH(int, expectedExtractedEntriesCount);
int extractedEntriesCount = 0;
- QDirIterator dirIt(destDir.path(), QDir::AllEntries | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ QDirIterator dirIt(destDir.path(), QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (dirIt.hasNext()) {
extractedEntriesCount++;
dirIt.next();
diff --git a/autotests/plugins/cliunarchiverplugin/data/hidden_files.rar b/autotests/plugins/cliunarchiverplugin/data/hidden_files.rar
new file mode 100644
index 0000000..b186eba
--- /dev/null
+++ b/autotests/plugins/cliunarchiverplugin/data/hidden_files.rar
Binary files differ
diff --git a/autotests/plugins/clizipplugin/cliziptest.cpp b/autotests/plugins/clizipplugin/cliziptest.cpp
index 1ca09d7..a0b8e7b 100644
--- a/autotests/plugins/clizipplugin/cliziptest.cpp
+++ b/autotests/plugins/clizipplugin/cliziptest.cpp
@@ -106,7 +106,7 @@ void CliZipTest::testAddArgs()
QFETCH(QString, password);
QFETCH(int, compressionLevel);
- QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, false, compressionLevel);
+ QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, false, compressionLevel, 0);
QFETCH(QStringList, expectedArgs);
QCOMPARE(replacedArgs, expectedArgs);
diff --git a/doc/create-archive.png b/doc/create-archive.png
new file mode 100644
index 0000000..40a6bf5
--- /dev/null
+++ b/doc/create-archive.png
Binary files differ
diff --git a/doc/create-protected-archive.png b/doc/create-protected-archive.png
index 686d701..cf3d5f2 100644
--- a/doc/create-protected-archive.png
+++ b/doc/create-protected-archive.png
Binary files differ
diff --git a/doc/index.docbook b/doc/index.docbook
index 73edbcd..6b17457 100644
--- a/doc/index.docbook
+++ b/doc/index.docbook
@@ -1,6 +1,5 @@
<?xml version="1.0" ?>
<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
- <!ENTITY kappname "&ark;">
<!ENTITY % addindex "IGNORE">
<!ENTITY % English "INCLUDE" > <!-- change language only here -->
<!ENTITY Ragnar.Thomsen '<personname><firstname>Ragnar</firstname><surname>Thomsen</surname></personname>'>
@@ -45,7 +44,7 @@
<legalnotice>&FDLNotice;</legalnotice>
-<date>2016-05-05</date>
+<date>2016-08-09</date>
<releaseinfo>Applications 16.08</releaseinfo>
<abstract>
@@ -74,7 +73,8 @@ archives.
<command>tar</command>, <command>gzip</command>,
<command>bzip2</command>, <command>zip</command>, <command>rar</command>,
<command>7zip</command>, <command>xz</command>, <command>rpm</command>,
-<command>cab</command> and <command>deb</command> (support for certain archive formats depends on
+<command>cab</command>, <command>deb</command>, <command>xar</command>
+and <command>AppImage</command> (support for certain archive formats depends on
the appropriate command-line programs being installed).</para>
<mediaobject>
@@ -121,6 +121,10 @@ For example, you can save the archive with a different name using <guimenuitem>S
Archive properties such as type, size and MD5 hash can be viewed using the
<guimenuitem>Properties</guimenuitem> item.</para>
+<para>&ark; has the ability to test archives for integrity. This functionality is currently available for
+<command>zip</command>, <command>rar</command> and <command>7z</command> archives.
+The test action can be found in the <guimenu>Archive</guimenu> menu.</para>
+
</sect2>
<sect2 id="ark-archive-comments">
@@ -263,11 +267,20 @@ archive.</para>
<guimenuitem>New</guimenuitem> (<keycombo action="simul">&Ctrl;<keycap>N</keycap></keycombo>)
from the <guimenu>Archive</guimenu> menu.</para>
+<mediaobject>
+<imageobject>
+<imagedata fileref="create-archive.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>Create an archive</phrase>
+</textobject>
+</mediaobject>
+
<para>You can then type the name of the archive, with the appropriate
extension (<literal role="extension">tar.gz</literal>, <literal
role="extension">zip</literal>, <literal role="extension">7z</literal>,
&etc;) or select a supported format in the <guilabel>Filter</guilabel> combo box
-and check the <guilabel>Automatically select filename extension</guilabel> option.</para>
+and check the <guilabel>Automatically add <replaceable>filename extension</replaceable></guilabel> option.</para>
<para>To add files or folders to the new archive, choose <guimenuitem>Add
File...</guimenuitem> or <guimenuitem>Add
@@ -278,6 +291,17 @@ from e.g. &dolphin; into the main &ark; window, and it will
be added to the current archive. Note that files added in this way will always be added
to the root directory of the archive.</para>
+<para>Additional options are presented in collapsible groups at the bottom of the dialog.
+</para>
+
+<sect2 id="ark-compression">
+<title>Compression</title>
+<para>A higher value generates smaller archives, but results in longer compression and decompression times.
+The default compression level proposed by &ark; is usually a good compromise between size and (de)compression speed.
+For most formats the minimum compression level is equivalent to just storing the files, &ie; applying no compression.
+</para>
+</sect2>
+
<sect2 id="ark-password-protection">
<title>Password Protection</title>
<para>If you create a <literal role="extension">zip</literal>, <literal
@@ -298,8 +322,28 @@ This is called header encryption and is available only with the
<literal role="extension">rar</literal> and <literal role="extension">7zip</literal>
formats. Header encryption is enabled by default (when available), in order to offer
the maximum protection for novice users.</para>
+</sect2>
+<sect2 id="ark-multi-volume">
+<title>Multi-volume Archive</title>
+<para>With the <literal role="extension">zip</literal>, <literal role="extension">rar</literal>
+and <literal role="extension">7z</literal> formats you can create multi-volume archives, also
+known as multi-part or split archives.</para>
+<para>A multi-volume archive is one big compressed archive split into several files. This feature
+is useful if the maximum file size is limited, &eg; by the capacity of a storage medium
+or the maximal size of an email with attachments.</para>
+<para>To create a multi-volume archive, check the <guilabel>Create multi-volume archive</guilabel>
+checkbox and set a maximum <guilabel>Volume size</guilabel> in the dialog. Then add all files to
+the archive and &ark; will automatically generate the required number of archive volumes.
+Depending on the selected format the files have an extension with consecutively numbering
+scheme &eg; <filename>xxx.7z.001</filename>, <filename>xxx.7z.002</filename> or
+<filename>xxx.zip.001</filename>, <filename>xxx.zip.002</filename> or <filename>xxx.part1.rar</filename>,
+<filename>xxx.part2.rar</filename> &etc;.</para>
+<para>To extract a multi-volume archive, put all archive files into one folder and open the file
+with the lowest extension number in &ark; and all other parts of the split archive
+will be opened automatically.</para>
</sect2>
+
</sect1>
</chapter>
diff --git a/doc/man-ark.1.docbook b/doc/man-ark.1.docbook
index 9786bca..b98cb54 100644
--- a/doc/man-ark.1.docbook
+++ b/doc/man-ark.1.docbook
@@ -19,8 +19,8 @@
<contrib>Update of &ark; man page in 2015 and 2016.</contrib>
<email>rthomsen6@gmail.com</email></author>
-<date>2016-03-19</date><!--Update only when changing/reviewing this man page-->
-<releaseinfo>16.04</releaseinfo><!--Update only when changing/reviewing this man page-->
+<date>2016-08-09</date><!--Update only when changing/reviewing this man page-->
+<releaseinfo>16.08</releaseinfo><!--Update only when changing/reviewing this man page-->
<productname>KDE Applications</productname>
</refentryinfo>
@@ -50,7 +50,7 @@ file</replaceable></group>
<group choice="opt"><option>-d</option></group>
<group choice="opt"><option>-o</option> <replaceable>
directory</replaceable></group>
-<arg choice="opt">&kde; Generic Options</arg>
+<arg choice="opt">&kf5; Generic Options</arg>
<arg choice="opt">&Qt; Generic Options</arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -175,6 +175,19 @@ a subfolder by the name of the archive will be created.</para>
</refsect1>
<refsect1>
+<title>See Also</title>
+<simplelist>
+<member>More detailed user documentation is available from <ulink
+url="help:/ark">help:/ark</ulink>
+(either enter this &URL; into &konqueror;, or run
+<userinput><command>khelpcenter</command>
+<parameter>help:/ark</parameter></userinput>).</member>
+<member>kf5options(7)</member>
+<member>qt5options(7)</member>
+</simplelist>
+</refsect1>
+
+<refsect1>
<title>Examples</title>
<variablelist>
diff --git a/kerfuffle/adddialog.cpp b/kerfuffle/adddialog.cpp
index 882f145..a86c626 100644
--- a/kerfuffle/adddialog.cpp
+++ b/kerfuffle/adddialog.cpp
@@ -88,6 +88,7 @@ QStringList AddDialog::selectedFiles() const
CompressionOptions AddDialog::compressionOptions() const
{
+ qCDebug(ARK) << "Returning with options:" << m_compOptions;
return m_compOptions;
}
@@ -100,6 +101,7 @@ void AddDialog::slotOpenOptions()
CompressionOptionsWidget *optionsWidget = new CompressionOptionsWidget(optionsDialog, m_compOptions);
optionsWidget->setMimeType(m_mimeType);
optionsWidget->setEncryptionVisible(false);
+ optionsWidget->collapsibleMultiVolume->setVisible(false);
optionsWidget->collapsibleCompression->expand();
vlayout->addWidget(optionsWidget);
diff --git a/kerfuffle/addtoarchive.cpp b/kerfuffle/addtoarchive.cpp
index 3df09af..c8f8d50 100644
--- a/kerfuffle/addtoarchive.cpp
+++ b/kerfuffle/addtoarchive.cpp
@@ -68,7 +68,7 @@ void AddToArchive::setChangeToFirstPath(bool value)
void AddToArchive::setFilename(const QUrl &path)
{
- m_filename = path.toDisplayString(QUrl::PreferLocalFile);
+ m_filename = path.toLocalFile();
}
void AddToArchive::setMimeType(const QString & mimeType)
@@ -114,11 +114,11 @@ bool AddToArchive::showAddDialog()
bool AddToArchive::addInput(const QUrl &url)
{
Archive::Entry *entry = new Archive::Entry();
- entry->setFullPath(url.toDisplayString(QUrl::PreferLocalFile));
+ entry->setFullPath(url.toLocalFile());
m_entries << entry;
if (m_firstPath.isEmpty()) {
- QString firstEntry = url.toDisplayString(QUrl::PreferLocalFile);
+ QString firstEntry = url.toLocalFile();
m_firstPath = QFileInfo(firstEntry).dir().absolutePath();
}
diff --git a/kerfuffle/archive_kerfuffle.cpp b/kerfuffle/archive_kerfuffle.cpp
index 8e6cd59..41db04b 100644
--- a/kerfuffle/archive_kerfuffle.cpp
+++ b/kerfuffle/archive_kerfuffle.cpp
@@ -34,6 +34,7 @@
#include "pluginmanager.h"
#include <QEventLoop>
+#include <QRegularExpression>
#include <KPluginFactory>
#include <KPluginLoader>
@@ -59,7 +60,7 @@ Archive *Archive::create(const QString &fileName, const QString &fixedMimeType,
return new Archive(NoPlugin, parent);
}
- Archive *archive;
+ Archive *archive = Q_NULLPTR;
foreach (Plugin *plugin, offers) {
archive = create(fileName, plugin, parent);
// Use the first valid plugin, according to the priority sorting.
@@ -114,10 +115,12 @@ Archive::Archive(ReadOnlyArchiveInterface *archiveInterface, bool isReadOnly, QO
, m_hasBeenListed(false)
, m_isReadOnly(isReadOnly)
, m_isSingleFolderArchive(false)
+ , m_isMultiVolume(false)
, m_extractedFilesSize(0)
, m_error(NoError)
, m_encryptionType(Unencrypted)
, m_numberOfFiles(0)
+ , m_numberOfFolders(0)
{
qCDebug(ARK) << "Created archive instance";
@@ -134,11 +137,24 @@ Archive::~Archive()
QString Archive::completeBaseName() const
{
+ const QString suffix = QFileInfo(fileName()).suffix();
QString base = QFileInfo(fileName()).completeBaseName();
// Special case for compressed tar archives.
if (base.right(4).toUpper() == QLatin1String(".TAR")) {
base.chop(4);
+
+ // Multi-volume 7z's are named name.7z.001.
+ } else if (base.right(3).toUpper() == QLatin1String(".7Z")) {
+ base.chop(3);
+
+ // Multi-volume zip's are named name.zip.001.
+ } else if (base.right(4).toUpper() == QLatin1String(".ZIP")) {
+ base.chop(4);
+
+ // For multivolume rar's we want to remove the ".partNNN" suffix.
+ } else if (suffix.toUpper() == QLatin1String("RAR")) {
+ base.remove(QRegularExpression(QStringLiteral("\\.part[0-9]{1,3}$")));
}
return base;
@@ -191,9 +207,10 @@ QMimeType Archive::mimeType()
return m_mimeType;
}
-bool Archive::isReadOnly() const
+bool Archive::isReadOnly()
{
- return isValid() ? (m_iface->isReadOnly() || m_isReadOnly) : false;
+ return isValid() ? (m_iface->isReadOnly() || m_isReadOnly ||
+ (isMultiVolume() && (m_numberOfFiles > 0 || m_numberOfFolders > 0))) : false;
}
bool Archive::isSingleFolderArchive()
@@ -211,6 +228,25 @@ bool Archive::hasComment() const
return isValid() ? !comment().isEmpty() : false;
}
+bool Archive::isMultiVolume()
+{
+ if (!isValid()) {
+ return false;
+ }
+ listIfNotListed();
+ return m_iface->isMultiVolume();
+}
+
+void Archive::setMultiVolume(bool value)
+{
+ m_iface->setMultiVolume(value);
+}
+
+int Archive::numberOfVolumes() const
+{
+ return m_iface->numberOfVolumes();
+}
+
Archive::EncryptionType Archive::encryptionType()
{
if (!isValid()) {
@@ -221,6 +257,11 @@ Archive::EncryptionType Archive::encryptionType()
return m_encryptionType;
}
+QString Archive::password() const
+{
+ return m_iface->password();
+}
+
qulonglong Archive::numberOfFiles()
{
if (!isValid()) {
@@ -231,6 +272,16 @@ qulonglong Archive::numberOfFiles()
return m_numberOfFiles;
}
+qulonglong Archive::numberOfFolders()
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ listIfNotListed();
+ return m_numberOfFolders;
+}
+
qulonglong Archive::unpackedSize()
{
if (!isValid()) {
@@ -258,9 +309,7 @@ QString Archive::subfolderName()
void Archive::onNewEntry(const Archive::Entry *entry)
{
- if (!entry->isDir()) {
- m_numberOfFiles++;
- }
+ entry->isDir() ? m_numberOfFolders++ : m_numberOfFiles++;
}
bool Archive::isValid() const
@@ -298,6 +347,10 @@ ListJob* Archive::list()
if (!m_hasBeenListed) {
connect(job, &ListJob::result, this, &Archive::onListFinished);
}
+
+ // FIXME: this is only a temporary workaround. See T3300 for a proper fix.
+ m_hasBeenListed = true;
+
return job;
}
@@ -307,7 +360,7 @@ DeleteJob* Archive::deleteFiles(QList<Archive::Entry*> &entries)
return Q_NULLPTR;
}
- qCDebug(ARK) << "Going to delete entries" << entries;
+ qCDebug(ARK) << "Going to delete" << entries.size() << "entries";
if (m_iface->isReadOnly()) {
return 0;
@@ -455,8 +508,6 @@ void Archive::onListFinished(KJob* job)
// If we already know the password, it means that the archive is header-encrypted.
m_encryptionType = m_iface->password().isEmpty() ? Encrypted : HeaderEncrypted;
}
-
- m_hasBeenListed = true;
}
void Archive::listIfNotListed()
@@ -492,4 +543,9 @@ CompressionOptions Archive::compressionOptions() const
return m_compOptions;
}
+QString Archive::multiVolumeName() const
+{
+ return m_iface->multiVolumeName();
+}
+
} // namespace Kerfuffle
diff --git a/kerfuffle/archive_kerfuffle.h b/kerfuffle/archive_kerfuffle.h
index 2ad3782..e64dc14 100644
--- a/kerfuffle/archive_kerfuffle.h
+++ b/kerfuffle/archive_kerfuffle.h
@@ -2,7 +2,6 @@
* Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (c) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (c) 2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
- * Copyright (c) 2016 Vladyslav Batyrenko <mvlabat@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -85,11 +84,15 @@ class KERFUFFLE_EXPORT Archive : public QObject
Q_PROPERTY(QMimeType mimeType READ mimeType CONSTANT)
Q_PROPERTY(bool isReadOnly READ isReadOnly CONSTANT)
Q_PROPERTY(bool isSingleFolderArchive READ isSingleFolderArchive)
+ Q_PROPERTY(bool isMultiVolume READ isMultiVolume WRITE setMultiVolume)
+ Q_PROPERTY(bool numberOfVolumes READ numberOfVolumes)
Q_PROPERTY(EncryptionType encryptionType READ encryptionType)
Q_PROPERTY(qulonglong numberOfFiles READ numberOfFiles)
+ Q_PROPERTY(qulonglong numberOfFolders READ numberOfFolders)
Q_PROPERTY(qulonglong unpackedSize READ unpackedSize)
Q_PROPERTY(qulonglong packedSize READ packedSize)
Q_PROPERTY(QString subfolderName READ subfolderName)
+ Q_PROPERTY(QString password READ password)
public:
@@ -106,16 +109,22 @@ public:
QString fileName() const;
QString comment() const;
QMimeType mimeType();
- bool isReadOnly() const;
+ bool isReadOnly();
bool isSingleFolderArchive();
+ bool isMultiVolume();
+ void setMultiVolume(bool value);
bool hasComment() const;
+ int numberOfVolumes() const;
EncryptionType encryptionType();
+ QString password() const;
qulonglong numberOfFiles();
+ qulonglong numberOfFolders();
qulonglong unpackedSize();
qulonglong packedSize() const;
QString subfolderName();
void setCompressionOptions(const CompressionOptions &opts);
CompressionOptions compressionOptions() const;
+ QString multiVolumeName() const;
static Archive *create(const QString &fileName, QObject *parent = 0);
static Archive *create(const QString &fileName, const QString &fixedMimeType, QObject *parent = 0);
@@ -200,12 +209,14 @@ private:
bool m_hasBeenListed;
bool m_isReadOnly;
bool m_isSingleFolderArchive;
+ bool m_isMultiVolume;
QString m_subfolderName;
qulonglong m_extractedFilesSize;
ArchiveError m_error;
EncryptionType m_encryptionType;
qulonglong m_numberOfFiles;
+ qulonglong m_numberOfFolders;
CompressionOptions m_compOptions;
QMimeType m_mimeType;
};
diff --git a/kerfuffle/archiveformat.cpp b/kerfuffle/archiveformat.cpp
index c7c7817..9d36866 100644
--- a/kerfuffle/archiveformat.cpp
+++ b/kerfuffle/archiveformat.cpp
@@ -39,14 +39,16 @@ ArchiveFormat::ArchiveFormat(const QMimeType& mimeType,
int maxCompLevel,
int defaultCompLevel,
bool supportsWriteComment,
- bool supportsTesting) :
+ bool supportsTesting,
+ bool supportsMultiVolume) :
m_mimeType(mimeType),
m_encryptionType(encryptionType),
m_minCompressionLevel(minCompLevel),
m_maxCompressionLevel(maxCompLevel),
m_defaultCompressionLevel(defaultCompLevel),
m_supportsWriteComment(supportsWriteComment),
- m_supportsTesting(supportsTesting)
+ m_supportsTesting(supportsTesting),
+ m_supportsMultiVolume(supportsMultiVolume)
{
}
@@ -66,6 +68,7 @@ ArchiveFormat ArchiveFormat::fromMetadata(const QMimeType& mimeType, const KPlug
bool supportsWriteComment = formatProps[QStringLiteral("SupportsWriteComment")].toBool();
bool supportsTesting = formatProps[QStringLiteral("SupportsTesting")].toBool();
+ bool supportsMultiVolume = formatProps[QStringLiteral("SupportsMultiVolume")].toBool();
Archive::EncryptionType encType = Archive::Unencrypted;
if (formatProps[QStringLiteral("HeaderEncryption")].toBool()) {
@@ -74,7 +77,7 @@ ArchiveFormat ArchiveFormat::fromMetadata(const QMimeType& mimeType, const KPlug
encType = Archive::Encrypted;
}
- return ArchiveFormat(mimeType, encType, minCompLevel, maxCompLevel, defaultCompLevel, supportsWriteComment, supportsTesting);
+ return ArchiveFormat(mimeType, encType, minCompLevel, maxCompLevel, defaultCompLevel, supportsWriteComment, supportsTesting, supportsMultiVolume);
}
return ArchiveFormat();
@@ -115,4 +118,9 @@ bool ArchiveFormat::supportsTesting() const
return m_supportsTesting;
}
+bool ArchiveFormat::supportsMultiVolume() const
+{
+ return m_supportsMultiVolume;
+}
+
}
diff --git a/kerfuffle/archiveformat.h b/kerfuffle/archiveformat.h
index bd2b03a..f25e23b 100644
--- a/kerfuffle/archiveformat.h
+++ b/kerfuffle/archiveformat.h
@@ -43,7 +43,8 @@ public:
int maxCompLevel,
int defaultCompLevel,
bool supportsWriteComment,
- bool supportsTesting);
+ bool supportsTesting,
+ bool suppportsMultiVolume);
/**
* @return The archive format of the given @p mimeType, according to the given @p metadata.
@@ -65,6 +66,7 @@ public:
int defaultCompressionLevel() const;
bool supportsWriteComment() const;
bool supportsTesting() const;
+ bool supportsMultiVolume() const;
private:
QMimeType m_mimeType;
@@ -74,6 +76,7 @@ private:
int m_defaultCompressionLevel;
bool m_supportsWriteComment;
bool m_supportsTesting;
+ bool m_supportsMultiVolume;
};
}
diff --git a/kerfuffle/archiveinterface.cpp b/kerfuffle/archiveinterface.cpp
index c3df9ab..96995d0 100644
--- a/kerfuffle/archiveinterface.cpp
+++ b/kerfuffle/archiveinterface.cpp
@@ -39,9 +39,11 @@ namespace Kerfuffle
{
ReadOnlyArchiveInterface::ReadOnlyArchiveInterface(QObject *parent, const QVariantList & args)
: QObject(parent)
+ , m_numberOfVolumes(0)
, m_waitForFinishedSignal(false)
, m_isHeaderEncryptionEnabled(false)
, m_isCorrupt(false)
+ , m_isMultiVolume(false)
{
qCDebug(ARK) << "Created read-only interface for" << args.first().toString();
m_filename = args.first().toString();
@@ -114,6 +116,26 @@ bool ReadOnlyArchiveInterface::isCorrupt() const
return m_isCorrupt;
}
+bool ReadOnlyArchiveInterface::isMultiVolume() const
+{
+ return m_isMultiVolume;
+}
+
+void ReadOnlyArchiveInterface::setMultiVolume(bool value)
+{
+ m_isMultiVolume = value;
+}
+
+int ReadOnlyArchiveInterface::numberOfVolumes() const
+{
+ return m_numberOfVolumes;
+}
+
+QString ReadOnlyArchiveInterface::multiVolumeName() const
+{
+ return filename();
+}
+
ReadWriteArchiveInterface::ReadWriteArchiveInterface(QObject *parent, const QVariantList & args)
: ReadOnlyArchiveInterface(parent, args)
{
diff --git a/kerfuffle/archiveinterface.h b/kerfuffle/archiveinterface.h
index 2cb0fcc..901864f 100644
--- a/kerfuffle/archiveinterface.h
+++ b/kerfuffle/archiveinterface.h
@@ -65,6 +65,9 @@ public:
*/
QString password() const;
+ bool isMultiVolume() const;
+ int numberOfVolumes() const;
+
/**
* Returns whether the file can only be read.
*
@@ -151,6 +154,8 @@ public:
virtual bool doResume();
bool isHeaderEncryptionEnabled() const;
+ virtual QString multiVolumeName() const;
+ void setMultiVolume(bool value);
signals:
void cancelled();
@@ -174,6 +179,7 @@ protected:
void setCorrupt(bool isCorrupt);
bool isCorrupt() const;
QString m_comment;
+ int m_numberOfVolumes;
private:
QString m_filename;
@@ -181,6 +187,7 @@ private:
bool m_waitForFinishedSignal;
bool m_isHeaderEncryptionEnabled;
bool m_isCorrupt;
+ bool m_isMultiVolume;
};
class KERFUFFLE_EXPORT ReadWriteArchiveInterface: public ReadOnlyArchiveInterface
diff --git a/kerfuffle/cliinterface.cpp b/kerfuffle/cliinterface.cpp
index 43fc155..4793a54 100644
--- a/kerfuffle/cliinterface.cpp
+++ b/kerfuffle/cliinterface.cpp
@@ -47,6 +47,7 @@
#include <QDirIterator>
#include <QEventLoop>
#include <QFile>
+#include <QMimeDatabase>
#include <QProcess>
#include <QRegularExpression>
#include <QStandardPaths>
@@ -61,8 +62,8 @@ namespace Kerfuffle
CliInterface::CliInterface(QObject *parent, const QVariantList & args)
: ReadWriteArchiveInterface(parent, args),
m_process(0),
- m_listEmptyLines(false),
m_abortingOperation(false),
+ m_listEmptyLines(false),
m_extractTempDir(Q_NULLPTR),
m_commentTempFile(Q_NULLPTR)
{
@@ -231,18 +232,22 @@ bool CliInterface::addFiles(const QList<Archive::Entry*> &files, const Archive::
}
int compLevel = options.value(QStringLiteral("CompressionLevel"), -1).toInt();
+ ulong volumeSize = options.value(QStringLiteral("VolumeSize"), 0).toULongLong();
const auto args = substituteAddVariables(m_param.value(AddArgs).toStringList(),
filesToPass,
password(),
isHeaderEncryptionEnabled(),
- compLevel);
+ compLevel,
+ volumeSize);
return runProcess(m_param.value(AddProgram).toStringList(), args);
}
bool CliInterface::moveFiles(const QList<Archive::Entry*> &files, Archive::Entry *destination, const CompressionOptions &options)
{
+ Q_UNUSED(options);
+
cacheParameterList();
m_operationMode = Move;
@@ -296,7 +301,7 @@ bool CliInterface::testArchive()
cacheParameterList();
m_operationMode = Test;
- const auto args = substituteTestVariables(m_param.value(TestArgs).toStringList());
+ const auto args = substituteTestVariables(m_param.value(TestArgs).toStringList(), password());
return runProcess(m_param.value(TestProgram).toStringList(), args);
}
@@ -322,7 +327,6 @@ bool CliInterface::runProcess(const QStringList& programNames, const QStringList
qCDebug(ARK) << "Executing" << programPath << arguments << "within directory" << QDir::currentPath();
#ifdef Q_OS_WIN
-
m_process = new KProcess;
#else
m_process = new KPtyProcess;
@@ -333,17 +337,15 @@ bool CliInterface::runProcess(const QStringList& programNames, const QStringList
m_process->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered | QIODevice::Text);
m_process->setProgram(programPath, arguments);
- connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(readStdout()), Qt::DirectConnection);
+ connect(m_process, &QProcess::readyReadStandardOutput, this, [=]() {
+ readStdout();
+ });
if (m_operationMode == Extract) {
// Extraction jobs need a dedicated post-processing function.
- connect(m_process,
- static_cast<void (KPtyProcess::*)(int, QProcess::ExitStatus)>(&KPtyProcess::finished),
- this,
- &CliInterface::extractProcessFinished,
- Qt::DirectConnection);
+ connect(m_process, static_cast<void (KPtyProcess::*)(int, QProcess::ExitStatus)>(&KPtyProcess::finished), this, &CliInterface::extractProcessFinished);
} else {
- connect(m_process, static_cast<void (KPtyProcess::*)(int, QProcess::ExitStatus)>(&KPtyProcess::finished), this, &CliInterface::processFinished, Qt::DirectConnection);
+ connect(m_process, static_cast<void (KPtyProcess::*)(int, QProcess::ExitStatus)>(&KPtyProcess::finished), this, &CliInterface::processFinished);
}
m_stdOutData.clear();
@@ -383,7 +385,7 @@ void CliInterface::processFinished(int exitCode, QProcess::ExitStatus exitStatus
m_newMovedFiles.clear();
}
- if (m_operationMode == Add) {
+ if (m_operationMode == Add && !isMultiVolume()) {
if (m_extractTempDir) {
delete m_extractTempDir;
m_extractTempDir = Q_NULLPTR;
@@ -613,7 +615,7 @@ bool CliInterface::moveToDestination(const QDir &tempDir, const QDir &destDir, b
bool overwriteAll = false;
bool skipAll = false;
- QDirIterator dirIt(tempDir.path(), QDir::AllEntries | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ QDirIterator dirIt(tempDir.path(), QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (dirIt.hasNext()) {
dirIt.next();
@@ -752,7 +754,7 @@ QStringList CliInterface::substituteExtractVariables(const QStringList &extractA
return args;
}
-QStringList CliInterface::substituteAddVariables(const QStringList &addArgs, const QList<Archive::Entry*> &entries, const QString &password, bool encryptHeader, int compLevel)
+QStringList CliInterface::substituteAddVariables(const QStringList &addArgs, const QList<Archive::Entry*> &entries, const QString &password, bool encryptHeader, int compLevel, ulong volumeSize)
{
// Required if we call this function from unit tests.
cacheParameterList();
@@ -776,6 +778,11 @@ QStringList CliInterface::substituteAddVariables(const QStringList &addArgs, con
continue;
}
+ if (arg == QLatin1String("$MultiVolumeSwitch")) {
+ args << multiVolumeSwitch(volumeSize);
+ continue;
+ }
+
if (arg == QLatin1String("$Files")) {
args << entryFullPaths(entries, true);
continue;
@@ -890,7 +897,7 @@ QStringList CliInterface::substituteCommentVariables(const QStringList &commentA
return args;
}
-QStringList CliInterface::substituteTestVariables(const QStringList &testArgs)
+QStringList CliInterface::substituteTestVariables(const QStringList &testArgs, const QString &password)
{
// Required if we call this function from unit tests.
cacheParameterList();
@@ -904,6 +911,11 @@ QStringList CliInterface::substituteTestVariables(const QStringList &testArgs)
continue;
}
+ if (arg == QLatin1String("$PasswordSwitch")) {
+ args << passwordSwitch(password);
+ continue;
+ }
+
args << arg;
}
@@ -1025,6 +1037,24 @@ QString CliInterface::compressionLevelSwitch(int level) const
return compLevelSwitch;
}
+QString CliInterface::multiVolumeSwitch(ulong volumeSize) const
+{
+ // The maximum value we allow in the QDoubleSpinBox is 1000MB. Converted to
+ // KB this is 1024000.
+ if (volumeSize <= 0 || volumeSize > 1024000) {
+ return QString();
+ }
+
+ Q_ASSERT(m_param.contains(MultiVolumeSwitch));
+
+ QString multiVolumeSwitch = m_param.value(MultiVolumeSwitch).toString();
+ Q_ASSERT(!multiVolumeSwitch.isEmpty());
+
+ multiVolumeSwitch.replace(QLatin1String("$VolumeSize"), QString::number(volumeSize));
+
+ return multiVolumeSwitch;
+}
+
QStringList CliInterface::extractFilesList(const QList<Archive::Entry*> &entries) const
{
QStringList filesList;
@@ -1156,7 +1186,10 @@ void CliInterface::readStdout(bool handleAll)
foreach(const QByteArray& line, lines) {
if (!line.isEmpty() || (m_listEmptyLines && m_operationMode == List)) {
- handleLine(QString::fromLocal8Bit(line));
+ if (!handleLine(QString::fromLocal8Bit(line))) {
+ killProcess();
+ return;
+ }
}
}
}
@@ -1175,7 +1208,7 @@ bool CliInterface::setAddedFiles()
return true;
}
-void CliInterface::handleLine(const QString& line)
+bool CliInterface::handleLine(const QString& line)
{
// TODO: This should be implemented by each plugin; the way progress is
// shown by each CLI application is subject to a lot of variation.
@@ -1185,7 +1218,7 @@ void CliInterface::handleLine(const QString& line)
if (pos > 1) {
int percentage = line.midRef(pos - 2, 2).toInt();
emit progress(float(percentage) / 100);
- return;
+ return true;
}
}
@@ -1200,8 +1233,7 @@ void CliInterface::handleLine(const QString& line)
if (query.responseCancelled()) {
emit cancelled();
- killProcess();
- return;
+ return false;
}
setPassword(query.password());
@@ -1209,33 +1241,30 @@ void CliInterface::handleLine(const QString& line)
const QString response(password() + QLatin1Char('\n'));
writeToProcess(response.toLocal8Bit());
- return;
+ return true;
}
if (checkForErrorMessage(line, DiskFullPatterns)) {
qCWarning(ARK) << "Found disk full message:" << line;
emit error(i18nc("@info", "Extraction failed because the disk is full."));
- killProcess();
- return;
+ return false;
}
if (checkForErrorMessage(line, WrongPasswordPatterns)) {
qCWarning(ARK) << "Wrong password!";
setPassword(QString());
emit error(i18nc("@info", "Extraction failed: Incorrect password"));
- killProcess();
- return;
+ return false;
}
if (checkForErrorMessage(line, ExtractionFailedPatterns)) {
qCWarning(ARK) << "Error in extraction:" << line;
emit error(i18n("Extraction failed because of an unexpected error."));
- killProcess();
- return;
+ return false;
}
if (handleFileExistsMessage(line)) {
- return;
+ return true;
}
}
@@ -1249,8 +1278,7 @@ void CliInterface::handleLine(const QString& line)
if (query.responseCancelled()) {
emit cancelled();
- killProcess();
- return;
+ return false;
}
setPassword(query.password());
@@ -1258,36 +1286,35 @@ void CliInterface::handleLine(const QString& line)
const QString response(password() + QLatin1Char('\n'));
writeToProcess(response.toLocal8Bit());
- return;
+ return true;
}
if (checkForErrorMessage(line, WrongPasswordPatterns)) {
qCWarning(ARK) << "Wrong password!";
setPassword(QString());
emit error(i18n("Incorrect password."));
- killProcess();
- return;
+ return false;
}
if (checkForErrorMessage(line, ExtractionFailedPatterns)) {
qCWarning(ARK) << "Error in extraction!!";
emit error(i18n("Extraction failed because of an unexpected error."));
- killProcess();
- return;
+ return false;
}
if (checkForErrorMessage(line, CorruptArchivePatterns)) {
qCWarning(ARK) << "Archive corrupt";
setCorrupt(true);
- return;
+ // Special case: corrupt is not a "fatal" error so we return true here.
+ return true;
}
if (handleFileExistsMessage(line)) {
- return;
+ return true;
}
readListLine(line);
- return;
+ return true;
}
if (m_operationMode == Test) {
@@ -1295,17 +1322,18 @@ void CliInterface::handleLine(const QString& line)
if (checkForPasswordPromptMessage(line)) {
qCDebug(ARK) << "Found a password prompt";
- emit error(i18n("Ark does not currently support testing password-protected archives."));
- killProcess();
- return;
+ emit error(i18n("Ark does not currently support testing this archive."));
+ return false;
}
if (checkForTestSuccessMessage(line)) {
qCDebug(ARK) << "Test successful";
emit testSuccess();
- return;
+ return true;
}
}
+
+ return true;
}
bool CliInterface::checkForPasswordPromptMessage(const QString& line)
@@ -1493,4 +1521,20 @@ bool CliInterface::addComment(const QString &comment)
return true;
}
+QString CliInterface::multiVolumeName() const
+{
+ QString oldSuffix = QMimeDatabase().suffixForFileName(filename());
+ QString name;
+
+ foreach (const QString &multiSuffix, m_param.value(MultiVolumeSuffix).toStringList()) {
+ QString newSuffix = multiSuffix;
+ newSuffix.replace(QStringLiteral("$Suffix"), oldSuffix);
+ name = filename().remove(oldSuffix).append(newSuffix);
+ if (QFileInfo::exists(name)) {
+ break;
+ }
+ }
+ return name;
+}
+
}
diff --git a/kerfuffle/cliinterface.h b/kerfuffle/cliinterface.h
index 072a278..c4ad91e 100644
--- a/kerfuffle/cliinterface.h
+++ b/kerfuffle/cliinterface.h
@@ -285,7 +285,9 @@ enum CliInterfaceParameters {
CommentSwitch,
TestProgram,
TestArgs,
- TestPassedPattern
+ TestPassedPattern,
+ MultiVolumeSwitch,
+ MultiVolumeSuffix
};
typedef QHash<int, QVariant> ParameterList;
@@ -333,11 +335,11 @@ public:
QStringList substituteListVariables(const QStringList &listArgs, const QString &password);
QStringList substituteExtractVariables(const QStringList &extractArgs, const QList<Archive::Entry*> &entries, bool preservePaths, const QString &password);
- QStringList substituteAddVariables(const QStringList &addArgs, const QList<Archive::Entry*> &entries, const QString &password, bool encryptHeader, int compLevel);
+ QStringList substituteAddVariables(const QStringList &addArgs, const QList<Archive::Entry*> &entries, const QString &password, bool encryptHeader, int compLevel, ulong volumeSize);
QStringList substituteMoveVariables(const QStringList &moveArgs, const QList<Archive::Entry*> &entriesWithoutChildren, const Archive::Entry *destination, const QString &password);
QStringList substituteDeleteVariables(const QStringList &deleteArgs, const QList<Archive::Entry*> &entries, const QString &password);
QStringList substituteCommentVariables(const QStringList &commentArgs, const QString &commentFile);
- QStringList substituteTestVariables(const QStringList &testArgs);
+ QStringList substituteTestVariables(const QStringList &testArgs, const QString &password);
/**
* @see ArchiveModel::entryPathsFromDestination
@@ -364,15 +366,41 @@ public:
*/
QString compressionLevelSwitch(int level) const;
+ QString multiVolumeSwitch(ulong volumeSize) const;
+
/**
* @return The list of selected files to extract.
*/
QStringList extractFilesList(const QList<Archive::Entry*> &files) const;
+ QString multiVolumeName() const Q_DECL_OVERRIDE;
+
protected:
bool setAddedFiles();
- virtual void handleLine(const QString& line);
+
+ /**
+ * Handles the given @p line.
+ * @return True if the line is ok. False if the line contains/triggers a "fatal" error
+ * or a canceled user query. If false is returned, the caller is supposed to call killProcess().
+ */
+ virtual bool handleLine(const QString& line);
+
+ bool checkForErrorMessage(const QString& line, int parameterIndex);
+
+ /**
+ * Checks whether a line of the program's output is a password prompt.
+ *
+ * It uses the regular expression in the @c PasswordPromptPattern parameter
+ * for the check.
+ *
+ * @param line A line of the program's output.
+ *
+ * @return @c true if the given @p line is a password prompt, @c false
+ * otherwise.
+ */
+ bool checkForPasswordPromptMessage(const QString& line);
+
virtual void cacheParameterList();
/**
@@ -400,8 +428,6 @@ protected:
void cleanUp();
QString m_oldWorkingDir;
- ParameterList m_param;
- int m_exitCode;
QTemporaryDir *m_tempExtractDir;
QTemporaryDir *m_tempAddDir;
OperationMode m_subOperation;
@@ -410,27 +436,22 @@ protected:
Archive::Entry *m_passedDestination;
CompressionOptions m_passedOptions;
+ ParameterList m_param;
+
+#ifdef Q_OS_WIN
+ KProcess *m_process;
+#else
+ KPtyProcess *m_process;
+#endif
+
+ bool m_abortingOperation;
+
protected slots:
virtual void readStdout(bool handleAll = false);
private:
- /**
- * Checks whether a line of the program's output is a password prompt.
- *
- * It uses the regular expression in the @c PasswordPromptPattern parameter
- * for the check.
- *
- * @param line A line of the program's output.
- *
- * @return @c true if the given @p line is a password prompt, @c false
- * otherwise.
- */
-
- bool checkForPasswordPromptMessage(const QString& line);
-
bool handleFileExistsMessage(const QString& filename);
- bool checkForErrorMessage(const QString& line, int parameterIndex);
bool checkForTestSuccessMessage(const QString& line);
/**
@@ -474,16 +495,10 @@ private:
QRegularExpression m_passwordPromptPattern;
QHash<int, QList<QRegularExpression> > m_patternCache;
-#ifdef Q_OS_WIN
- KProcess *m_process;
-#else
- KPtyProcess *m_process;
-#endif
-
QList<Archive::Entry*> m_removedFiles;
QList<Archive::Entry*> m_newMovedFiles;
+ int m_exitCode;
bool m_listEmptyLines;
- bool m_abortingOperation;
QString m_storedFileName;
CompressionOptions m_compressionOptions;
diff --git a/kerfuffle/compressionoptionswidget.cpp b/kerfuffle/compressionoptionswidget.cpp
index 4340009..efdc964 100644
--- a/kerfuffle/compressionoptionswidget.cpp
+++ b/kerfuffle/compressionoptionswidget.cpp
@@ -47,19 +47,45 @@ CompressionOptionsWidget::CompressionOptionsWidget(QWidget *parent,
KColorScheme colorScheme(QPalette::Active, KColorScheme::View);
pwdWidget->setBackgroundWarningColor(colorScheme.background(KColorScheme::NegativeBackground).color());
pwdWidget->setPasswordStrengthMeterVisible(false);
+
+ connect(multiVolumeCheckbox, &QCheckBox::stateChanged, this, &CompressionOptionsWidget::slotMultiVolumeChecked);
+
+ if (m_opts.contains(QStringLiteral("VolumeSize"))) {
+ multiVolumeCheckbox->setChecked(true);
+ // Convert from kilobytes.
+ volumeSizeSpinbox->setValue(m_opts.value(QStringLiteral("VolumeSize")).toDouble() / 1024);
+ }
}
CompressionOptions CompressionOptionsWidget::commpressionOptions() const
{
CompressionOptions opts;
opts[QStringLiteral("CompressionLevel")] = compLevelSlider->value();
+ if (multiVolumeCheckbox->isChecked()) {
+ // Convert to kilobytes.
+ opts[QStringLiteral("VolumeSize")] = QString::number(volumeSize());
+ }
return opts;
}
int CompressionOptionsWidget::compressionLevel() const
{
- return compLevelSlider->value();
+ if (compLevelSlider->isEnabled()) {
+ return compLevelSlider->value();
+ } else {
+ return -1;
+ }
+}
+
+ulong CompressionOptionsWidget::volumeSize() const
+{
+ if (collapsibleMultiVolume->isEnabled() && multiVolumeCheckbox->isChecked()) {
+ // Convert to kilobytes.
+ return volumeSizeSpinbox->value() * 1024;
+ } else {
+ return 0;
+ }
}
void CompressionOptionsWidget::setEncryptionVisible(bool visible)
@@ -122,6 +148,15 @@ void CompressionOptionsWidget::updateWidgets()
compLevelSlider->setValue(archiveFormat.defaultCompressionLevel());
}
}
+
+ if (archiveFormat.supportsMultiVolume()) {
+ collapsibleMultiVolume->setEnabled(true);
+ collapsibleMultiVolume->setToolTip(QString());
+ } else {
+ collapsibleMultiVolume->setEnabled(false);
+ collapsibleMultiVolume->setToolTip(i18n("The %1 format does not support multi-volume archives.",
+ m_mimetype.comment()));
+ }
}
void CompressionOptionsWidget::setMimeType(const QMimeType &mimeType)
@@ -155,4 +190,15 @@ KNewPasswordWidget::PasswordStatus CompressionOptionsWidget::passwordStatus() co
return pwdWidget->passwordStatus();
}
+void CompressionOptionsWidget::slotMultiVolumeChecked(int state)
+{
+ if (state == Qt::Checked) {
+ lblVolumeSize->setEnabled(true);
+ volumeSizeSpinbox->setEnabled(true);
+ } else {
+ lblVolumeSize->setEnabled(false);
+ volumeSizeSpinbox->setEnabled(false);
+ }
+}
+
}
diff --git a/kerfuffle/compressionoptionswidget.h b/kerfuffle/compressionoptionswidget.h
index 350aa7a..00b95f8 100644
--- a/kerfuffle/compressionoptionswidget.h
+++ b/kerfuffle/compressionoptionswidget.h
@@ -45,6 +45,7 @@ public:
explicit CompressionOptionsWidget(QWidget *parent = Q_NULLPTR,
const CompressionOptions &opts = QHash<QString, QVariant>());
int compressionLevel() const;
+ ulong volumeSize() const;
QString password() const;
CompressionOptions commpressionOptions() const;
bool isEncryptionAvailable() const;
@@ -61,6 +62,9 @@ private:
QMimeType m_mimetype;
CompressionOptions m_opts;
+
+private slots:
+ void slotMultiVolumeChecked(int state);
};
}
diff --git a/kerfuffle/compressionoptionswidget.ui b/kerfuffle/compressionoptionswidget.ui
index b0a36f4..3a17156 100644
--- a/kerfuffle/compressionoptionswidget.ui
+++ b/kerfuffle/compressionoptionswidget.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>384</width>
- <height>62</height>
+ <width>401</width>
+ <height>90</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -106,7 +106,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <widget class="KNewPasswordWidget" name="pwdWidget" native="true">
+ <widget class="KNewPasswordWidget" name="pwdWidget">
<property name="enabled">
<bool>false</bool>
</property>
@@ -131,6 +131,73 @@
</layout>
</widget>
</item>
+ <item>
+ <widget class="KCollapsibleGroupBox" name="collapsibleMultiVolume">
+ <property name="title">
+ <string>Multi-volume Archive</string>
+ </property>
+ <property name="expanded">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="multiVolumeCheckbox">
+ <property name="text">
+ <string>Create multi-volume archive</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>30</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblVolumeSize">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Volume size:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="volumeSizeSpinbox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> megabytes</string>
+ </property>
+ <property name="decimals">
+ <number>1</number>
+ </property>
+ <property name="minimum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>1000.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.500000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
</widget>
<customwidgets>
@@ -143,7 +210,7 @@
<customwidget>
<class>KNewPasswordWidget</class>
<extends>QWidget</extends>
- <header location="global">KNewPasswordWidget</header>
+ <header>knewpasswordwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
diff --git a/kerfuffle/createdialog.cpp b/kerfuffle/createdialog.cpp
index dd21d88..2078547 100644
--- a/kerfuffle/createdialog.cpp
+++ b/kerfuffle/createdialog.cpp
@@ -151,6 +151,11 @@ int CreateDialog::compressionLevel() const
return m_ui->optionsWidget->compressionLevel();
}
+ulong CreateDialog::volumeSize() const
+{
+ return m_ui->optionsWidget->volumeSize();
+}
+
QString CreateDialog::password() const
{
return m_ui->optionsWidget->password();
diff --git a/kerfuffle/createdialog.h b/kerfuffle/createdialog.h
index 4ba3d40..302fe9c 100644
--- a/kerfuffle/createdialog.h
+++ b/kerfuffle/createdialog.h
@@ -59,6 +59,7 @@ public:
QMimeType currentMimeType() const;
bool setMimeType(const QString &mimeTypeName);
int compressionLevel() const;
+ ulong volumeSize() const;
/**
* @return Whether the user can encrypt the new archive.
diff --git a/kerfuffle/extractiondialog.cpp b/kerfuffle/extractiondialog.cpp
index a22bb8e..c87ef20 100644
--- a/kerfuffle/extractiondialog.cpp
+++ b/kerfuffle/extractiondialog.cpp
@@ -274,7 +274,11 @@ QUrl ExtractionDialog::destinationDirectory() const
{
if (extractToSubfolder()) {
QUrl subUrl = fileWidget->baseUrl();
- subUrl.setPath(subUrl.path() + QLatin1Char('/') + subfolder());
+ if (subUrl.path().endsWith(QDir::separator())) {
+ subUrl.setPath(subUrl.path() + subfolder());
+ } else {
+ subUrl.setPath(subUrl.path() + QDir::separator() + subfolder());
+ }
return subUrl;
} else {
diff --git a/kerfuffle/jobs.cpp b/kerfuffle/jobs.cpp
index bb815b1..8b9aa02 100644
--- a/kerfuffle/jobs.cpp
+++ b/kerfuffle/jobs.cpp
@@ -31,6 +31,7 @@
#include "ark_debug.h"
#include <QDir>
+#include <QDirIterator>
#include <QFileInfo>
#include <QRegularExpression>
#include <QThread>
@@ -137,7 +138,7 @@ void Job::connectToArchiveInterfaceSignals()
connect(archiveInterface(), &ReadOnlyArchiveInterface::entryRemoved, this, &Job::onEntryRemoved);
connect(archiveInterface(), &ReadOnlyArchiveInterface::progress, this, &Job::onProgress);
connect(archiveInterface(), &ReadOnlyArchiveInterface::info, this, &Job::onInfo);
- connect(archiveInterface(), &ReadOnlyArchiveInterface::finished, this, &Job::onFinished, Qt::DirectConnection);
+ connect(archiveInterface(), &ReadOnlyArchiveInterface::finished, this, &Job::onFinished);
connect(archiveInterface(), &ReadOnlyArchiveInterface::userQuery, this, &Job::onUserQuery);
}
@@ -305,7 +306,7 @@ void ExtractJob::doWork()
connectToArchiveInterfaceSignals();
- qCDebug(ARK) << "Starting extraction with selected files:"
+ qCDebug(ARK) << "Starting extraction with" << m_entries.count() << "selected files."
<< m_entries
<< "Destination dir:" << m_destinationDir
<< "Options:" << m_options;
@@ -346,9 +347,9 @@ TempExtractJob::TempExtractJob(Archive::Entry *entry, bool passwordProtectedHint
, m_entry(entry)
, m_passwordProtectedHint(passwordProtectedHint)
{
+ m_tmpExtractDir = new QTemporaryDir();
}
-
QString TempExtractJob::validatedFilePath() const
{
QString path = extractionDir() + QLatin1Char('/') + m_entry->fullPath();
@@ -373,6 +374,11 @@ ExtractionOptions TempExtractJob::extractionOptions() const
return options;
}
+QTemporaryDir *TempExtractJob::tempDir() const
+{
+ return m_tmpExtractDir;
+}
+
void TempExtractJob::doWork()
{
emit description(this, i18n("Extracting one file"));
@@ -388,33 +394,21 @@ void TempExtractJob::doWork()
}
}
-PreviewJob::PreviewJob(Archive::Entry *entry, bool passwordProtectedHint, ReadOnlyArchiveInterface *interface)
- : TempExtractJob(entry, passwordProtectedHint, interface)
+QString TempExtractJob::extractionDir() const
{
- qCDebug(ARK) << "PreviewJob started";
+ return m_tmpExtractDir->path();
}
-QString PreviewJob::extractionDir() const
+PreviewJob::PreviewJob(Archive::Entry *entry, bool passwordProtectedHint, ReadOnlyArchiveInterface *interface)
+ : TempExtractJob(entry, passwordProtectedHint, interface)
{
- return m_tmpExtractDir.path();
+ qCDebug(ARK) << "PreviewJob started";
}
OpenJob::OpenJob(Archive::Entry *entry, bool passwordProtectedHint, ReadOnlyArchiveInterface *interface)
: TempExtractJob(entry, passwordProtectedHint, interface)
{
qCDebug(ARK) << "OpenJob started";
-
- m_tmpExtractDir = new QTemporaryDir();
-}
-
-QTemporaryDir *OpenJob::tempDir() const
-{
- return m_tmpExtractDir;
-}
-
-QString OpenJob::extractionDir() const
-{
- return m_tmpExtractDir->path();
}
OpenWithJob::OpenWithJob(Archive::Entry *entry, bool passwordProtectedHint, ReadOnlyArchiveInterface *interface)
@@ -434,15 +428,7 @@ AddJob::AddJob(const QList<Archive::Entry*> &entries, const Archive::Entry *dest
void AddJob::doWork()
{
- qCDebug(ARK) << "AddJob: going to add" << m_entries.count() << "file(s)";
-
- emit description(this, i18np("Adding a file", "Adding %1 files", m_entries.count()));
-
- ReadWriteArchiveInterface *m_writeInterface =
- qobject_cast<ReadWriteArchiveInterface*>(archiveInterface());
-
- Q_ASSERT(m_writeInterface);
-
+ // Set current dir.
const QString globalWorkDir = m_options.value(QStringLiteral("GlobalWorkDir")).toString();
const QDir workDir = globalWorkDir.isEmpty() ? QDir::current() : QDir(globalWorkDir);
if (!globalWorkDir.isEmpty()) {
@@ -451,6 +437,33 @@ void AddJob::doWork()
QDir::setCurrent(globalWorkDir);
}
+ // Count total number of entries to be added.
+ qulonglong totalCount = 0;
+ QElapsedTimer timer;
+ timer.start();
+ foreach (const Archive::Entry* entry, m_entries) {
+ totalCount++;
+ if (QFileInfo(entry->fullPath()).isDir()) {
+ QDirIterator it(entry->fullPath(), QDir::AllEntries | QDir::Readable | QDir::Hidden |
+ QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ totalCount++;
+ }
+ }
+ }
+ qCDebug(ARK) << "Counted" << totalCount << "entries in" << timer.elapsed() << "ms";
+
+ m_options[QStringLiteral("NumberOfEntries")] = totalCount;
+
+ qCDebug(ARK) << "AddJob: going to add" << totalCount << "entries";
+ emit description(this, i18np("Adding a file", "Adding %1 files", totalCount));
+
+ ReadWriteArchiveInterface *m_writeInterface =
+ qobject_cast<ReadWriteArchiveInterface*>(archiveInterface());
+
+ Q_ASSERT(m_writeInterface);
+
// The file paths must be relative to GlobalWorkDir.
foreach (Archive::Entry *entry, m_entries) {
// #191821: workDir must be used instead of QDir::current()
diff --git a/kerfuffle/jobs.h b/kerfuffle/jobs.h
index 50f3c28..196b0bf 100644
--- a/kerfuffle/jobs.h
+++ b/kerfuffle/jobs.h
@@ -162,13 +162,20 @@ public:
ExtractionOptions extractionOptions() const;
+ /**
+ * @return The temporary dir used for the extraction.
+ * It is safe to delete this pointer in order to remove the directory.
+ */
+ QTemporaryDir *tempDir() const;
+
public slots:
virtual void doWork() Q_DECL_OVERRIDE;
private:
- virtual QString extractionDir() const = 0;
+ QString extractionDir() const;
Archive::Entry *m_entry;
+ QTemporaryDir *m_tmpExtractDir;
bool m_passwordProtectedHint;
};
@@ -182,11 +189,6 @@ class KERFUFFLE_EXPORT PreviewJob : public TempExtractJob
public:
PreviewJob(Archive::Entry *entry, bool passwordProtectedHint, ReadOnlyArchiveInterface *interface);
-
-private:
- QString extractionDir() const Q_DECL_OVERRIDE;
-
- QTemporaryDir m_tmpExtractDir;
};
/**
@@ -199,17 +201,6 @@ class KERFUFFLE_EXPORT OpenJob : public TempExtractJob
public:
OpenJob(Archive::Entry *entry, bool passwordProtectedHint, ReadOnlyArchiveInterface *interface);
-
- /**
- * @return The temporary dir used for the extraction.
- * It is safe to delete this pointer in order to remove the directory.
- */
- QTemporaryDir *tempDir() const;
-
-private:
- QString extractionDir() const Q_DECL_OVERRIDE;
-
- QTemporaryDir *m_tmpExtractDir;
};
class KERFUFFLE_EXPORT OpenWithJob : public OpenJob
diff --git a/kerfuffle/kerfufflePlugin.desktop b/kerfuffle/kerfufflePlugin.desktop
index 7581b9d..087ff23 100644
--- a/kerfuffle/kerfufflePlugin.desktop
+++ b/kerfuffle/kerfufflePlugin.desktop
@@ -20,7 +20,7 @@ Comment[fi]=Kerfuffle-kirjaston arkistomuotojen tuki
Comment[fr]=Module externe de gestion des formats d'archives pour la bibliothèque « Kerfuffle »
Comment[ga]=Breiseán a láimhseálann formáidí cartlainne thar ceann na leabharlainne Kerfuffle
Comment[gl]=Extensión da biblioteca Kerfuffle para manexar formatos de arquivo
-Comment[he]=תוסף לניהול פורמטי ארכיונים לספריית Kerfuffle
+Comment[he]=תוסף לניהול פורמטי ארכיונים לספריית Kerfuffle
Comment[hne]=करफुफल लाइब्रेरी बर अभिलेख फारमेट हेंडल करे बर प्लगइन
Comment[hr]=Priključak za upravljanje arhivnim formatima za biblioteku Kerfuffle
Comment[hu]=Archívumkezelő modul a Kerfuffle programkönyvtárhoz
diff --git a/kerfuffle/mime/XMLMessages.sh b/kerfuffle/mime/XmlMessages.sh
index ea80d0c..ea80d0c 100644
--- a/kerfuffle/mime/XMLMessages.sh
+++ b/kerfuffle/mime/XmlMessages.sh
diff --git a/kerfuffle/mime/kerfuffle.xml b/kerfuffle/mime/kerfuffle.xml
index 00b1101..c02d807 100644
--- a/kerfuffle/mime/kerfuffle.xml
+++ b/kerfuffle/mime/kerfuffle.xml
@@ -2,11 +2,44 @@
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-lzip-compressed-tar">
<comment>Tar archive (lzip-compressed)</comment>
- <comment xml:lang="en">Tar archive (lzip-compressed)</comment>
+ <comment xml:lang="ca">Arxiu tar (comprimit amb LZIP)</comment>
+ <comment xml:lang="ca@valencia">Arxiu tar (comprimit amb LZIP)</comment>
+ <comment xml:lang="cs">Archiv Tar (komprimovaný lzip)</comment>
+ <comment xml:lang="de">Tar-Archiv (lzip-komprimiert)</comment>
+ <comment xml:lang="en_GB">Tar archive (lzip-compressed)</comment>
+ <comment xml:lang="es">Archivo comprimido Tar (comprimido con lzip)</comment>
+ <comment xml:lang="nl">Tar-archief (lzip-gecomprimeerd)</comment>
+ <comment xml:lang="pl">Archiwum Tar (kompresja lzip)</comment>
+ <comment xml:lang="pt">Pacote TAR (comprimido com Lzip)</comment>
+ <comment xml:lang="sk">Tar archív (lzip komprimovaný)</comment>
+ <comment xml:lang="sl">Arhiv tar (stisnjen z lzip)</comment>
+ <comment xml:lang="sr">тар архива (ЛЗИП‑компресована)</comment>
+ <comment xml:lang="sr@ijekavian">тар архива (ЛЗИП‑компресована)</comment>
+ <comment xml:lang="sr@ijekavianlatin">tar arhiva (LZIP-kompresovana)</comment>
+ <comment xml:lang="sr@latin">tar arhiva (LZIP-kompresovana)</comment>
+ <comment xml:lang="sv">Tar-arkiv (lzip-komprimerat)</comment>
+ <comment xml:lang="uk">архів Tar (стиснутий lzip)</comment>
<glob pattern="*.tar.lz"/>
</mime-type>
<mime-type type="application/x-xar">
<comment>XAR archive</comment>
+ <comment xml:lang="ca">Arxiu XAR</comment>
+ <comment xml:lang="ca@valencia">Arxiu XAR</comment>
+ <comment xml:lang="cs">Archiv XAR</comment>
+ <comment xml:lang="de">XAR-Archiv</comment>
+ <comment xml:lang="en_GB">XAR archive</comment>
+ <comment xml:lang="es">Archivo comprimido XAR</comment>
+ <comment xml:lang="nl">XAR-archief</comment>
+ <comment xml:lang="pl">Archiwum XAR</comment>
+ <comment xml:lang="pt">Pacote XAR</comment>
+ <comment xml:lang="sk">XAR archív</comment>
+ <comment xml:lang="sl">Arhiv XAR</comment>
+ <comment xml:lang="sr">КСАР архива</comment>
+ <comment xml:lang="sr@ijekavian">КСАР архива</comment>
+ <comment xml:lang="sr@ijekavianlatin">XAR arhiva</comment>
+ <comment xml:lang="sr@latin">XAR arhiva</comment>
+ <comment xml:lang="sv">XAR-arkiv</comment>
+ <comment xml:lang="uk">архів XAR</comment>
<acronym>XAR</acronym>
<expanded-acronym>eXtensible ARchive</expanded-acronym>
<generic-icon name="package-x-generic"/>
@@ -15,7 +48,56 @@
</mime-type>
<mime-type type="application/x-lz4-compressed-tar">
<comment>Tar archive (LZ4-compressed)</comment>
- <comment xml:lang="en">Tar archive (LZ4-compressed)</comment>
+ <comment xml:lang="ca">Arxiu tar (comprimit amb LZ4)</comment>
+ <comment xml:lang="ca@valencia">Arxiu tar (comprimit amb LZ4)</comment>
+ <comment xml:lang="cs">Archiv Tar (komprimovaný LZ4)</comment>
+ <comment xml:lang="de">Tar-Archiv (LZ4-komprimiert)</comment>
+ <comment xml:lang="en_GB">Tar archive (LZ4-compressed)</comment>
+ <comment xml:lang="es">Archivo comprimido Tar (comprimido con LZ4)</comment>
+ <comment xml:lang="nl">Tar-archief (lz4-gecomprimeerd)</comment>
+ <comment xml:lang="pl">Archiwum Tar (kompresja LZ4)</comment>
+ <comment xml:lang="pt">Pacote TAR (comprimido com LZ4)</comment>
+ <comment xml:lang="sk">Tar archív (LZ4 komprimovaný)</comment>
+ <comment xml:lang="sl">Arhiv tar (stisnjen z LZ4)</comment>
+ <comment xml:lang="sr">тар архива (ЛЗ4‑компресована)</comment>
+ <comment xml:lang="sr@ijekavian">тар архива (ЛЗ4‑компресована)</comment>
+ <comment xml:lang="sr@ijekavianlatin">tar arhiva (LZ4-kompresovana)</comment>
+ <comment xml:lang="sr@latin">tar arhiva (LZ4-kompresovana)</comment>
+ <comment xml:lang="sv">Tar-arkiv (LZ4-komprimerat)</comment>
+ <comment xml:lang="uk">архів Tar (стиснутий LZ4)</comment>
<glob pattern="*.tar.lz4"/>
</mime-type>
+ <mime-type type="application/x-iso9660-appimage">
+ <comment>AppImage application bundle</comment>
+ <comment xml:lang="ca">Paquet d'aplicació «AppImage»</comment>
+ <comment xml:lang="ca@valencia">Paquet d'aplicació «AppImage»</comment>
+ <comment xml:lang="de">AppImage-Anwendungspaket</comment>
+ <comment xml:lang="en_GB">AppImage application bundle</comment>
+ <comment xml:lang="es">Paquete de aplicación AppImage</comment>
+ <comment xml:lang="nl">Toepassingsbundel AppImage</comment>
+ <comment xml:lang="pl">Pęk aplikacji AppImage</comment>
+ <comment xml:lang="pt">Pacote de aplicação AppImage</comment>
+ <comment xml:lang="sk">Balík aplikácie AppImage</comment>
+ <comment xml:lang="sl">Zbirka programov AppImage</comment>
+ <comment xml:lang="sr">ап‑имејџ пакет програма</comment>
+ <comment xml:lang="sr@ijekavian">ап‑имејџ пакет програма</comment>
+ <comment xml:lang="sr@ijekavianlatin">AppImage paket programa</comment>
+ <comment xml:lang="sr@latin">AppImage paket programa</comment>
+ <comment xml:lang="sv">AppImage programpacke</comment>
+ <comment xml:lang="uk">пакунок програми AppImage</comment>
+ <sub-class-of type="application/x-executable"/>
+ <sub-class-of type="application/x-cd-image"/>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="ELF" type="string" offset="1" >
+ <match value="0x41" type="byte" offset="8">
+ <match value="0x49" type="byte" offset="9">
+ <match value="0x01" type="byte" offset="10"/>
+ </match>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.AppImage"/>
+ </mime-type>
</mime-info>
+
diff --git a/kerfuffle/propertiesdialog.cpp b/kerfuffle/propertiesdialog.cpp
index 96e55af..d8e81b6 100644
--- a/kerfuffle/propertiesdialog.cpp
+++ b/kerfuffle/propertiesdialog.cpp
@@ -49,7 +49,7 @@ public:
}
};
-PropertiesDialog::PropertiesDialog(QWidget *parent, Archive *archive)
+PropertiesDialog::PropertiesDialog(QWidget *parent, Archive *archive, qulonglong numberOfFiles, qulonglong numberOfFolders, qulonglong size)
: QDialog(parent, Qt::Dialog)
{
qCDebug(ARK) << "PropertiesDialog loaded";
@@ -64,9 +64,11 @@ PropertiesDialog::PropertiesDialog(QWidget *parent, Archive *archive)
m_ui->lblArchiveType->setText(archive->mimeType().comment());
m_ui->lblMimetype->setText(archive->mimeType().name());
m_ui->lblReadOnly->setText(archive->isReadOnly() ? i18n("yes") : i18n("no"));
+ m_ui->lblMultiVolume->setText(archive->isMultiVolume() ? i18n("yes (%1 volumes)", archive->numberOfVolumes()) : i18n("no"));
m_ui->lblHasComment->setText(archive->hasComment() ? i18n("yes") : i18n("no"));
- m_ui->lblNumberOfFiles->setText(QString::number(archive->numberOfFiles()));
- m_ui->lblUnpackedSize->setText(KIO::convertSize(archive->unpackedSize()));
+ m_ui->lblNumberOfEntries->setText(i18np("%1 file", "%1 files", numberOfFiles) +
+ i18np(", %1 folder", ", %1 folders", numberOfFolders));
+ m_ui->lblUnpackedSize->setText(KIO::convertSize(size));
m_ui->lblPackedSize->setText(KIO::convertSize(archive->packedSize()));
m_ui->lblCompressionRatio->setText(QString::number(float(archive->unpackedSize()) / float(archive->packedSize()), 'f', 1));
m_ui->lblLastModified->setText(fi.lastModified().toString(QStringLiteral("yyyy-MM-dd HH:mm")));
@@ -99,53 +101,41 @@ PropertiesDialog::PropertiesDialog(QWidget *parent, Archive *archive)
QIcon icon = QIcon::fromTheme(archive->mimeType().iconName());
m_ui->lblIcon->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop), IconSize(KIconLoader::Desktop)));
+ m_ui->lblMD5->setText(i18n("Calculating..."));
m_ui->lblSHA1->setText(i18n("Calculating..."));
m_ui->lblSHA256->setText(i18n("Calculating..."));
- QFile file(archive->fileName());
- if (file.open(QIODevice::ReadOnly)) {
- m_byteArray = file.readAll();
- file.close();
-
- QCryptographicHash hashMD5(QCryptographicHash::Md5);
- hashMD5.addData(m_byteArray);
- m_ui->lblMD5->setText(QLatin1String(hashMD5.result().toHex()));
-
- // The two SHA hashes take some time to calculate, so run them in another thread.
- QFutureWatcher<QString> *watchCalcSha1 = new QFutureWatcher<QString>;
- connect(watchCalcSha1, SIGNAL(finished()), this, SLOT(slotShowSha1()));
- m_futureCalcSha1 = QtConcurrent::run(this, &PropertiesDialog::calcHash, QCryptographicHash::Sha1);
- watchCalcSha1->setFuture(m_futureCalcSha1);
-
- QFutureWatcher<QString> *watchCalcSha256 = new QFutureWatcher<QString>;
- connect(watchCalcSha256, SIGNAL(finished()), this, SLOT(slotShowSha256()));
- m_futureCalcSha256 = QtConcurrent::run(this, &PropertiesDialog::calcHash, QCryptographicHash::Sha256);
- watchCalcSha256->setFuture(m_futureCalcSha256);
- } else {
- m_ui->lblMD5->setText(QString());
- m_ui->lblSHA1->setText(QString());
- m_ui->lblSHA256->setText(QString());
- }
+ showChecksum(QCryptographicHash::Md5, archive->fileName(), m_ui->lblMD5);
+ showChecksum(QCryptographicHash::Sha1, archive->fileName(), m_ui->lblSHA1);
+ showChecksum(QCryptographicHash::Sha256, archive->fileName(), m_ui->lblSHA256);
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
}
-QString PropertiesDialog::calcHash(QCryptographicHash::Algorithm algorithm)
+QString PropertiesDialog::calcHash(QCryptographicHash::Algorithm algorithm, const QString &path)
{
- QCryptographicHash hash(algorithm);
- hash.addData(m_byteArray);
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ return QString();
+ }
- return QLatin1String(hash.result().toHex());
-}
+ QCryptographicHash hash(algorithm);
+ hash.addData(&file);
-void PropertiesDialog::slotShowSha1()
-{
- m_ui->lblSHA1->setText(m_futureCalcSha1.result());
+ return QString::fromLatin1(hash.result().toHex());
}
-void PropertiesDialog::slotShowSha256()
+void PropertiesDialog::showChecksum(QCryptographicHash::Algorithm algorithm, const QString &fileName, QLabel *label)
{
- m_ui->lblSHA256->setText(m_futureCalcSha256.result());
+ // Calculate checksum in another thread.
+ auto futureWatcher = new QFutureWatcher<QString>(this);
+ connect(futureWatcher, &QFutureWatcher<QString>::finished, this, [=]() {
+ label->setText(futureWatcher->result());
+ futureWatcher->deleteLater();
+ });
+
+ auto future = QtConcurrent::run(this, &PropertiesDialog::calcHash, algorithm, fileName);
+ futureWatcher->setFuture(future);
}
}
diff --git a/kerfuffle/propertiesdialog.h b/kerfuffle/propertiesdialog.h
index ab5b345..be797c4 100644
--- a/kerfuffle/propertiesdialog.h
+++ b/kerfuffle/propertiesdialog.h
@@ -32,7 +32,8 @@
#include <QCryptographicHash>
#include <QDialog>
-#include <QFuture>
+
+class QLabel;
namespace Kerfuffle
{
@@ -41,19 +42,13 @@ class KERFUFFLE_EXPORT PropertiesDialog : public QDialog
Q_OBJECT
public:
- explicit PropertiesDialog(QWidget *parent, Archive *archive);
-
-private slots:
- void slotShowSha1();
- void slotShowSha256();
+ explicit PropertiesDialog(QWidget *parent, Archive *archive, qulonglong numberOfFiles, qulonglong numberOfFolders, qulonglong size);
private:
- QString calcHash(QCryptographicHash::Algorithm algorithm);
+ QString calcHash(QCryptographicHash::Algorithm algorithm, const QString &path);
+ void showChecksum(QCryptographicHash::Algorithm algorithm, const QString &fileName, QLabel *label);
class PropertiesDialogUI *m_ui;
- QByteArray m_byteArray;
- QFuture<QString> m_futureCalcSha1;
- QFuture<QString> m_futureCalcSha256;
};
}
diff --git a/kerfuffle/propertiesdialog.ui b/kerfuffle/propertiesdialog.ui
index 54451f3..c5b8e69 100644
--- a/kerfuffle/propertiesdialog.ui
+++ b/kerfuffle/propertiesdialog.ui
@@ -38,6 +38,13 @@
</property>
</widget>
</item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="lblArchiveName">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
@@ -94,98 +101,98 @@
</property>
</widget>
</item>
- <item row="5" column="0">
+ <item row="6" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Has comment:</string>
</property>
</widget>
</item>
- <item row="5" column="1">
+ <item row="6" column="1">
<widget class="QLabel" name="lblHasComment">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="6" column="0">
+ <item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
- <string>Number of files:</string>
+ <string>Number of entries:</string>
</property>
</widget>
</item>
- <item row="6" column="1">
- <widget class="QLabel" name="lblNumberOfFiles">
+ <item row="7" column="1">
+ <widget class="QLabel" name="lblNumberOfEntries">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="7" column="0">
+ <item row="8" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Unpacked size:</string>
</property>
</widget>
</item>
- <item row="7" column="1">
+ <item row="8" column="1">
<widget class="QLabel" name="lblUnpackedSize">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="8" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Packed size:</string>
</property>
</widget>
</item>
- <item row="8" column="1">
+ <item row="9" column="1">
<widget class="QLabel" name="lblPackedSize">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="9" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Compression ratio:</string>
</property>
</widget>
</item>
- <item row="9" column="1">
+ <item row="10" column="1">
<widget class="QLabel" name="lblCompressionRatio">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Last modified:</string>
</property>
</widget>
</item>
- <item row="10" column="1">
+ <item row="11" column="1">
<widget class="QLabel" name="lblLastModified">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>MD5 hash:</string>
</property>
</widget>
</item>
- <item row="11" column="1">
+ <item row="12" column="1">
<widget class="QLabel" name="lblMD5">
<property name="text">
<string/>
@@ -198,14 +205,14 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="13" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>SHA-1 hash:</string>
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="13" column="1">
<widget class="QLabel" name="lblSHA1">
<property name="text">
<string/>
@@ -215,14 +222,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="14" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>SHA-256 hash:</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="lblSHA256">
<property name="text">
<string notr="true">nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn</string>
@@ -232,8 +239,15 @@
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QLabel" name="lblArchiveName">
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Multi-volume:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLabel" name="lblMultiVolume">
<property name="text">
<string/>
</property>
diff --git a/part/archivemodel.cpp b/part/archivemodel.cpp
index 85e5389..4bdc6d0 100644
--- a/part/archivemodel.cpp
+++ b/part/archivemodel.cpp
@@ -29,6 +29,7 @@
#include <kio/global.h>
#include <QDBusConnection>
+#include <QElapsedTimer>
#include <QMimeData>
#include <QRegularExpression>
#include <QUrl>
@@ -151,6 +152,8 @@ ArchiveModel::ArchiveModel(const QString &dbusPathName, QObject *parent)
: QAbstractItemModel(parent)
, m_rootEntry()
, m_dbusPathName(dbusPathName)
+ , m_numberOfFiles(0)
+ , m_numberOfFolders(0)
{
m_rootEntry.setProperty("isDirectory", true);
}
@@ -368,6 +371,7 @@ int ArchiveModel::rowCount(const QModelIndex &parent) const
int ArchiveModel::columnCount(const QModelIndex &parent) const
{
+ Q_UNUSED(parent)
return m_showColumns.size();
}
@@ -456,6 +460,13 @@ bool ArchiveModel::dropMimeData(const QMimeData * data, Qt::DropAction action, i
return false;
}
+ if (archive()->isReadOnly() ||
+ (archive()->encryptionType() != Archive::Unencrypted &&
+ archive()->password().isEmpty())) {
+ emit messageWidget(KMessageWidget::Error, i18n("Adding files is not supported for this archive."));
+ return false;
+ }
+
QStringList paths;
foreach(const QUrl &url, data->urls()) {
paths << url.toLocalFile();
@@ -672,16 +683,20 @@ void ArchiveModel::newEntry(Archive::Entry *receivedEntry, InsertBehaviour behav
void ArchiveModel::slotLoadingFinished(KJob *job)
{
- int i = 0;
- foreach(Archive::Entry *entry, m_newArchiveEntries) {
- newEntry(entry, DoNotNotifyViews);
- i++;
- }
- beginResetModel();
- endResetModel();
- m_newArchiveEntries.clear();
+ if (!job->error()) {
+ QElapsedTimer timer;
+ timer.start();
+ int i = 0;
+ foreach(Archive::Entry *entry, m_newArchiveEntries) {
+ newEntry(entry, DoNotNotifyViews);
+ i++;
+ }
+ beginResetModel();
+ endResetModel();
+ m_newArchiveEntries.clear();
- qCDebug(ARK) << "Added" << i << "entries to model";
+ qCDebug(ARK) << "Added" << i << "entries to model in" << timer.elapsed() << "ms";
+ }
emit loadingFinished(job);
}
@@ -985,3 +1000,52 @@ void ArchiveModel::slotCleanupEmptyDirs()
endRemoveRows();
}
}
+
+void ArchiveModel::countEntriesAndSize() {
+
+ // This function is used to count the number of folders/files and
+ // the total compressed size. This is needed for PropertiesDialog
+ // to update the corresponding values after adding/deleting files.
+
+ // When ArchiveModel has been properly fixed, this code can likely
+ // be removed.
+
+ m_numberOfFiles = 0;
+ m_numberOfFolders = 0;
+ m_uncompressedSize = 0;
+
+ QElapsedTimer timer;
+ timer.start();
+
+ traverseAndCountDirNode(&m_rootEntry);
+
+ qCDebug(ARK) << "Time to count entries and size:" << timer.elapsed() << "ms";
+}
+
+void ArchiveModel::traverseAndCountDirNode(Archive::Entry *dir)
+{
+ foreach(Archive::Entry *entry, dir->entries()) {
+ if (entry->isDir()) {
+ traverseAndCountDirNode(entry);
+ m_numberOfFolders++;
+ } else {
+ m_numberOfFiles++;
+ m_uncompressedSize += entry->property("size").toULongLong();
+ }
+ }
+}
+
+qulonglong ArchiveModel::numberOfFiles() const
+{
+ return m_numberOfFiles;
+}
+
+qulonglong ArchiveModel::numberOfFolders() const
+{
+ return m_numberOfFolders;
+}
+
+qulonglong ArchiveModel::uncompressedSize() const
+{
+ return m_uncompressedSize;
+}
diff --git a/part/archivemodel.h b/part/archivemodel.h
index fcca1ca..0290d8f 100644
--- a/part/archivemodel.h
+++ b/part/archivemodel.h
@@ -26,6 +26,7 @@
#include <QAbstractItemModel>
#include <QScopedPointer>
+#include <KMessageWidget>
#include <kjobtrackerinterface.h>
#include "kerfuffle/archiveentry.h"
@@ -85,6 +86,11 @@ public:
*/
void encryptArchive(const QString &password, bool encryptHeader);
+ void countEntriesAndSize();
+ qulonglong numberOfFiles() const;
+ qulonglong numberOfFolders() const;
+ qulonglong uncompressedSize() const;
+
/**
* Constructs a list of conflicting entries.
*
@@ -116,6 +122,7 @@ signals:
void extractionFinished(bool success);
void error(const QString& error, const QString& details);
void droppedFiles(const QStringList& files, const Archive::Entry*, const QString&);
+ void messageWidget(KMessageWidget::MessageType type, const QString& msg);
private slots:
void slotNewEntryFromSetArchive(Archive::Entry *entry);
@@ -149,6 +156,8 @@ private:
void insertEntry(Archive::Entry *entry, InsertBehaviour behaviour = NotifyViews);
void newEntry(Kerfuffle::Archive::Entry *receivedEntry, InsertBehaviour behaviour);
+ void traverseAndCountDirNode(Archive::Entry *dir);
+
QList<Kerfuffle::Archive::Entry*> m_newArchiveEntries; // holds entries from opening a new archive until it's totally open
QList<int> m_showColumns;
QScopedPointer<Kerfuffle::Archive> m_archive;
@@ -156,6 +165,10 @@ private:
QHash<QString, QIcon> m_entryIcons;
QString m_dbusPathName;
+
+ qulonglong m_numberOfFiles;
+ qulonglong m_numberOfFolders;
+ qulonglong m_uncompressedSize;
};
#endif // ARCHIVEMODEL_H
diff --git a/part/part.cpp b/part/part.cpp
index b6a9f59..24ccb9e 100644
--- a/part/part.cpp
+++ b/part/part.cpp
@@ -176,6 +176,8 @@ Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList& args)
this, static_cast<void (Part::*)(const QStringList&, const Archive::Entry*, const QString&)>(&Part::slotAddFiles));
connect(m_model, &ArchiveModel::error,
this, &Part::slotError);
+ connect(m_model, &ArchiveModel::messageWidget,
+ this, &Part::displayMsgWidget);
connect(this, &Part::busy,
this, &Part::setBusyGui);
@@ -191,7 +193,7 @@ Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList& args)
Part::~Part()
{
- qDeleteAll(m_tmpOpenDirList);
+ qDeleteAll(m_tmpExtractDirList);
// Only save splitterSizes if infopanel is visible,
// because we don't want to store zero size for infopanel.
@@ -322,7 +324,7 @@ void Part::setupActions()
// pass the OpenFileMode as argument to the slot.
m_signalMapper = new QSignalMapper;
- m_showInfoPanelAction = new KToggleAction(i18nc("@action:inmenu", "Show information panel"), this);
+ m_showInfoPanelAction = new KToggleAction(i18nc("@action:inmenu", "Show Information Panel"), this);
actionCollection()->addAction(QStringLiteral( "show-infopanel" ), m_showInfoPanelAction);
m_showInfoPanelAction->setChecked(ArkSettings::showInfoPanel());
connect(m_showInfoPanelAction, &QAction::triggered,
@@ -370,6 +372,7 @@ void Part::setupActions()
m_addFilesAction->setIcon(QIcon::fromTheme(QStringLiteral("archive-insert")));
m_addFilesAction->setText(i18n("Add &Files to..."));
m_addFilesAction->setToolTip(i18nc("@info:tooltip", "Click to add files to the archive"));
+ actionCollection()->setDefaultShortcut(m_addFilesAction, Qt::ALT + Qt::Key_A);
connect(m_addFilesAction, &QAction::triggered, this, static_cast<void (Part::*)()>(&Part::slotAddFiles));
m_renameFileAction = actionCollection()->addAction(QStringLiteral("rename"));
@@ -441,6 +444,25 @@ void Part::updateActions()
const Archive::Entry *entry = m_model->entryForIndex(m_view->selectionModel()->currentIndex());
int selectedEntriesCount = m_view->selectionModel()->selectedRows().count();
+ // We disable adding files if the archive is encrypted but the password is
+ // unknown (this happens when opening existing non-he password-protected
+ // archives). If we added files they would not get encrypted resulting in an
+ // archive with a mixture of encrypted and unencrypted files.
+ const bool isEncryptedButUnknownPassword = m_model->archive() &&
+ m_model->archive()->encryptionType() != Archive::Unencrypted &&
+ m_model->archive()->password().isEmpty();
+
+ if (isEncryptedButUnknownPassword) {
+ m_addFilesAction->setToolTip(xi18nc("@info:tooltip",
+ "Adding files to existing password-protected archives with no header-encryption is currently not supported."
+ "<nl/><nl/>Extract the files and create a new archive if you want to add files."));
+ m_testArchiveAction->setToolTip(xi18nc("@info:tooltip",
+ "Testing password-protected archives with no header-encryption is currently not supported."));
+ } else {
+ m_addFilesAction->setToolTip(i18nc("@info:tooltip", "Click to add files to the archive"));
+ m_testArchiveAction->setToolTip(i18nc("@info:tooltip", "Click to test the archive for integrity"));
+ }
+
// Figure out if entry size is larger than preview size limit.
const int maxPreviewSize = ArkSettings::previewFileSizeLimit() * 1024 * 1024;
const bool limit = ArkSettings::limitPreviewFileSize();
@@ -458,7 +480,8 @@ void Part::updateActions()
m_saveAsAction->setEnabled(!isBusy() &&
m_model->rowCount() > 0);
m_addFilesAction->setEnabled(!isBusy() &&
- isWritable);
+ isWritable &&
+ !isEncryptedButUnknownPassword);
m_deleteFilesAction->setEnabled(!isBusy() &&
isWritable &&
(selectedEntriesCount > 0));
@@ -504,7 +527,8 @@ void Part::updateActions()
bool supportsTesting = ArchiveFormat::fromMetadata(m_model->archive()->mimeType(), metadata).supportsTesting();
m_testArchiveAction->setEnabled(!isBusy() &&
- supportsTesting);
+ supportsTesting &&
+ !isEncryptedButUnknownPassword);
} else {
m_commentView->setReadOnly(true);
m_editCommentAction->setText(i18nc("@action:inmenu mutually exclusive with Edit &Comment", "Add &Comment"));
@@ -620,26 +644,26 @@ void Part::slotQuickExtractFiles(QAction *triggeredAction)
// #190507: triggeredAction->data.isNull() means it's the "Extract to..."
// action, and we do not want it to run here
if (!triggeredAction->data().isNull()) {
- const QString userDestination = triggeredAction->data().toString();
- qCDebug(ARK) << "Extract to user dest" << userDestination;
+ QString userDestination = triggeredAction->data().toString();
QString finalDestinationDirectory;
const QString detectedSubfolder = detectSubfolder();
qCDebug(ARK) << "Detected subfolder" << detectedSubfolder;
if (!isSingleFolderArchive()) {
- finalDestinationDirectory = userDestination +
- QDir::separator() + detectedSubfolder;
+ if (!userDestination.endsWith(QDir::separator())) {
+ userDestination.append(QDir::separator());
+ }
+ finalDestinationDirectory = userDestination + detectedSubfolder;
QDir(userDestination).mkdir(detectedSubfolder);
} else {
finalDestinationDirectory = userDestination;
}
- qCDebug(ARK) << "Extract to final dest" << finalDestinationDirectory;
+ qCDebug(ARK) << "Extracting to:" << finalDestinationDirectory;
Kerfuffle::ExtractionOptions options;
options[QStringLiteral("PreservePaths")] = true;
- QList<Archive::Entry*> files = filesAndRootNodesForIndexes(m_view->selectionModel()->selectedRows());
- ExtractJob *job = m_model->extractFiles(files, finalDestinationDirectory, options);
+ ExtractJob *job = m_model->extractFiles(filesAndRootNodesForIndexes(addChildren(m_view->selectionModel()->selectedRows())), finalDestinationDirectory, options);
registerJob(job);
connect(job, &KJob::result,
@@ -684,6 +708,10 @@ bool Part::openFile()
Q_ASSERT(archive->isValid());
+ if (arguments().metaData().contains(QStringLiteral("volumeSize"))) {
+ archive.data()->setMultiVolume(true);
+ }
+
// Plugin loaded successfully.
KJob *job = m_model->setArchive(archive.take());
if (job) {
@@ -940,7 +968,7 @@ void Part::slotOpenExtractedEntry(KJob *job)
// Since the user could modify the file (unlike the Preview case),
// we'll need to manually delete the temp dir in the Part destructor.
- m_tmpOpenDirList << openJob->tempDir();
+ m_tmpExtractDirList << openJob->tempDir();
const QString fullName = openJob->validatedFilePath();
@@ -978,6 +1006,7 @@ void Part::slotPreviewExtractedEntry(KJob *job)
PreviewJob *previewJob = qobject_cast<PreviewJob*>(job);
Q_ASSERT(previewJob);
+ m_tmpExtractDirList << previewJob->tempDir();
ArkViewer::view(previewJob->validatedFilePath());
} else if (job->error() != KJob::KilledJobError) {
@@ -992,7 +1021,7 @@ void Part::slotWatchedFileModified(const QString& file)
// Find the relative path of the file within the archive.
QString relPath = file;
- foreach (QTemporaryDir *tmpDir, m_tmpOpenDirList) {
+ foreach (QTemporaryDir *tmpDir, m_tmpExtractDirList) {
relPath.remove(tmpDir->path()); //Remove tmpDir.
}
relPath = relPath.mid(1); //Remove leading slash.
@@ -1101,7 +1130,7 @@ void Part::slotShowExtractionDialog()
}
options[QStringLiteral("FollowExtractionDialogSettings")] = true;
- const QString destinationDirectory = dialog.data()->destinationDirectory().toDisplayString(QUrl::PreferLocalFile);
+ const QString destinationDirectory = dialog.data()->destinationDirectory().toLocalFile();
ExtractJob *job = m_model->extractFiles(files, destinationDirectory, options);
registerJob(job);
@@ -1310,6 +1339,9 @@ void Part::slotAddFiles()
if (arguments().metaData().contains(QStringLiteral("compressionLevel"))) {
opts[QStringLiteral("CompressionLevel")] = arguments().metaData()[QStringLiteral("compressionLevel")];
}
+ if (arguments().metaData().contains(QStringLiteral("volumeSize"))) {
+ opts[QStringLiteral("VolumeSize")] = arguments().metaData()[QStringLiteral("volumeSize")];
+ }
m_model->archive()->setCompressionOptions(opts);
} else {
opts = m_model->archive()->compressionOptions();
@@ -1527,6 +1559,17 @@ void Part::slotAddFilesDone(KJob* job)
} else {
// Hide the "archive will be created as soon as you add a file" message.
m_messageWidget->hide();
+
+ // For multi-volume archive, we need to re-open the archive after adding files
+ // because the name changes from e.g name.rar to name.part1.rar.
+ if (m_model->archive()->isMultiVolume()) {
+ qCDebug(ARK) << "Multi-volume archive detected, re-opening...";
+ KParts::OpenUrlArguments args = arguments();
+ args.metaData()[QStringLiteral("createNewArchive")] = QStringLiteral("false");
+ setArguments(args);
+
+ openUrl(QUrl::fromLocalFile(m_model->archive()->multiVolumeName()));
+ }
}
m_cutIndexes.clear();
m_model->filesToMove.clear();
@@ -1581,8 +1624,12 @@ void Part::slotDeleteFiles()
void Part::slotShowProperties()
{
+ m_model->countEntriesAndSize();
QPointer<Kerfuffle::PropertiesDialog> dialog(new Kerfuffle::PropertiesDialog(0,
- m_model->archive()));
+ m_model->archive(),
+ m_model->numberOfFiles(),
+ m_model->numberOfFolders(),
+ m_model->uncompressedSize()));
dialog.data()->show();
}
@@ -1653,11 +1700,11 @@ void Part::slotShowContextMenu()
void Part::displayMsgWidget(KMessageWidget::MessageType type, const QString& msg)
{
- // The widget could be already visible, so hide it.
- m_messageWidget->hide();
- m_messageWidget->setText(msg);
- m_messageWidget->setMessageType(type);
- m_messageWidget->animatedShow();
+ KMessageWidget *msgWidget = new KMessageWidget();
+ msgWidget->setText(msg);
+ msgWidget->setMessageType(type);
+ m_vlayout->insertWidget(0, msgWidget);
+ msgWidget->animatedShow();
}
} // namespace Ark
diff --git a/part/part.h b/part/part.h
index ce62810..9fa850a 100644
--- a/part/part.h
+++ b/part/part.h
@@ -156,6 +156,7 @@ private slots:
void slotAddComment();
void slotCommentChanged();
void slotTestArchive();
+ void displayMsgWidget(KMessageWidget::MessageType type, const QString& msg);
signals:
void busy();
@@ -172,7 +173,6 @@ private:
QList<Kerfuffle::Archive::Entry*> filesAndRootNodesForIndexes(const QModelIndexList& list) const;
QModelIndexList addChildren(const QModelIndexList &list) const;
void registerJob(KJob *job);
- void displayMsgWidget(KMessageWidget::MessageType type, const QString& msg);
ArchiveModel *m_model;
ArchiveView *m_view;
@@ -194,7 +194,7 @@ private:
KToggleAction *m_showInfoPanelAction;
InfoPanel *m_infoPanel;
QSplitter *m_splitter;
- QList<QTemporaryDir*> m_tmpOpenDirList;
+ QList<QTemporaryDir*> m_tmpExtractDirList;
bool m_busy;
bool m_archiveIsLoaded;
OpenFileMode m_openFileMode;
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index db1eb54..d821217 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -1,3 +1,10 @@
+function(kerfuffle_add_plugin plugin)
+ kcoreaddons_add_plugin(${plugin}
+ SOURCES ${ARGN}
+ INSTALL_NAMESPACE kerfuffle)
+ target_link_libraries(${plugin} kerfuffle)
+endfunction()
+
add_subdirectory( libarchive )
add_subdirectory( clirarplugin )
add_subdirectory( cli7zplugin )
diff --git a/plugins/cli7zplugin/CMakeLists.txt b/plugins/cli7zplugin/CMakeLists.txt
index 3ab6271..0b127e4 100644
--- a/plugins/cli7zplugin/CMakeLists.txt
+++ b/plugins/cli7zplugin/CMakeLists.txt
@@ -9,7 +9,6 @@ ecm_qt_declare_logging_category(kerfuffle_cli7z_SRCS
IDENTIFIER ARK
CATEGORY_NAME ark.cli7z)
-
# NOTE: the first double-quotes of the first mime and the last
# double-quotes of the last mime must NOT be escaped.
set(SUPPORTED_MIMETYPES
@@ -20,13 +19,7 @@ configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_cli7z.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_cli7z.json)
-add_library(kerfuffle_cli7z MODULE ${kerfuffle_cli7z_SRCS})
-
-target_link_libraries(kerfuffle_cli7z kerfuffle)
-
-########### install files ###############
-
-install(TARGETS kerfuffle_cli7z DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
+kerfuffle_add_plugin(kerfuffle_cli7z ${kerfuffle_cli7z_SRCS})
set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLI7Z_MIMETYPES}" PARENT_SCOPE)
set(INSTALLED_KERFUFFLE_PLUGINS "${INSTALLED_KERFUFFLE_PLUGINS}kerfuffle_cli7z;" PARENT_SCOPE)
diff --git a/plugins/cli7zplugin/cliplugin.cpp b/plugins/cli7zplugin/cliplugin.cpp
index 51837af..ae385ac 100644
--- a/plugins/cli7zplugin/cliplugin.cpp
+++ b/plugins/cli7zplugin/cliplugin.cpp
@@ -54,6 +54,7 @@ void CliPlugin::resetParsing()
{
m_parseState = ParseStateTitle;
m_comment.clear();
+ m_numberOfVolumes = 0;
}
ParameterList CliPlugin::parameterList() const
@@ -82,6 +83,7 @@ ParameterList CliPlugin::parameterList() const
<< QStringLiteral("$Archive")
<< QStringLiteral("$PasswordSwitch")
<< QStringLiteral("$CompressionLevelSwitch")
+ << QStringLiteral("$MultiVolumeSwitch")
<< QStringLiteral("$Files");
p[MoveArgs] = QStringList() << QStringLiteral("rn")
<< QStringLiteral("$PasswordSwitch")
@@ -92,7 +94,8 @@ ParameterList CliPlugin::parameterList() const
<< QStringLiteral("$Archive")
<< QStringLiteral("$Files");
p[TestArgs] = QStringList() << QStringLiteral("t")
- << QStringLiteral("$Archive");
+ << QStringLiteral("$Archive")
+ << QStringLiteral("$PasswordSwitch");
p[TestPassedPattern] = QStringLiteral("^Everything is Ok$");
p[FileExistsExpression] = QStringList()
@@ -106,10 +109,12 @@ ParameterList CliPlugin::parameterList() const
<< QStringLiteral("S") //autoskip
<< QStringLiteral("Q"); //cancel
p[PasswordPromptPattern] = QStringLiteral("Enter password \\(will not be echoed\\)");
- p[ExtractionFailedPatterns] = QStringList() << QStringLiteral("ERROR: E_FAIL");
+ p[ExtractionFailedPatterns] = QStringList() << QStringLiteral("ERROR: E_FAIL") << QStringLiteral("Open ERROR: Can not open the file as \\[7z\\] archive");
p[CorruptArchivePatterns] = QStringList() << QStringLiteral("Unexpected end of archive")
<< QStringLiteral("Headers Error");
p[DiskFullPatterns] = QStringList() << QStringLiteral("No space left on device");
+ p[MultiVolumeSwitch] = QStringLiteral("-v$VolumeSizek");
+ p[MultiVolumeSuffix] = QStringList() << QStringLiteral("$Suffix.001");
}
return p;
@@ -166,12 +171,15 @@ bool CliPlugin::readListLine(const QString& line)
m_archiveType = ArchiveTypeZip;
} else if (type == QLatin1String("Rar")) {
m_archiveType = ArchiveTypeRar;
+ } else if (type == QLatin1String("Split")) {
+ setMultiVolume(true);
} else {
// Should not happen
qCWarning(ARK) << "Unsupported archive type";
return false;
}
-
+ } else if (line.startsWith(QStringLiteral("Volumes = "))) {
+ m_numberOfVolumes = line.section(QLatin1Char('='), 1).trimmed().toInt();
} else if (rxComment.match(line).hasMatch()) {
m_parseState = ParseStateComment;
m_comment.append(line.section(QLatin1Char('='), 1) + QLatin1Char('\n'));
diff --git a/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake b/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake
index 605e5e0..74fba26 100644
--- a/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake
+++ b/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake
@@ -47,13 +47,15 @@
"CompressionLevelMax": 9,
"CompressionLevelMin": 0,
"SupportsTesting": true,
- "HeaderEncryption": true
+ "HeaderEncryption": true,
+ "SupportsMultiVolume": true
},
"application/zip": {
"CompressionLevelDefault": 5,
"CompressionLevelMax": 9,
"CompressionLevelMin": 0,
"SupportsTesting": true,
- "Encryption": true
+ "Encryption": true,
+ "SupportsMultiVolume": true
}
}
diff --git a/plugins/cliplugin-example/CMakeLists.txt b/plugins/cliplugin-example/CMakeLists.txt
index 0175ddf..2aa5c1f 100644
--- a/plugins/cliplugin-example/CMakeLists.txt
+++ b/plugins/cliplugin-example/CMakeLists.txt
@@ -5,8 +5,4 @@ ecm_qt_declare_logging_category(kerfuffle_cli_SRCS
IDENTIFIER ARK
CATEGORY_NAME ark.cliexample)
-add_library(kerfuffle_cli ${kerfuffle_cli_SRCS})
-target_link_libraries(kerfuffle_cli KF5::KIOCore kerfuffle)
-
-install(TARGETS kerfuffle_cli DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
-
+kerfuffle_add_plugin(kerfuffle_cli ${kerfuffle_cli_SRCS})
diff --git a/plugins/clirarplugin/CMakeLists.txt b/plugins/clirarplugin/CMakeLists.txt
index 3dfeff8..4bad5a5 100644
--- a/plugins/clirarplugin/CMakeLists.txt
+++ b/plugins/clirarplugin/CMakeLists.txt
@@ -9,18 +9,13 @@ ecm_qt_declare_logging_category(kerfuffle_clirar_SRCS
IDENTIFIER ARK
CATEGORY_NAME ark.clirar)
-
set(SUPPORTED_MIMETYPES "application/x-rar")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_clirar.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clirar.json)
-add_library(kerfuffle_clirar MODULE ${kerfuffle_clirar_SRCS})
-
-target_link_libraries(kerfuffle_clirar kerfuffle)
-
-install(TARGETS kerfuffle_clirar DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
+kerfuffle_add_plugin(kerfuffle_clirar ${kerfuffle_clirar_SRCS})
set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLIRAR_MIMETYPES}" PARENT_SCOPE)
set(INSTALLED_KERFUFFLE_PLUGINS "${INSTALLED_KERFUFFLE_PLUGINS}kerfuffle_clirar;" PARENT_SCOPE)
diff --git a/plugins/clirarplugin/cliplugin.cpp b/plugins/clirarplugin/cliplugin.cpp
index d701a8b..5e132ca 100644
--- a/plugins/clirarplugin/cliplugin.cpp
+++ b/plugins/clirarplugin/cliplugin.cpp
@@ -38,7 +38,6 @@ CliPlugin::CliPlugin(QObject *parent, const QVariantList& args)
, m_parseState(ParseStateTitle)
, m_isUnrar5(false)
, m_isPasswordProtected(false)
- , m_isMultiVolume(false)
, m_isSolid(false)
, m_remainingIgnoreLines(1) //The first line of UNRAR output is empty.
, m_linesComment(0)
@@ -58,6 +57,7 @@ void CliPlugin::resetParsing()
m_parseState = ParseStateTitle;
m_remainingIgnoreLines = 1;
m_comment.clear();
+ m_numberOfVolumes = 0;
}
ParameterList CliPlugin::parameterList() const
@@ -84,6 +84,7 @@ ParameterList CliPlugin::parameterList() const
p[PasswordSwitch] = QStringList() << QStringLiteral( "-p$Password" );
p[PasswordHeaderSwitch] = QStringList() << QStringLiteral("-hp$Password");
p[CompressionLevelSwitch] = QStringLiteral("-m$CompressionLevel");
+ p[MultiVolumeSwitch] = QStringLiteral("-v$VolumeSizek");
p[DeleteArgs] = QStringList() << QStringLiteral( "d" )
<< QStringLiteral( "$PasswordSwitch" )
<< QStringLiteral( "$Archive" )
@@ -101,6 +102,7 @@ ParameterList CliPlugin::parameterList() const
<< QStringLiteral( "$Archive" )
<< QStringLiteral("$PasswordSwitch")
<< QStringLiteral("$CompressionLevelSwitch")
+ << QStringLiteral("$MultiVolumeSwitch")
<< QStringLiteral( "$Files" );
p[MoveArgs] = QStringList() << QStringLiteral( "rn" )
<< QStringLiteral( "$PasswordSwitch" )
@@ -118,8 +120,13 @@ ParameterList CliPlugin::parameterList() const
<< QStringLiteral("$Archive");
p[CommentSwitch] = QStringLiteral("-z$CommentFile");
p[TestArgs] = QStringList() << QStringLiteral("t")
- << QStringLiteral("$Archive");
+ << QStringLiteral("$Archive")
+ << QStringLiteral("$PasswordSwitch");
p[TestPassedPattern] = QStringLiteral("^All OK$");
+ // rar will sometimes create multi-volume archives where first volume is
+ // called name.part1.rar and other times name.part01.rar.
+ p[MultiVolumeSuffix] = QStringList() << QStringLiteral("part01.$Suffix")
+ << QStringLiteral("part1.$Suffix");
}
return p;
@@ -198,9 +205,12 @@ void CliPlugin::handleUnrar5Line(const QString &line) {
// "Details: " indicates end of header.
if (line.startsWith(QStringLiteral("Details: "))) {
ignoreLines(1, ParseStateEntryDetails);
- if (line.contains(QLatin1String("volume")) && !m_isMultiVolume) {
- m_isMultiVolume = true;
- qCDebug(ARK) << "Multi-volume archive detected";
+ if (line.contains(QLatin1String("volume"))) {
+ m_numberOfVolumes++;
+ if (!isMultiVolume()) {
+ setMultiVolume(true);
+ qCDebug(ARK) << "Multi-volume archive detected";
+ }
}
if (line.contains(QLatin1String("solid")) && !m_isSolid) {
m_isSolid = true;
@@ -299,9 +309,12 @@ void CliPlugin::handleUnrar4Line(const QString &line) {
if (rxCommentEnd.match(line).hasMatch()) {
- if (line.startsWith(QLatin1String("Volume")) && !m_isMultiVolume) {
- m_isMultiVolume = true;
- qCDebug(ARK) << "Multi-volume archive detected";
+ if (line.startsWith(QLatin1String("Volume "))) {
+ m_numberOfVolumes++;
+ if (!isMultiVolume()) {
+ setMultiVolume(true);
+ qCDebug(ARK) << "Multi-volume archive detected";
+ }
}
if (line.startsWith(QLatin1String("Solid archive")) && !m_isSolid) {
m_isSolid = true;
@@ -329,6 +342,8 @@ void CliPlugin::handleUnrar4Line(const QString &line) {
// Horizontal line indicates end of header.
if (line.startsWith(QStringLiteral("--------------------"))) {
m_parseState = ParseStateEntryFileName;
+ } else if (line.startsWith(QLatin1String("Volume "))) {
+ m_numberOfVolumes++;
}
return;
}
diff --git a/plugins/clirarplugin/cliplugin.h b/plugins/clirarplugin/cliplugin.h
index dc7dbfa..dc7b48c 100644
--- a/plugins/clirarplugin/cliplugin.h
+++ b/plugins/clirarplugin/cliplugin.h
@@ -60,7 +60,6 @@ private:
bool m_isUnrar5;
bool m_isPasswordProtected;
- bool m_isMultiVolume;
bool m_isSolid;
int m_remainingIgnoreLines;
diff --git a/plugins/clirarplugin/kerfuffle_clirar.json.cmake b/plugins/clirarplugin/kerfuffle_clirar.json.cmake
index d201a73..96c993d 100644
--- a/plugins/clirarplugin/kerfuffle_clirar.json.cmake
+++ b/plugins/clirarplugin/kerfuffle_clirar.json.cmake
@@ -51,6 +51,7 @@
"CompressionLevelMin": 0,
"SupportsWriteComment": true,
"SupportsTesting": true,
- "HeaderEncryption": true
+ "HeaderEncryption": true,
+ "SupportsMultiVolume": true
}
}
diff --git a/plugins/cliunarchiverplugin/CMakeLists.txt b/plugins/cliunarchiverplugin/CMakeLists.txt
index 6b90ddb..4ee1f6a 100644
--- a/plugins/cliunarchiverplugin/CMakeLists.txt
+++ b/plugins/cliunarchiverplugin/CMakeLists.txt
@@ -9,18 +9,13 @@ ecm_qt_declare_logging_category(kerfuffle_cliunarchiver_SRCS
IDENTIFIER ARK
CATEGORY_NAME ark.cliunarchiver)
-
set(SUPPORTED_MIMETYPES "application/x-rar")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_cliunarchiver.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_cliunarchiver.json)
-add_library(kerfuffle_cliunarchiver MODULE ${kerfuffle_cliunarchiver_SRCS})
-
-target_link_libraries(kerfuffle_cliunarchiver kerfuffle)
-
-install(TARGETS kerfuffle_cliunarchiver DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
+kerfuffle_add_plugin(kerfuffle_cliunarchiver ${kerfuffle_cliunarchiver_SRCS})
set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLIUNARCHIVER_MIMETYPES}"
PARENT_SCOPE)
diff --git a/plugins/cliunarchiverplugin/cliplugin.cpp b/plugins/cliunarchiverplugin/cliplugin.cpp
index 3fe0c31..a973419 100644
--- a/plugins/cliunarchiverplugin/cliplugin.cpp
+++ b/plugins/cliunarchiverplugin/cliplugin.cpp
@@ -21,12 +21,14 @@
*/
#include "cliplugin.h"
+#include "queries.h"
#include <QJsonArray>
#include <QJsonParseError>
#include <KLocalizedString>
#include <KPluginFactory>
+#include <KPtyProcess>
using namespace Kerfuffle;
@@ -49,31 +51,7 @@ bool CliPlugin::list()
m_operationMode = List;
const auto args = substituteListVariables(m_param.value(ListArgs).toStringList(), password());
-
- if (!runProcess(m_param.value(ListProgram).toStringList(), args)) {
- return false;
- }
-
- if (!password().isEmpty()) {
-
- // lsar -json exits with error code 1 if the archive is header-encrypted and the password is wrong.
- if (m_exitCode == 1) {
- qCWarning(ARK) << "Wrong password, list() aborted";
- emit error(i18n("Wrong password."));
- emit finished(false);
- killProcess();
- setPassword(QString());
- return false;
- }
-
- // lsar -json exits with error code 2 if the archive is header-encrypted and no password is given as argument.
- // At this point we have already asked a password to the user, so we can just list() again.
- if (m_exitCode == 2) {
- return CliPlugin::list();
- }
- }
-
- return true;
+ return runProcess(m_param.value(ListProgram).toStringList(), args);
}
bool CliPlugin::extractFiles(const QList<Archive::Entry*> &files, const QString &destinationDirectory, const ExtractionOptions &options)
@@ -96,6 +74,7 @@ bool CliPlugin::extractFiles(const QList<Archive::Entry*> &files, const QString
void CliPlugin::resetParsing()
{
m_jsonOutput.clear();
+ m_numberOfVolumes = 0;
}
ParameterList CliPlugin::parameterList() const
@@ -161,14 +140,84 @@ void CliPlugin::cacheParameterList()
Q_ASSERT(m_param.contains(ListProgram));
}
-void CliPlugin::handleLine(const QString& line)
+bool CliPlugin::handleLine(const QString& line)
{
// Collect the json output line by line.
if (m_operationMode == List) {
m_jsonOutput += line + QLatin1Char('\n');
}
- CliInterface::handleLine(line);
+ // TODO: is this check really needed?
+ if (m_operationMode == Copy) {
+ if (checkForErrorMessage(line, ExtractionFailedPatterns)) {
+ qCWarning(ARK) << "Error in extraction:" << line;
+ emit error(i18n("Extraction failed because of an unexpected error."));
+ return false;
+ }
+ }
+
+ if (m_operationMode == List) {
+ // This can only be an header-encrypted archive.
+ if (checkForPasswordPromptMessage(line)) {
+ qCDebug(ARK) << "Detected header-encrypted RAR archive";
+
+ Kerfuffle::PasswordNeededQuery query(filename());
+ emit userQuery(&query);
+ query.waitForResponse();
+
+ if (query.responseCancelled()) {
+ emit cancelled();
+ // Process is gone, so we emit finished() manually and we return true.
+ emit finished(false);
+ return true;
+ }
+
+ setPassword(query.password());
+ CliPlugin::list();
+ }
+ }
+
+ return true;
+}
+
+void CliPlugin::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ qCDebug(ARK) << "Process finished, exitcode:" << exitCode << "exitstatus:" << exitStatus;
+
+ if (m_process) {
+ //handle all the remaining data in the process
+ readStdout(true);
+
+ delete m_process;
+ m_process = Q_NULLPTR;
+ }
+
+ // #193908 - #222392
+ // Don't emit finished() if the job was killed quietly.
+ if (m_abortingOperation) {
+ return;
+ }
+
+ if (!password().isEmpty()) {
+
+ // lsar -json exits with error code 1 if the archive is header-encrypted and the password is wrong.
+ if (exitCode == 1) {
+ qCWarning(ARK) << "Wrong password, list() aborted";
+ emit error(i18n("Wrong password."));
+ emit finished(false);
+ setPassword(QString());
+ return;
+ }
+ }
+
+ // lsar -json exits with error code 2 if the archive is header-encrypted and no password is given as argument.
+ // At this point we are asking a password to the user and we are going to list() again after we get one.
+ // This means that we cannot emit finished here.
+ if (exitCode == 2) {
+ return;
+ }
+
+ emit finished(true);
}
void CliPlugin::readJsonOutput()
@@ -182,6 +231,15 @@ void CliPlugin::readJsonOutput()
}
const QJsonObject json = jsonDoc.object();
+
+ const QJsonObject properties = json.value(QStringLiteral("lsarProperties")).toObject();
+ const QJsonArray volumes = properties.value(QStringLiteral("XADVolumes")).toArray();
+ if (volumes.count() > 1) {
+ qCDebug(ARK) << "Detected multivolume archive";
+ m_numberOfVolumes = volumes.count();
+ setMultiVolume(true);
+ }
+
const QJsonArray entries = json.value(QStringLiteral("lsarContents")).toArray();
foreach (const QJsonValue& value, entries) {
diff --git a/plugins/cliunarchiverplugin/cliplugin.h b/plugins/cliunarchiverplugin/cliplugin.h
index e106679..adf8a50 100644
--- a/plugins/cliunarchiverplugin/cliplugin.h
+++ b/plugins/cliunarchiverplugin/cliplugin.h
@@ -51,7 +51,10 @@ protected slots:
protected:
void cacheParameterList() Q_DECL_OVERRIDE;
- void handleLine(const QString& line) Q_DECL_OVERRIDE;
+ bool handleLine(const QString& line) Q_DECL_OVERRIDE;
+
+private slots:
+ void processFinished(int exitCode, QProcess::ExitStatus exitStatus) Q_DECL_OVERRIDE;
private:
diff --git a/plugins/clizipplugin/CMakeLists.txt b/plugins/clizipplugin/CMakeLists.txt
index 8644248..a2c5eec 100644
--- a/plugins/clizipplugin/CMakeLists.txt
+++ b/plugins/clizipplugin/CMakeLists.txt
@@ -19,11 +19,7 @@ configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_clizip.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clizip.json)
-add_library(kerfuffle_clizip MODULE ${kerfuffle_clizip_SRCS})
-
-target_link_libraries(kerfuffle_clizip kerfuffle )
-
-install(TARGETS kerfuffle_clizip DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
+kerfuffle_add_plugin(kerfuffle_clizip ${kerfuffle_clizip_SRCS})
set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLIZIP_MIMETYPES}" PARENT_SCOPE)
set(INSTALLED_KERFUFFLE_PLUGINS "${INSTALLED_KERFUFFLE_PLUGINS}kerfuffle_clizip;" PARENT_SCOPE)
diff --git a/plugins/clizipplugin/cliplugin.cpp b/plugins/clizipplugin/cliplugin.cpp
index 83265d1..f4f09f0 100644
--- a/plugins/clizipplugin/cliplugin.cpp
+++ b/plugins/clizipplugin/cliplugin.cpp
@@ -125,7 +125,8 @@ ParameterList CliPlugin::parameterList() const
p[DiskFullPatterns] = QStringList() << QStringLiteral("write error \\(disk full\\?\\)")
<< QStringLiteral("No space left on device");
p[TestArgs] = QStringList() << QStringLiteral("-t")
- << QStringLiteral("$Archive");
+ << QStringLiteral("$Archive")
+ << QStringLiteral("$PasswordSwitch");
p[TestPassedPattern] = QStringLiteral("^No errors detected in compressed data of ");
}
return p;
diff --git a/plugins/libarchive/CMakeLists.txt b/plugins/libarchive/CMakeLists.txt
index 7411439..9585c43 100644
--- a/plugins/libarchive/CMakeLists.txt
+++ b/plugins/libarchive/CMakeLists.txt
@@ -4,7 +4,7 @@ include_directories(${LibArchive_INCLUDE_DIRS})
set(SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES "application/x-tar;application/x-compressed-tar;application/x-bzip-compressed-tar;application/x-tarz;application/x-xz-compressed-tar;")
set(SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES "${SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES}application/x-lzma-compressed-tar;application/x-lzip-compressed-tar;application/x-tzo;application/x-lrzip-compressed-tar;")
set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "application/vnd.debian.binary-package;application/x-deb;application/x-cd-image;application/x-bcpio;application/x-cpio;application/x-cpio-compressed;application/x-sv4cpio;application/x-sv4crc;")
-set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}application/x-rpm;application/x-source-rpm;application/vnd.ms-cab-compressed;application/x-xar;")
+set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}application/x-rpm;application/x-source-rpm;application/vnd.ms-cab-compressed;application/x-xar;application/x-iso9660-appimage;")
if(LibArchive_VERSION VERSION_EQUAL "3.2.0" OR
LibArchive_VERSION VERSION_GREATER "3.2.0")
@@ -36,7 +36,8 @@ set(SUPPORTED_READONLY_MIMETYPES
\"application/x-source-rpm\",
\"application/vnd.debian.binary-package\",
\"application/vnd.ms-cab-compressed\",
- \"application/x-xar")
+ \"application/x-xar\",
+ \"application/x-iso9660-appimage")
# NOTE: the first double-quotes of the first mime and the last
# double-quotes of the last mime must NOT be escaped.
@@ -66,21 +67,18 @@ configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libarchive.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libarchive.json)
-add_library(kerfuffle_libarchive_readonly MODULE ${kerfuffle_libarchive_readonly_SRCS})
-add_library(kerfuffle_libarchive MODULE ${kerfuffle_libarchive_readwrite_SRCS})
+kerfuffle_add_plugin(kerfuffle_libarchive_readonly ${kerfuffle_libarchive_readonly_SRCS})
+kerfuffle_add_plugin(kerfuffle_libarchive ${kerfuffle_libarchive_readwrite_SRCS})
if(LibArchive_VERSION VERSION_EQUAL "3.2.0" OR
LibArchive_VERSION VERSION_GREATER "3.2.0")
target_compile_definitions(kerfuffle_libarchive PRIVATE -DHAVE_LIBARCHIVE_3_2_0)
endif()
-target_link_libraries(kerfuffle_libarchive_readonly KF5::KIOCore ${LibArchive_LIBRARIES} kerfuffle)
-target_link_libraries(kerfuffle_libarchive KF5::KIOCore ${LibArchive_LIBRARIES} kerfuffle)
+target_link_libraries(kerfuffle_libarchive_readonly ${LibArchive_LIBRARIES})
+target_link_libraries(kerfuffle_libarchive ${LibArchive_LIBRARIES})
-install(TARGETS kerfuffle_libarchive_readonly DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
set(INSTALLED_LIBARCHIVE_PLUGINS "${INSTALLED_LIBARCHIVE_PLUGINS}kerfuffle_libarchive_readonly;")
-
-install(TARGETS kerfuffle_libarchive DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
set(INSTALLED_LIBARCHIVE_PLUGINS "${INSTALLED_LIBARCHIVE_PLUGINS}kerfuffle_libarchive;")
set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES}${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}" PARENT_SCOPE)
diff --git a/plugins/libarchive/kerfuffle_libarchive.json.cmake b/plugins/libarchive/kerfuffle_libarchive.json.cmake
index fd56a27..f78f034 100644
--- a/plugins/libarchive/kerfuffle_libarchive.json.cmake
+++ b/plugins/libarchive/kerfuffle_libarchive.json.cmake
@@ -70,5 +70,10 @@
"CompressionLevelDefault": 6,
"CompressionLevelMax": 9,
"CompressionLevelMin": 0
+ },
+ "application/x-lz4-compressed-tar": {
+ "CompressionLevelDefault": 1,
+ "CompressionLevelMax": 9,
+ "CompressionLevelMin": 1
}
}
diff --git a/plugins/libsinglefileplugin/CMakeLists.txt b/plugins/libsinglefileplugin/CMakeLists.txt
index 9cfc4f3..7e22b84 100644
--- a/plugins/libsinglefileplugin/CMakeLists.txt
+++ b/plugins/libsinglefileplugin/CMakeLists.txt
@@ -23,8 +23,8 @@ set_package_properties(ZLIB PROPERTIES
PURPOSE "Required for .gz format support in Ark")
if (ZLIB_FOUND)
- set(kerfuffle_libgz_SRCS gzplugin.cpp ${kerfuffle_singlefile_SRCS})
- set(SUPPORTED_LIBSINGLEFILE_MIMETYPES "${SUPPORTED_LIBSINGLEFILE_MIMETYPES}application/gzip;")
+ set(kerfuffle_libgz_SRCS gzplugin.cpp ${kerfuffle_singlefile_SRCS})
+ set(SUPPORTED_LIBSINGLEFILE_MIMETYPES "${SUPPORTED_LIBSINGLEFILE_MIMETYPES}application/gzip;")
set(SUPPORTED_MIMETYPES "application/gzip")
@@ -32,12 +32,10 @@ if (ZLIB_FOUND)
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libgz.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libgz.json)
- add_library(kerfuffle_libgz MODULE ${kerfuffle_libgz_SRCS})
+ kerfuffle_add_plugin(kerfuffle_libgz ${kerfuffle_libgz_SRCS})
+ target_link_libraries(kerfuffle_libgz KF5::Archive)
- target_link_libraries(kerfuffle_libgz KF5::Archive KF5::KIOCore kerfuffle)
-
- install(TARGETS kerfuffle_libgz DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
- set(INSTALLED_LIBSINGLEFILE_PLUGINS "${INSTALLED_LIBSINGLEFILE_PLUGINS}kerfuffle_libgz;")
+ set(INSTALLED_LIBSINGLEFILE_PLUGINS "${INSTALLED_LIBSINGLEFILE_PLUGINS}kerfuffle_libgz;")
endif (ZLIB_FOUND)
#
@@ -59,12 +57,10 @@ if (BZIP2_FOUND)
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libbz2.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libbz2.json)
- add_library(kerfuffle_libbz2 MODULE ${kerfuffle_libbz2_SRCS})
-
- target_link_libraries(kerfuffle_libbz2 KF5::Archive KF5::KIOCore kerfuffle)
+ kerfuffle_add_plugin(kerfuffle_libbz2 ${kerfuffle_libbz2_SRCS})
+ target_link_libraries(kerfuffle_libbz2 KF5::Archive)
- install(TARGETS kerfuffle_libbz2 DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
- set(INSTALLED_LIBSINGLEFILE_PLUGINS "${INSTALLED_LIBSINGLEFILE_PLUGINS}kerfuffle_libbz2;")
+ set(INSTALLED_LIBSINGLEFILE_PLUGINS "${INSTALLED_LIBSINGLEFILE_PLUGINS}kerfuffle_libbz2;")
endif (BZIP2_FOUND)
#
@@ -90,12 +86,10 @@ if (LIBLZMA_FOUND)
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libxz.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libxz.json)
- add_library(kerfuffle_libxz MODULE ${kerfuffle_libxz_SRCS})
-
- target_link_libraries(kerfuffle_libxz KF5::Archive KF5::KIOCore kerfuffle )
+ kerfuffle_add_plugin(kerfuffle_libxz ${kerfuffle_libxz_SRCS})
+ target_link_libraries(kerfuffle_libxz KF5::Archive)
- install(TARGETS kerfuffle_libxz DESTINATION ${KDE_INSTALL_PLUGINDIR}/kerfuffle)
- set(INSTALLED_LIBSINGLEFILE_PLUGINS "${INSTALLED_LIBSINGLEFILE_PLUGINS}kerfuffle_libxz;")
+ set(INSTALLED_LIBSINGLEFILE_PLUGINS "${INSTALLED_LIBSINGLEFILE_PLUGINS}kerfuffle_libxz;")
endif (LIBLZMA_FOUND)
set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_LIBSINGLEFILE_MIMETYPES}" PARENT_SCOPE)