Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip]Karchive #2569

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
[submodule "flatpak/shared-modules"]
path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git
[submodule "libraries/karchive"]
path = libraries/karchive
url = https://invent.kde.org/frameworks/karchive.git
47 changes: 46 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,15 @@ endif()

option(BUILD_TESTING "Build the testing tree." ON)

find_package(ECM QUIET NO_MODULE)
# find_package(ECM QUIET NO_MODULE)
if(NOT ECM_FOUND)
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
execute_process(
COMMAND git -C ${CMAKE_SOURCE_DIR}/libraries/extra-cmake-modules checkout bbcbaff78283270c2beee69afd8d5b91da854af8
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE result
)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/CMakeLists.txt")
message(STATUS "Using bundled ECM")
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/modules;${CMAKE_MODULE_PATH}")
Expand Down Expand Up @@ -351,6 +358,13 @@ if(NOT Launcher_FORCE_BUNDLED_LIBS)

# Find cmark
find_package(cmark QUIET)

if(Launcher_QT_VERSION_MAJOR EQUAL 5)
find_package(KF5Archive)
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
find_package(KF6Archive)
endif()

endif()

include(ECMQtDeclareLoggingCategory)
Expand Down Expand Up @@ -551,6 +565,37 @@ else()
endif()
add_subdirectory(libraries/qdcss) # css parser

if(Launcher_QT_VERSION_MAJOR EQUAL 5)
if(NOT KF5Archive_FOUND)
execute_process(
COMMAND git -C ${CMAKE_SOURCE_DIR}/libraries/karchive checkout eba2cb37f128733561c9a03f0230e7836c76a045
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE result
)
message(STATUS "Using bundled karchive")
add_subdirectory(libraries/karchive)
else()
message(STATUS "Using system karchive")
endif()
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
if(NOT KF6Archive_FOUND)
message(STATUS "Using bundled karchive")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/CMakeLists.txt")
message(STATUS "Using bundled ECM")
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/modules;${CMAKE_MODULE_PATH}")
find_package(ECM 6.3.0 REQUIRED NO_MODULE)
else()
message(FATAL_ERROR
" Could not find ECM\n \n"
" Either install ECM using the system package manager or clone submodules\n"
" Submodules can be cloned with 'git submodule update --init --recursive'")
endif()
add_subdirectory(libraries/karchive)
else()
message(STATUS "Using system karchive")
endif()
endif()

############################### Built Artifacts ###############################

add_subdirectory(buildconfig)
Expand Down
1 change: 1 addition & 0 deletions launcher/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,7 @@ target_link_libraries(Launcher_logic
BuildConfig
Qt${QT_VERSION_MAJOR}::Widgets
ghcFilesystem::ghc_filesystem
KF6::Archive
)

if (UNIX AND NOT CYGWIN AND NOT APPLE)
Expand Down
2 changes: 1 addition & 1 deletion launcher/LaunchController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ void LaunchController::login()
if ((m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) ||
m_accountToUse->shouldRefresh()) {
// Force account refresh on the account used to launch the instance updating the AccountState
// only on first try and if it is not meant to be offline
// only on first try and if it is not meant to be offline
auto accounts = APPLICATION->accounts();
accounts->requestRefresh(m_accountToUse->internalId());
}
Expand Down
161 changes: 161 additions & 0 deletions launcher/MMCZip.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
#endif
#include "tasks/Task.h"

#include <KArchive>
#include <KTar>
#include <KZip>
#include <QIODevice>
#include <QStack>
namespace MMCZip {
using FilterFunction = std::function<bool(const QString&)>;

Expand Down Expand Up @@ -235,5 +240,161 @@ class ExtractZipTask : public Task {
QFuture<ZipResult> m_zip_future;
QFutureWatcher<ZipResult> m_zip_watcher;
};

class ExtractKArchive : public Task {
public:
ExtractKArchive(QString fileName, QDir outputDir, QString subdirectory = "")
: ExtractKArchive(newArchive(fileName), outputDir, subdirectory)
{}
ExtractKArchive(KArchive* a, QDir outputDir, QString subdirectory = "")
: m_archive(a), m_output_dir(outputDir), m_subdirectory(subdirectory)
{
setAbortable(true);
}
~ExtractKArchive()
{
if (m_archive) {
if (m_archive->isOpen()) {
m_archive->close();
}
delete m_archive;
}
}

static KArchive* newArchive(QString filename)
{
if (filename.endsWith(".zip") || filename.endsWith(".jar")) {
return new KZip(filename);
}
return new KTar(filename);
}

protected:
static bool sortByPosition(const KArchiveFile* file1, const KArchiveFile* file2) { return file1->position() < file2->position(); }
virtual void executeTask() override
{
m_needs_abort = false;
#define CAN_CONTINUE \
if (m_needs_abort) { \
emitAborted(); \
return; \
}
if (!m_archive->isOpen()) {
if (!m_archive->open(QIODevice::ReadOnly)) {
emitFailed(tr("unable to open file"));
return;
}
}
CAN_CONTINUE;
setStatus(tr("collect files"));
QDir root;
const QString destDir(m_output_dir.absolutePath()); // get directory path without any "." or ".."

QList<const KArchiveFile*> fileList;
QMap<qint64, QString> fileToDir;

// placeholders for iterated items
QStack<const KArchiveDirectory*> dirStack;
QStack<QString> dirNameStack;

auto kdir = m_archive->directory();
if (!m_subdirectory.isEmpty()) {
auto subDir = kdir->entry(m_subdirectory);
kdir = nullptr;
if (subDir && subDir->isDirectory()) {
kdir = dynamic_cast<const KArchiveDirectory*>(subDir);
}
if (!kdir) {
emitFailed(tr("unable to find subdirecotry"));
return;
}
}
CAN_CONTINUE;

dirStack.push(kdir); // init stack at current directory
dirNameStack.push(destDir); // ... with given path
do {
CAN_CONTINUE;
const KArchiveDirectory* curDir = dirStack.pop();

// extract only to specified folder if it is located within archive's extraction folder
// otherwise put file under root position in extraction folder
QString curDirName = dirNameStack.pop();
if (!QDir(curDirName).absolutePath().startsWith(destDir)) {
qWarning() << "Attempted export into folder" << curDirName << "which is outside of the extraction root folder" << destDir
<< "."
<< "Changing export of contained files to extraction root folder.";
curDirName = destDir;
}

if (!root.mkpath(curDirName)) {
emitFailed(tr("unable to create directory:%1").arg(curDirName));
return;
}

const QStringList dirEntries = curDir->entries();
for (QStringList::const_iterator it = dirEntries.begin(); it != dirEntries.end(); ++it) {
CAN_CONTINUE;
const KArchiveEntry* curEntry = curDir->entry(*it);
if (!curEntry->symLinkTarget().isEmpty()) {
QString linkName = curDirName + QLatin1Char('/') + curEntry->name();
// To create a valid link on Windows, linkName must have a .lnk file extension.
#ifdef Q_OS_WIN
if (!linkName.endsWith(QLatin1String(".lnk"))) {
linkName += QLatin1String(".lnk");
}
#endif
QFile symLinkTarget(curEntry->symLinkTarget());
if (!symLinkTarget.link(linkName)) {
qWarning() << "symlink(" << curEntry->symLinkTarget() << ',' << linkName << ") failed";
}
} else {
if (curEntry->isFile()) {
const KArchiveFile* curFile = dynamic_cast<const KArchiveFile*>(curEntry);
if (curFile) {
fileList.append(curFile);
fileToDir.insert(curFile->position(), curDirName);
}
}

if (curEntry->isDirectory()) {
const KArchiveDirectory* ad = dynamic_cast<const KArchiveDirectory*>(curEntry);
if (ad) {
dirStack.push(ad);
dirNameStack.push(curDirName + QLatin1Char('/') + curEntry->name());
}
}
}
}
} while (!dirStack.isEmpty());

std::sort(fileList.begin(), fileList.end(), sortByPosition); // sort on d->pos, so we have a linear access
setStatus(tr("extract files"));

setProgress(0, fileList.size());
for (auto f : fileList) {
CAN_CONTINUE;
setProgress(m_progress + 1, m_progressTotal);
qint64 pos = f->position();
if (!f->copyTo(fileToDir[pos])) {
emitFailed(tr("failed to create file:%1").arg(fileToDir[pos]));
return;
}
}
emitSucceeded();
};
bool abort() override
{
m_needs_abort = true;
return true;
}

private:
KArchive* m_archive;
QDir m_output_dir;
QString m_subdirectory;

bool m_needs_abort = false;
};
#endif
} // namespace MMCZip
93 changes: 33 additions & 60 deletions launcher/java/download/ArchiveDownloadTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "java/download/ArchiveDownloadTask.h"
#include <quazip.h>
#include <memory>
#include "MMCZip.h"

#include "Application.h"
#include "Untar.h"
#include "net/ChecksumValidator.h"
#include "net/NetJob.h"
#include "tasks/Task.h"
Expand Down Expand Up @@ -65,69 +63,44 @@ void ArchiveDownloadTask::executeTask()

void ArchiveDownloadTask::extractJava(QString input)
{
setStatus(tr("Extracting Java"));
if (input.endsWith("tar")) {
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
QFile in(input);
if (!in.open(QFile::ReadOnly)) {
emitFailed(tr("Unable to open supplied tar file."));
return;
}
if (!Tar::extract(&in, QDir(m_final_path).absolutePath())) {
emitFailed(tr("Unable to extract supplied tar file."));
return;
}
emitSucceeded();
setStatus(tr("Extracting java"));

auto archive = MMCZip::ExtractKArchive::newArchive(input);
if (!archive->open(QIODevice::ReadOnly)) {
emitFailed(tr("Unable to open supplied archive file."));
return;
} else if (input.endsWith("tar.gz") || input.endsWith("taz") || input.endsWith("tgz")) {
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
if (!GZTar::extract(input, QDir(m_final_path).absolutePath())) {
emitFailed(tr("Unable to extract supplied tar file."));
return;
}
emitSucceeded();
}
auto files = archive->directory()->entries();
if (files.isEmpty()) {
emitFailed(tr("No files were found in the supplied zip file,"));
return;
} else if (input.endsWith("zip")) {
auto zip = std::make_shared<QuaZip>(input);
if (!zip->open(QuaZip::mdUnzip)) {
emitFailed(tr("Unable to open supplied zip file."));
return;
}
auto files = zip->getFileNameList();
if (files.isEmpty()) {
emitFailed(tr("No files were found in the supplied zip file."));
return;
}
m_task = makeShared<MMCZip::ExtractZipTask>(zip, m_final_path, files[0]);

auto progressStep = std::make_shared<TaskStepProgress>();
connect(m_task.get(), &Task::finished, this, [this, progressStep] {
progressStep->state = TaskStepState::Succeeded;
stepProgress(*progressStep);
});
}
m_task = makeShared<MMCZip::ExtractKArchive>(archive, m_final_path, files[0]);

connect(m_task.get(), &Task::succeeded, this, &ArchiveDownloadTask::emitSucceeded);
connect(m_task.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted);
connect(m_task.get(), &Task::failed, this, [this, progressStep](QString reason) {
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
emitFailed(reason);
});
connect(m_task.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress);
auto progressStep = std::make_shared<TaskStepProgress>();
connect(m_task.get(), &Task::finished, this, [this, progressStep] {
progressStep->state = TaskStepState::Succeeded;
stepProgress(*progressStep);
});

connect(m_task.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
progressStep->update(current, total);
stepProgress(*progressStep);
});
connect(m_task.get(), &Task::status, this, [this, progressStep](QString status) {
progressStep->status = status;
stepProgress(*progressStep);
});
m_task->start();
return;
}
connect(m_task.get(), &Task::succeeded, this, &ArchiveDownloadTask::emitSucceeded);
connect(m_task.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted);
connect(m_task.get(), &Task::failed, this, [this, progressStep](QString reason) {
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
emitFailed(reason);
});
connect(m_task.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress);

emitFailed(tr("Could not determine archive type!"));
connect(m_task.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
progressStep->update(current, total);
stepProgress(*progressStep);
});
connect(m_task.get(), &Task::status, this, [this, progressStep](QString status) {
progressStep->status = status;
stepProgress(*progressStep);
});
m_task->start();
}

bool ArchiveDownloadTask::abort()
Expand Down
2 changes: 1 addition & 1 deletion libraries/extra-cmake-modules
1 change: 1 addition & 0 deletions libraries/karchive
Submodule karchive added at 952e1e
Loading