/* This file is part of the KDE libraries Copyright (C) 2018 Ivailo Monev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2, as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "karchive.h" #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_LIBARCHIVE) # include # include #endif #include #if defined(HAVE_STRMODE) # include #endif #ifndef PATH_MAX # define PATH_MAX _POSIX_PATH_MAX #endif // NOTE: many KArchive users are not doing listing and extraction in a thread which means that the // UI will be "frozen" while that happens so process events while doing so #define KARCHIVE_TIMEOUT 250 #define KARCHIVE_BUFFSIZE 10240 KArchiveEntry::KArchiveEntry() : encrypted(false), size(0), gid(-1), uid(-1), mode(0), atime(0), ctime(0), mtime(0) { } QString KArchiveEntry::fancyEncrypted() const { if (encrypted) { return QString::fromLatin1("yes"); } return QString::fromLatin1("No"); } QString KArchiveEntry::fancySize() const { return KGlobal::locale()->formatByteSize(size, 1); } QString KArchiveEntry::fancyMode() const { #if defined(HAVE_STRMODE) if (mode == 0) { return QString::fromLatin1("---------"); } char strmodebuffer[20]; ::memset(strmodebuffer, '\0', sizeof(strmodebuffer) * sizeof(char)); ::strmode(mode, strmodebuffer); return QString::fromLatin1(strmodebuffer); #elif defined(HAVE_LIBARCHIVE) struct archive_entry* archiveentry = archive_entry_new(); archive_entry_set_mode(archiveentry, mode); const QString result = QString::fromLatin1(archive_entry_strmode(archiveentry)); archive_entry_free(archiveentry); return result; #else return QString::number(mode); #endif // HAVE_STRMODE } QString KArchiveEntry::fancyATime() const { return KGlobal::locale()->formatDateTime(QDateTime::fromTime_t(atime)); } QString KArchiveEntry::fancyCTime() const { return KGlobal::locale()->formatDateTime(QDateTime::fromTime_t(ctime)); } QString KArchiveEntry::fancyMTime() const { return KGlobal::locale()->formatDateTime(QDateTime::fromTime_t(mtime)); } QString KArchiveEntry::fancyType() const { if (S_ISREG(mode)) { return QString::fromLatin1("File"); } else if (S_ISDIR(mode)) { return QString::fromLatin1("Directory"); } else if (S_ISLNK(mode)) { return QString::fromLatin1("Link"); } else if (S_ISCHR(mode)) { return QString::fromLatin1("Character"); } else if (S_ISBLK(mode)) { return QString::fromLatin1("Block"); } else if (S_ISFIFO(mode)) { return QString::fromLatin1("Fifo"); } else if (S_ISSOCK(mode)) { return QString::fromLatin1("Socket"); } else { return QString::fromLatin1("None"); } } bool KArchiveEntry::isNull() const { return (mode == 0); } bool KArchiveEntry::operator==(const KArchiveEntry &karchiveentry) const { return ( encrypted == karchiveentry.encrypted && size == karchiveentry.size && gid == karchiveentry.gid && uid == karchiveentry.uid && mode == karchiveentry.mode && atime == karchiveentry.atime && ctime == karchiveentry.ctime && mtime == karchiveentry.mtime && hardlink == karchiveentry.hardlink && symlink == karchiveentry.symlink && pathname == karchiveentry.pathname && groupname == karchiveentry.groupname && username == karchiveentry.username ); } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const KArchiveEntry &karchiveentry) { d << "KArchiveEntry( encrypted:" << karchiveentry.fancyEncrypted() << ", size:" << karchiveentry.fancySize() << ", gid:" << karchiveentry.gid << ", uid:" << karchiveentry.uid << ", mode:" << karchiveentry.fancyMode() << ", atime:" << karchiveentry.fancyATime() << ", ctime:" << karchiveentry.fancyCTime() << ", mtime:" << karchiveentry.fancyMTime() << ", hardlink:" << karchiveentry.hardlink << ", symlink:" << karchiveentry.symlink << ", pathname:" << karchiveentry.pathname << ", groupname:" << karchiveentry.groupname << ", username:" << karchiveentry.username << ", type:" << karchiveentry.fancyType() << ")"; return d; } #endif class KArchivePrivate { public: KArchivePrivate(); QString m_path; bool m_writable; QString m_error; QByteArray m_readpass; QByteArray m_writepass; #if defined(HAVE_LIBARCHIVE) struct archive* openRead(const QByteArray &path); struct archive* openWrite(const QByteArray &path); struct archive* openDisk(const bool preserve); static bool closeRead(struct archive*); static bool closeWrite(struct archive*); bool copyData(struct archive* readarchive, struct archive* writearchive); bool copyData(struct archive* readarchive, QByteArray *buffer); #endif }; KArchivePrivate::KArchivePrivate() : m_writable(false) { } #if defined(HAVE_LIBARCHIVE) struct archive* KArchivePrivate::openRead(const QByteArray &path) { struct archive* readarchive = archive_read_new(); if (readarchive) { if (archive_read_support_filter_all(readarchive) != ARCHIVE_OK) { kDebug() << "archive_read_support_filter_all" << archive_error_string(readarchive); KArchivePrivate::closeRead(readarchive); return nullptr; } if (archive_read_support_format_all(readarchive) != ARCHIVE_OK) { kDebug() << "archive_read_support_format_all" << archive_error_string(readarchive); KArchivePrivate::closeRead(readarchive); return nullptr; } if (!m_readpass.isEmpty() && archive_read_add_passphrase(readarchive, m_readpass.constData()) != ARCHIVE_OK) { kDebug() << "archive_read_add_passphrase" << archive_error_string(readarchive); KArchivePrivate::closeRead(readarchive); return nullptr; } if (archive_read_open_filename(readarchive, path, KARCHIVE_BUFFSIZE) != ARCHIVE_OK) { kDebug() << "archive_read_open_filename" << archive_error_string(readarchive); KArchivePrivate::closeRead(readarchive); return nullptr; } } return readarchive; } struct archive* KArchivePrivate::openWrite(const QByteArray &path) { struct archive* writearchive = archive_write_new(); if (writearchive) { // NOTE: archive_write_set_format_filter_by_ext() cannot recognize all supported formats const QByteArray lowerpath = path.toLower(); if (lowerpath.endsWith(".lzma")) { archive_write_add_filter_lzma(writearchive); #if ARCHIVE_VERSION_NUMBER > 3000004 } else if (lowerpath.endsWith(".lzo")) { archive_write_add_filter_lzop(writearchive); } else if (lowerpath.endsWith(".lrz")) { archive_write_add_filter_lrzip(writearchive); #endif #if ARCHIVE_VERSION_NUMBER > 3001002 } else if (lowerpath.endsWith(".lz4")) { archive_write_add_filter_lz4(writearchive); #endif #if ARCHIVE_VERSION_NUMBER > 3003002 } else if (lowerpath.endsWith(".zst")) { archive_write_add_filter_zstd(writearchive); #endif } else if (lowerpath.endsWith(".lz")) { archive_write_add_filter_lzip(writearchive); } else if (lowerpath.endsWith(".z")) { archive_write_add_filter_compress(writearchive); } else if (archive_write_set_format_filter_by_ext(writearchive, path) != ARCHIVE_OK) { kDebug() << "archive_write_set_format_filter_by_ext" << archive_error_string(writearchive); KArchivePrivate::closeWrite(writearchive); return nullptr; } (void)archive_write_set_format_pax_restricted(writearchive); if (!m_writepass.isEmpty() && archive_write_set_passphrase(writearchive, m_writepass.constData()) != ARCHIVE_OK) { kDebug() << "archive_write_set_passphrase" << archive_error_string(writearchive); KArchivePrivate::closeWrite(writearchive); return nullptr; } if (archive_write_open_filename(writearchive, path) != ARCHIVE_OK) { kDebug() << "archive_write_open_filename" << archive_error_string(writearchive); KArchivePrivate::closeWrite(writearchive); return nullptr; } } return writearchive; } struct archive* KArchivePrivate::openDisk(const bool preserve) { struct archive* writearchive = archive_write_disk_new(); if (writearchive) { int extractFlags = ARCHIVE_EXTRACT_TIME; extractFlags |= ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_SECURE_NODOTDOT; if (preserve) { extractFlags |= ARCHIVE_EXTRACT_PERM; extractFlags |= ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_XATTR; extractFlags |= ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_MAC_METADATA; } if (archive_write_disk_set_options(writearchive, extractFlags) != ARCHIVE_OK) { kDebug() << "archive_write_disk_set_options" << archive_error_string(writearchive); KArchivePrivate::closeWrite(writearchive); return nullptr; } if (archive_write_disk_set_standard_lookup(writearchive) != ARCHIVE_OK) { kDebug() << "archive_write_disk_set_standard_lookup" << archive_error_string(writearchive); KArchivePrivate::closeWrite(writearchive); return nullptr; } } return writearchive; } bool KArchivePrivate::closeRead(struct archive* readarchive) { if (readarchive) { if (archive_read_close(readarchive) != ARCHIVE_OK) { kDebug() << "archive_read_close" << archive_error_string(readarchive); return false; } if (archive_read_free(readarchive) != ARCHIVE_OK) { kDebug() << "archive_read_free" << archive_error_string(readarchive); return false; } } return true; } bool KArchivePrivate::closeWrite(struct archive* writearchive) { if (writearchive) { if (archive_write_close(writearchive) != ARCHIVE_OK) { kDebug() << "archive_write_close" << archive_error_string(writearchive); return false; } if (archive_write_free(writearchive) != ARCHIVE_OK) { kDebug() << "archive_write_free" << archive_error_string(writearchive); return false; } } return true; } bool KArchivePrivate::copyData(struct archive* readarchive, struct archive* writearchive) { char readbuffer[KARCHIVE_BUFFSIZE]; ssize_t readsize = archive_read_data(readarchive, readbuffer, sizeof(readbuffer)); while (readsize > 0) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); const int result = archive_errno(readarchive); if (result != ARCHIVE_OK) { m_error = archive_error_string(readarchive); kDebug() << "archive_read_data" << m_error; return false; } if (archive_write_data(writearchive, readbuffer, readsize) != readsize) { m_error = archive_error_string(writearchive); kDebug() << "archive_write_data" << m_error; return false; } readsize = archive_read_data(readarchive, readbuffer, sizeof(readbuffer)); } return true; } bool KArchivePrivate::copyData(struct archive* readarchive, QByteArray *buffer) { char readbuffer[KARCHIVE_BUFFSIZE]; ssize_t readsize = archive_read_data(readarchive, readbuffer, sizeof(readbuffer)); while (readsize > 0) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); const int result = archive_errno(readarchive); if (result != ARCHIVE_OK) { m_error = archive_error_string(readarchive); kDebug() << "archive_read_data" << m_error; return false; } buffer->append(readbuffer, readsize); readsize = archive_read_data(readarchive, readbuffer, sizeof(readbuffer)); } return true; } #endif // HAVE_LIBARCHIVE KArchive::KArchive(const QString &path, QObject *parent) : QObject(parent), d(new KArchivePrivate()) { d->m_path = path; #if defined(HAVE_LIBARCHIVE) if (path.isEmpty()) { d->m_error = i18n("Empty archive path"); kDebug() << d->m_error; return; } const KMimeType::Ptr kmimetype = KMimeType::findByPath(path); if (kmimetype) { foreach (const QString &mime, KArchive::writableMimeTypes()) { if (kmimetype->is(mime)) { d->m_writable = true; } } } if (d->m_writable) { d->m_writable = QFileInfo(QFileInfo(path).path()).isWritable(); } if (!d->m_writable) { d->m_error = i18n("Archive is not writable"); } #else d->m_error = QString::fromLatin1("Built without LibArchive"); #endif // HAVE_LIBARCHIVE } KArchive::~KArchive() { delete d; } bool KArchive::isSupported() { #if defined(HAVE_LIBARCHIVE) return true; #else return false; #endif } bool KArchive::add(const QStringList &paths, const QByteArray &strip, const QByteArray &destination) const { bool result = false; #if defined(HAVE_LIBARCHIVE) d->m_error.clear(); if (d->m_path.isEmpty()) { d->m_error = i18n("Empty archive path"); kDebug() << d->m_error; return result; } else if (!d->m_writable) { d->m_error = i18n("Archive is not writable"); kDebug() << d->m_error; return result; } QFileInfo fileinfo(d->m_path); const QString tmptemplate = QString::fromLatin1("XXXXXXXXXX.%1").arg(fileinfo.completeSuffix()); const QString tmpfile = KTemporaryFile::filePath(tmptemplate); struct archive* writearchive = d->openWrite(QFile::encodeName(tmpfile)); if (!writearchive) { d->m_error = i18n("Could not open temporary archive: %1", tmpfile); kDebug() << d->m_error; return result; } QStringList recursivepaths; foreach (const QString &path, paths) { if (QDir(path).exists()) { QDirIterator iterator(path, QDir::AllEntries | QDir::System | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (iterator.hasNext()) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); recursivepaths << QDir::cleanPath(iterator.next()); } } else { recursivepaths << QDir::cleanPath(path); } } recursivepaths.removeDuplicates(); if (QFile::exists(d->m_path)) { struct archive* readarchive = d->openRead(QFile::encodeName(d->m_path)); if (!readarchive) { d->m_error = i18n("Could not open archive: %1", d->m_path); kDebug() << d->m_error; return result; } struct archive_entry* archiveentry = archive_entry_new(); int ret = archive_read_next_header(readarchive, &archiveentry); while (ret != ARCHIVE_EOF) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); if (ret < ARCHIVE_OK) { d->m_error = archive_error_string(readarchive); kDebug() << "archive_read_next_header" << d->m_error; result = false; break; } const QByteArray pathname = archive_entry_pathname(archiveentry); if (recursivepaths.contains(strip + pathname)) { kDebug() << "Removing (update)" << pathname; archive_read_data_skip(readarchive); ret = archive_read_next_header(readarchive, &archiveentry); continue; } if (archive_write_header(writearchive, archiveentry) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_header" << d->m_error; result = false; break; } if (!d->copyData(readarchive, writearchive)) { result = false; break; } if (archive_write_finish_entry(writearchive) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_finish_entry" << d->m_error; result = false; break; } ret = archive_read_next_header(readarchive, &archiveentry); } KArchivePrivate::closeRead(readarchive); } qreal progressvalue = 0.0; const qreal progessstep = (qreal(1.0) / qreal(recursivepaths.size())); foreach (const QString &path, recursivepaths) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); const QByteArray localpath = QFile::encodeName(path); struct stat statistic; if (::lstat(localpath, &statistic) != 0) { const int savederrno = errno; d->m_error = i18n("lstat: %1", qt_error_string(savederrno)); kDebug() << d->m_error; result = false; break; } QByteArray pathname = localpath; if (pathname.startsWith(strip)) { pathname.remove(0, strip.size()); } pathname.prepend(destination); if (pathname.isEmpty()) { kWarning() << "Not adding empty pathname"; continue; } kDebug() << "Adding" << path << "as" << pathname; // NOTE: archive_entry_copy_stat doesn't work // http://linux.die.net/man/2/stat struct archive_entry* newentry = archive_entry_new(); archive_entry_set_pathname(newentry, pathname.constData()); archive_entry_set_size(newentry, statistic.st_size); archive_entry_set_gid(newentry, statistic.st_gid); archive_entry_set_uid(newentry, statistic.st_uid); // filetype and mode are supposedly the same, permissions are set when mode is set archive_entry_set_mode(newentry, statistic.st_mode); archive_entry_set_atime(newentry, statistic.st_atim.tv_sec, statistic.st_atim.tv_nsec); archive_entry_set_ctime(newentry, statistic.st_ctim.tv_sec, statistic.st_ctim.tv_nsec); archive_entry_set_mtime(newentry, statistic.st_mtim.tv_sec, statistic.st_mtim.tv_nsec); if (statistic.st_nlink > 1) { // TODO: archive_entry_set_hardlink(newentry, pathname); } if (S_ISLNK(statistic.st_mode)) { QByteArray linkbuffer(PATH_MAX + 1, char('\0')); if (::readlink(localpath, linkbuffer.data(), PATH_MAX) == -1) { const int savederrno = errno; d->m_error = i18n("readlink: %1", qt_error_string(savederrno)); result = false; break; } if (linkbuffer.startsWith(strip)) { linkbuffer.remove(0, strip.size()); } archive_entry_set_symlink(newentry, linkbuffer.constData()); } const QByteArray pathgname = KUserGroup(statistic.st_gid).name().toUtf8(); if (!pathgname.isEmpty()) { archive_entry_set_gname(newentry, pathgname.constData()); } else { kDebug() << "Empty group name"; } const QByteArray pathuname = KUser(statistic.st_uid).loginName().toUtf8(); if (!pathuname.isEmpty()) { archive_entry_set_uname(newentry, pathuname.constData()); } else { kDebug() << "Empty user name"; } if (archive_write_header(writearchive, newentry) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_header" << d->m_error; archive_entry_free(newentry); result = false; break; } archive_entry_free(newentry); if (S_ISREG(statistic.st_mode)) { QFile file(path); if (!file.open(QFile::ReadOnly)) { d->m_error = i18n("Could not open source: %1", path); kDebug() << d->m_error; result = false; break; } const QByteArray data = file.readAll(); if (data.isEmpty() && statistic.st_size > 0) { d->m_error = i18n("Could not read source: %1", path); kDebug() << d->m_error; result = false; break; } if (statistic.st_size > 0 && data.size() != statistic.st_size) { d->m_error = i18n("Read and stat size are different: %1", path); kDebug() << d->m_error; result = false; break; } if (archive_write_data(writearchive, data.constData(), data.size()) != statistic.st_size) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_data" << d->m_error; result = false; break; } } if (archive_write_finish_entry(writearchive) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_finish_entry" << d->m_error; result = false; break; } result = true; progressvalue += progessstep; emit progress(progressvalue); } KArchivePrivate::closeWrite(writearchive); if (result) { kDebug() << "Replacing" << d->m_path << "with" << tmpfile; // TODO: set permissions on file after copy, same as the original result = (KDE::rename(tmpfile, d->m_path) != -1); if (!result) { d->m_error = i18n("Could not move: %1 to: %2", tmpfile, d->m_path); kDebug() << d->m_error; } } #else Q_UNUSED(paths); Q_UNUSED(strip); Q_UNUSED(destination); #endif // HAVE_LIBARCHIVE return result; } bool KArchive::remove(const QStringList &paths) const { bool result = false; #if defined(HAVE_LIBARCHIVE) d->m_error.clear(); if (d->m_path.isEmpty()) { d->m_error = i18n("Empty archive path"); kDebug() << d->m_error; return result; } else if (!d->m_writable) { d->m_error = i18n("Archive is not writable"); kDebug() << d->m_error; return result; } struct archive* readarchive = d->openRead(QFile::encodeName(d->m_path)); if (!readarchive) { d->m_error = i18n("Could not open archive: %1", d->m_path); kDebug() << d->m_error; return result; } QFileInfo fileinfo(d->m_path); const QString tmptemplate = QString::fromLatin1("XXXXXXXXXX.%1").arg(fileinfo.completeSuffix()); const QString tmpfile = KTemporaryFile::filePath(tmptemplate); struct archive* writearchive = d->openWrite(QFile::encodeName(tmpfile)); if (!writearchive) { d->m_error = i18n("Could not open temporary archive: %1", tmpfile); kDebug() << d->m_error; return result; } QStringList notfound = paths; qreal progressvalue = 0.0; const qreal progessstep = (qreal(1.0) / qreal(paths.size())); struct archive_entry* entry = archive_entry_new(); int ret = archive_read_next_header(readarchive, &entry); while (ret != ARCHIVE_EOF) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); if (ret < ARCHIVE_OK) { d->m_error = archive_error_string(readarchive); kDebug() << "archive_read_next_header" << d->m_error; result = false; break; } const QByteArray pathname = archive_entry_pathname(entry); const QString pathnamestring = QFile::decodeName(pathname); if (paths.contains(pathnamestring)) { kDebug() << "Removing" << pathname; notfound.removeAll(pathname); archive_read_data_skip(readarchive); ret = archive_read_next_header(readarchive, &entry); result = true; progressvalue += progessstep; emit progress(progressvalue); continue; } if (archive_write_header(writearchive, entry) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_header" << d->m_error; result = false; break; } if (!d->copyData(readarchive, writearchive)) { result = false; break; } if (archive_write_finish_entry(writearchive) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_finish_entry" << d->m_error; result = false; break; } ret = archive_read_next_header(readarchive, &entry); } KArchivePrivate::closeWrite(writearchive); KArchivePrivate::closeRead(readarchive); if (result) { kDebug() << "Replacing" << d->m_path << "with" << tmpfile; result = (KDE::rename(tmpfile, d->m_path) != -1); if (!result) { d->m_error = i18n("Could not move: %1 to: %2", tmpfile, d->m_path); kDebug() << d->m_error; } } if (!notfound.isEmpty()) { d->m_error = i18n("Entries not in archive: %1", notfound.join(QLatin1String(", "))); kDebug() << d->m_error; result = false; } #else Q_UNUSED(paths); #endif // HAVE_LIBARCHIVE return result; } bool KArchive::extract(const QStringList &paths, const QString &destination, const bool preserve) const { bool result = false; #if defined(HAVE_LIBARCHIVE) d->m_error.clear(); if (d->m_path.isEmpty()) { d->m_error = i18n("Empty archive path"); kDebug() << d->m_error; return result; } const QString currentdir = QDir::currentPath(); if (!QDir::setCurrent(destination)) { d->m_error = i18n("could not change to destination directory: %1", destination); kDebug() << d->m_error; return result; } struct archive* readarchive = d->openRead(QFile::encodeName(d->m_path)); if (!readarchive) { d->m_error = i18n("Could not open archive: %1", d->m_path); kDebug() << d->m_error; return result; } struct archive* writearchive = d->openDisk(preserve); if (!writearchive) { d->m_error = i18n("Could not open destination: %1", destination); kDebug() << d->m_error; return result; } QStringList notfound = paths; qreal progressvalue = 0.0; const qreal progessstep = (qreal(1.0) / qreal(paths.size())); struct archive_entry* entry = archive_entry_new(); int ret = archive_read_next_header(readarchive, &entry); while (ret != ARCHIVE_EOF) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); if (ret < ARCHIVE_OK) { d->m_error = archive_error_string(readarchive); kDebug() << "archive_read_next_header" << d->m_error; result = false; break; } const QByteArray pathname = archive_entry_pathname(entry); const QString pathnamestring = QFile::decodeName(pathname); if (!paths.contains(pathnamestring)) { archive_read_data_skip(readarchive); ret = archive_read_next_header(readarchive, &entry); continue; } notfound.removeAll(pathnamestring); result = true; if (archive_write_header(writearchive, entry) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_header" << d->m_error; result = false; break; } if (archive_read_extract2(readarchive, entry, writearchive) != ARCHIVE_OK) { d->m_error = archive_error_string(readarchive); kDebug() << "archive_read_extract2" << d->m_error; result = false; break; } if (archive_write_finish_entry(writearchive) != ARCHIVE_OK) { d->m_error = archive_error_string(writearchive); kDebug() << "archive_write_finish_entry" << d->m_error; result = false; break; } progressvalue += progessstep; emit progress(progressvalue); ret = archive_read_next_header(readarchive, &entry); } KArchivePrivate::closeWrite(writearchive); KArchivePrivate::closeRead(readarchive); Q_ASSERT_X(currentdir == QDir::currentPath(), "KArchive::extract", "Current directory changed"); if (!QDir::setCurrent(currentdir)) { kWarning() << "Could not change to orignal directory" << currentdir; } if (!notfound.isEmpty()) { d->m_error = i18n("Entries not in archive: %1", notfound.join(QLatin1String(", "))); kDebug() << d->m_error; result = false; } #else Q_UNUSED(paths); Q_UNUSED(destination); Q_UNUSED(preserve); #endif // HAVE_LIBARCHIVE return result; } QList KArchive::list(const QString &path) const { QList result; #if defined(HAVE_LIBARCHIVE) d->m_error.clear(); if (d->m_path.isEmpty()) { d->m_error = i18n("Empty archive path"); kDebug() << d->m_error; return result; } struct archive* readarchive = d->openRead(QFile::encodeName(d->m_path)); if (!readarchive) { d->m_error = i18n("Could not open archive: %1", d->m_path); kDebug() << d->m_error; return result; } struct archive_entry* entry = archive_entry_new(); int ret = archive_read_next_header(readarchive, &entry); while (ret != ARCHIVE_EOF) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); if (ret < ARCHIVE_OK) { d->m_error = archive_error_string(readarchive); kDebug() << "archive_read_next_header" << d->m_error; result.clear(); break; } const QByteArray pathname = archive_entry_pathname(entry); if (!path.isEmpty()) { const QString pathnamestring = QFile::decodeName(pathname); if (!pathnamestring.startsWith(path)) { ret = archive_read_next_header(readarchive, &entry); continue; } } KArchiveEntry karchiveentry; karchiveentry.encrypted = bool(archive_entry_is_encrypted(entry)); karchiveentry.size = archive_entry_size(entry); karchiveentry.gid = archive_entry_gid(entry); karchiveentry.uid = archive_entry_uid(entry); karchiveentry.mode = archive_entry_mode(entry); karchiveentry.atime = archive_entry_atime(entry); karchiveentry.ctime = archive_entry_ctime(entry); karchiveentry.mtime = archive_entry_mtime(entry); karchiveentry.hardlink = archive_entry_hardlink(entry); karchiveentry.symlink = archive_entry_symlink(entry); karchiveentry.pathname = pathname; karchiveentry.groupname = archive_entry_gname(entry); karchiveentry.username = archive_entry_uname(entry); result << karchiveentry; ret = archive_read_next_header(readarchive, &entry); } KArchivePrivate::closeRead(readarchive); #else Q_UNUSED(path); #endif // HAVE_LIBARCHIVE return result; } KArchiveEntry KArchive::entry(const QString &path) const { KArchiveEntry result; #if defined(HAVE_LIBARCHIVE) d->m_error.clear(); if (d->m_path.isEmpty()) { d->m_error = i18n("Empty archive path"); kDebug() << d->m_error; return result; } struct archive* readarchive = d->openRead(QFile::encodeName(d->m_path)); if (!readarchive) { d->m_error = i18n("Could not open archive: %1", d->m_path); kDebug() << d->m_error; return result; } bool found = false; struct archive_entry* entry = archive_entry_new(); int ret = archive_read_next_header(readarchive, &entry); while (ret != ARCHIVE_EOF) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); if (ret < ARCHIVE_OK) { d->m_error = archive_error_string(readarchive); kDebug() << "archive_read_next_header" << d->m_error; break; } const QByteArray pathname = archive_entry_pathname(entry); const QString pathnamestring = QFile::decodeName(pathname); if (pathnamestring == path) { result.encrypted = bool(archive_entry_is_encrypted(entry)); result.size = archive_entry_size(entry); result.gid = archive_entry_gid(entry); result.uid = archive_entry_uid(entry); result.mode = archive_entry_mode(entry); result.atime = archive_entry_atime(entry); result.ctime = archive_entry_ctime(entry); result.mtime = archive_entry_mtime(entry); result.hardlink = archive_entry_hardlink(entry); result.symlink = archive_entry_symlink(entry); result.pathname = pathname; result.groupname = archive_entry_gname(entry); result.username = archive_entry_uname(entry); found = true; break; } ret = archive_read_next_header(readarchive, &entry); } KArchivePrivate::closeRead(readarchive); if (!found) { d->m_error = i18n("Entry not in archive: %1", path); kDebug() << d->m_error; } #else Q_UNUSED(path); #endif // HAVE_LIBARCHIVE return result; } QByteArray KArchive::data(const QString &path) const { QByteArray result; #if defined(HAVE_LIBARCHIVE) d->m_error.clear(); if (d->m_path.isEmpty()) { d->m_error = i18n("Empty archive path"); kDebug() << d->m_error; return result; } struct archive* readarchive = d->openRead(QFile::encodeName(d->m_path)); if (!readarchive) { d->m_error = i18n("Could not open archive: %1", d->m_path); kDebug() << d->m_error; return result; } bool found = false; struct archive_entry* entry = archive_entry_new(); int ret = archive_read_next_header(readarchive, &entry); while (ret != ARCHIVE_EOF) { QCoreApplication::processEvents(QEventLoop::AllEvents, KARCHIVE_TIMEOUT); if (ret < ARCHIVE_OK) { d->m_error = archive_error_string(readarchive); kDebug() << "archive_read_next_header" << d->m_error; break; } const QByteArray pathname = archive_entry_pathname(entry); const QString pathnamestring = QFile::decodeName(pathname); if (pathnamestring == path) { d->copyData(readarchive, &result); found = true; break; } ret = archive_read_next_header(readarchive, &entry); } KArchivePrivate::closeRead(readarchive); if (!found) { d->m_error = i18n("Entry not in archive: %1", path); kDebug() << d->m_error; result.clear(); } #else Q_UNUSED(path); #endif // HAVE_LIBARCHIVE return result; } bool KArchive::isReadable() const { bool result = false; #if defined(HAVE_LIBARCHIVE) d->m_error.clear(); struct archive* readarchive = d->openRead(QFile::encodeName(d->m_path)); if (readarchive) { result = true; } KArchivePrivate::closeRead(readarchive); #endif return result; } bool KArchive::isWritable() const { return d->m_writable; } bool KArchive::requiresPassphrase() const { bool result = false; #if defined(HAVE_LIBARCHIVE) foreach (const KArchiveEntry &karchiveentry, KArchive::list()) { if (karchiveentry.encrypted == true) { result = true; break; } } #endif return result; } void KArchive::setReadPassphrase(const QString &passphrase) { d->m_readpass = passphrase.toUtf8(); } void KArchive::setWritePassphrase(const QString &passphrase) { d->m_writepass = passphrase.toUtf8(); } QString KArchive::errorString() const { return d->m_error; } QStringList KArchive::readableMimeTypes() { #if defined(HAVE_LIBARCHIVE) static const QStringList s_readmimetypes = QStringList() #if ARCHIVE_VERSION_NUMBER > 3003004 << QString::fromLatin1("application/vnd.rar") #endif << KArchive::writableMimeTypes(); return s_readmimetypes; #else return QStringList(); #endif // HAVE_LIBARCHIVE } QStringList KArchive::writableMimeTypes() { #if defined(HAVE_LIBARCHIVE) static const QStringList s_writemimetypes = QStringList() #if ARCHIVE_VERSION_NUMBER > 3000004 << QString::fromLatin1("application/x-lzop") << QString::fromLatin1("application/x-lrzip") #endif #if ARCHIVE_VERSION_NUMBER > 3001002 << QString::fromLatin1("application/x-lz4") << QString::fromLatin1("application/x-lz4-compressed-tar") #endif #if ARCHIVE_VERSION_NUMBER > 3003002 << QString::fromLatin1("application/zstd") << QString::fromLatin1("application/x-zstd") << QString::fromLatin1("application/x-zstd-compressed-tar") #endif << QString::fromLatin1("application/x-tar") << QString::fromLatin1("application/x-compressed-tar") << QString::fromLatin1("application/x-bzip-compressed-tar") << QString::fromLatin1("application/x-gzip-compressed-tar") << QString::fromLatin1("application/x-tarz") << QString::fromLatin1("application/x-xz-compressed-tar") << QString::fromLatin1("application/x-lzma-compressed-tar") << QString::fromLatin1("application/x-java-archive") << QString::fromLatin1("application/zip") << QString::fromLatin1("application/x-7z-compressed") << QString::fromLatin1("application/x-iso9660-image") << QString::fromLatin1("application/x-apple-diskimage") << QString::fromLatin1("application/x-cd-image") << QString::fromLatin1("application/x-raw-disk-image") << QString::fromLatin1("application/x-archive") << QString::fromLatin1("application/x-xar") << QString::fromLatin1("application/x-deb") << QString::fromLatin1("application/x-cd-image") << QString::fromLatin1("application/x-bcpio") << QString::fromLatin1("application/x-cpio") << QString::fromLatin1("application/x-cpio-compressed") << QString::fromLatin1("application/x-sv4cpio") << QString::fromLatin1("application/x-sv4crc") << QString::fromLatin1("application/x-rpm") << QString::fromLatin1("application/x-source-rpm") << QString::fromLatin1("application/vnd.ms-cab-compressed") << QString::fromLatin1("application/x-servicepack") << QString::fromLatin1("application/x-compress") << QString::fromLatin1("application/x-lzip"); return s_writemimetypes; #else return QStringList(); #endif // HAVE_LIBARCHIVE }