summaryrefslogtreecommitdiff
path: root/archived/projt-launcher/launcher/Untar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'archived/projt-launcher/launcher/Untar.cpp')
-rw-r--r--archived/projt-launcher/launcher/Untar.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/Untar.cpp b/archived/projt-launcher/launcher/Untar.cpp
new file mode 100644
index 0000000000..df575c9d4a
--- /dev/null
+++ b/archived/projt-launcher/launcher/Untar.cpp
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: 2026 Project Tick
+// SPDX-FileContributor: Project Tick Team
+/*
+ * ProjT Launcher - Minecraft Launcher
+ * Copyright (C) 2026 Project Tick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * === Upstream License Block (Do Not Modify) ==============================
+ *
+ *
+ *
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ======================================================================== */
+#include "Untar.h"
+#include <quagzipfile.h>
+#include <QByteArray>
+#include <QFileInfo>
+#include <QIODevice>
+#include <QString>
+#include "FileSystem.h"
+
+// adaptation of the:
+// - https://github.com/madler/zlib/blob/develop/contrib/untgz/untgz.c
+// - https://en.wikipedia.org/wiki/Tar_(computing)
+// - https://github.com/euroelessar/cutereader/blob/master/karchive/src/ktar.cpp
+
+#define BLOCKSIZE 512
+#define SHORTNAMESIZE 100
+
+enum class TypeFlag : char
+{
+ Regular = '0', // regular file
+ ARegular = 0, // regular file
+ Link = '1', // link
+ Symlink = '2', // reserved
+ Character = '3', // character special
+ Block = '4', // block special
+ Directory = '5', // directory
+ FIFO = '6', // FIFO special
+ Contiguous = '7', // reserved
+ // Posix stuff
+ GlobalPosixHeader = 'g',
+ ExtendedPosixHeader = 'x',
+ // 'A'– 'Z' Vendor specific extensions(POSIX .1 - 1988)
+ // GNU
+ GNULongLink = 'K', /* long link name */
+ GNULongName = 'L', /* long file name */
+};
+
+// struct Header { /* byte offset */
+// char name[100]; /* 0 */
+// char mode[8]; /* 100 */
+// char uid[8]; /* 108 */
+// char gid[8]; /* 116 */
+// char size[12]; /* 124 */
+// char mtime[12]; /* 136 */
+// char chksum[8]; /* 148 */
+// TypeFlag typeflag; /* 156 */
+// char linkname[100]; /* 157 */
+// char magic[6]; /* 257 */
+// char version[2]; /* 263 */
+// char uname[32]; /* 265 */
+// char gname[32]; /* 297 */
+// char devmajor[8]; /* 329 */
+// char devminor[8]; /* 337 */
+// char prefix[155]; /* 345 */
+// /* 500 */
+// };
+
+bool readLonglink(QIODevice* in, qint64 size, QByteArray& longlink)
+{
+ qint64 n = 0;
+ size--; // ignore trailing null
+ if (size < 0)
+ {
+ qCritical() << "The filename size is negative";
+ return false;
+ }
+ longlink.resize(size + (BLOCKSIZE - size % BLOCKSIZE)); // make the size divisible by BLOCKSIZE
+ for (qint64 offset = 0; offset < longlink.size(); offset += BLOCKSIZE)
+ {
+ n = in->read(longlink.data() + offset, BLOCKSIZE);
+ if (n != BLOCKSIZE)
+ {
+ qCritical() << "The expected blocksize was not respected for the name";
+ return false;
+ }
+ }
+ longlink.truncate(qstrlen(longlink.constData()));
+ return true;
+}
+
+int getOctal(char* buffer, int maxlenght, bool* ok)
+{
+ return QByteArray(buffer, qstrnlen(buffer, maxlenght)).toInt(ok, 8);
+}
+
+QString decodeName(char* name)
+{
+ return QFile::decodeName(QByteArray(name, qstrnlen(name, 100)));
+}
+bool Tar::extract(QIODevice* in, QString dst)
+{
+ char buffer[BLOCKSIZE];
+ QString name, symlink, firstFolderName;
+ bool doNotReset = false, ok;
+ while (true)
+ {
+ auto n = in->read(buffer, BLOCKSIZE);
+ if (n != BLOCKSIZE)
+ { // allways expect complete blocks
+ qCritical() << "The expected blocksize was not respected";
+ return false;
+ }
+ if (buffer[0] == 0)
+ { // end of archive
+ return true;
+ }
+ int mode = getOctal(buffer + 100, 8, &ok) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read
+ // permisions
+ if (!ok)
+ {
+ qCritical() << "The file mode can't be read";
+ return false;
+ }
+ // there are names that are exactly 100 bytes long
+ // and neither longlink nor \0 terminated (bug:101472)
+
+ if (name.isEmpty())
+ {
+ name = decodeName(buffer);
+ if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName))
+ {
+ name = name.mid(firstFolderName.size());
+ }
+ }
+ if (symlink.isEmpty())
+ symlink = decodeName(buffer);
+ qint64 size = getOctal(buffer + 124, 12, &ok);
+ if (!ok)
+ {
+ qCritical() << "The file size can't be read";
+ return false;
+ }
+ switch (TypeFlag(buffer[156]))
+ {
+ case TypeFlag::Regular:
+ /* fallthrough */
+ case TypeFlag::ARegular:
+ {
+ auto fileName = FS::PathCombine(dst, name);
+ if (!FS::ensureFilePathExists(fileName))
+ {
+ qCritical() << "Can't ensure the file path to exist: " << fileName;
+ return false;
+ }
+ QFile out(fileName);
+ if (!out.open(QFile::WriteOnly))
+ {
+ qCritical() << "Can't open file:" << fileName;
+ return false;
+ }
+ out.setPermissions(QFile::Permissions(mode));
+ while (size > 0)
+ {
+ QByteArray tmp(BLOCKSIZE, 0);
+ n = in->read(tmp.data(), BLOCKSIZE);
+ if (n != BLOCKSIZE)
+ {
+ qCritical() << "The expected blocksize was not respected when reading file";
+ return false;
+ }
+ tmp.truncate(qMin(qint64(BLOCKSIZE), size));
+ out.write(tmp);
+ size -= BLOCKSIZE;
+ }
+ break;
+ }
+ case TypeFlag::Directory:
+ {
+ if (firstFolderName.isEmpty())
+ {
+ firstFolderName = name;
+ break;
+ }
+ auto folderPath = FS::PathCombine(dst, name);
+ if (!FS::ensureFolderPathExists(folderPath))
+ {
+ qCritical() << "Can't ensure that folder exists: " << folderPath;
+ return false;
+ }
+ break;
+ }
+ case TypeFlag::GNULongLink:
+ {
+ doNotReset = true;
+ QByteArray longlink;
+ if (readLonglink(in, size, longlink))
+ {
+ symlink = QFile::decodeName(longlink.constData());
+ }
+ else
+ {
+ qCritical() << "Failed to read long link";
+ return false;
+ }
+ break;
+ }
+ case TypeFlag::GNULongName:
+ {
+ doNotReset = true;
+ QByteArray longlink;
+ if (readLonglink(in, size, longlink))
+ {
+ name = QFile::decodeName(longlink.constData());
+ }
+ else
+ {
+ qCritical() << "Failed to read long name";
+ return false;
+ }
+ break;
+ }
+ case TypeFlag::Link:
+ /* fallthrough */
+ case TypeFlag::Symlink:
+ {
+ auto fileName = FS::PathCombine(dst, name);
+ if (!FS::create_link(FS::PathCombine(QFileInfo(fileName).path(), symlink), fileName)())
+ { // do not use symlinks
+ qCritical() << "Can't create link for:" << fileName
+ << " to:" << FS::PathCombine(QFileInfo(fileName).path(), symlink);
+ return false;
+ }
+ FS::ensureFilePathExists(fileName);
+ QFile::setPermissions(fileName, QFile::Permissions(mode));
+ break;
+ }
+ case TypeFlag::Character:
+ /* fallthrough */
+ case TypeFlag::Block:
+ /* fallthrough */
+ case TypeFlag::FIFO:
+ /* fallthrough */
+ case TypeFlag::Contiguous:
+ /* fallthrough */
+ case TypeFlag::GlobalPosixHeader:
+ /* fallthrough */
+ case TypeFlag::ExtendedPosixHeader:
+ /* fallthrough */
+ default: break;
+ }
+ if (!doNotReset)
+ {
+ name.truncate(0);
+ symlink.truncate(0);
+ }
+ doNotReset = false;
+ }
+ return true;
+}
+
+bool GZTar::extract(QString src, QString dst)
+{
+ QuaGzipFile a(src);
+ if (!a.open(QIODevice::ReadOnly))
+ {
+ qCritical() << "Can't open tar file:" << src;
+ return false;
+ }
+ return Tar::extract(&a, dst);
+} \ No newline at end of file