diff --git a/karchivemanager/CMakeLists.txt b/karchivemanager/CMakeLists.txt index 52daa7b6..74320d8a 100644 --- a/karchivemanager/CMakeLists.txt +++ b/karchivemanager/CMakeLists.txt @@ -9,30 +9,25 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) endif() -find_package(LibArchive 3.0.3) -set_package_properties(LibArchive PROPERTIES - DESCRIPTION "A library for dealing with a wide variety of archive file formats" - URL "https://www.libarchive.org/" - PURPOSE "Required for among others tar, tar.gz, tar.bz2 formats in KArchiveManager" - TYPE REQUIRED -) - -include_directories(${LibArchive_INCLUDE_DIRS}) - set(karchivemanager_sources karchiveapp.cpp karchivemanager.cpp main.cpp - # TODO: only if system does not have strmode() - strmode.c ) add_executable(karchivemanager ${karchivemanager_sources}) target_link_libraries(karchivemanager KDE4::kdeui KDE4::kio - ${LibArchive_LIBRARIES} + KDE4::karchive ) -install(TARGETS karchivemanager DESTINATION ${KDE4_BIN_INSTALL_DIR}) -install(PROGRAMS karchivemanager.desktop DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR}) +install( + TARGETS karchivemanager + DESTINATION ${KDE4_BIN_INSTALL_DIR} +) + +install( + PROGRAMS karchivemanager.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) diff --git a/karchivemanager/karchiveapp.cpp b/karchivemanager/karchiveapp.cpp index 95ac4868..6b5b0d99 100644 --- a/karchivemanager/karchiveapp.cpp +++ b/karchivemanager/karchiveapp.cpp @@ -66,7 +66,7 @@ class KArchiveAppPrivate { Ui_KArchiveAppWindow ui; KArchiveModel m_model; - KArchiveManager *m_archive; + KArchive *m_archive; }; KArchiveApp::KArchiveApp() @@ -109,15 +109,16 @@ void KArchiveApp::changePath(const QString path) { delete d->m_archive; } - d->m_archive = new KArchiveManager(path); + d->m_archive = new KArchive(path); d->m_model.loadArchive(d->m_archive); statusBar()->showMessage(path); - const bool iswritable = d->m_archive->writable(); + const bool iswritable = d->m_archive->isWritable(); + const bool isreadable = d->m_archive->isReadable(); d->ui.actionAdd->setEnabled(iswritable); d->ui.actionRemove->setEnabled(iswritable); - d->ui.actionExtract->setEnabled(iswritable); + d->ui.actionExtract->setEnabled(isreadable); } void KArchiveApp::slotOpenAction() { diff --git a/karchivemanager/karchivemanager.cpp b/karchivemanager/karchivemanager.cpp index 8f186209..2b57017e 100644 --- a/karchivemanager/karchivemanager.cpp +++ b/karchivemanager/karchivemanager.cpp @@ -29,972 +29,57 @@ #include #include +#include +#include + #include "karchivemanager.hpp" -#include -#include -#include -#include - -extern "C" { void strmode(mode_t mode, char *str); }; - -#define KARCHIVEMANAGER_BUFFSIZE 10240 - -static const QStringList s_writemimes = QStringList() -#if ARCHIVE_VERSION_NUMBER > 3000004 - << "application/x-lzop" -#endif -#if ARCHIVE_VERSION_NUMBER > 3001002 - << "application/x-lz4" -#endif -#if ARCHIVE_VERSION_NUMBER > 3003002 - << "application/zstd" - << "application/x-zstd-compressed-tar" -#endif - << "application/x-tar" - << "application/x-compressed-tar" - << "application/x-bzip" - << "application/x-gzip" - << "application/x-bzip-compressed-tar" - << "application/x-gzip-compressed-tar" - << "application/x-tarz" - << "application/x-xz" - << "application/x-xz-compressed-tar" - << "application/x-lzma-compressed-tar" - << "application/x-java-archive" - << "application/zip" - << "application/x-7z-compressed" - << "application/x-iso9660-image" - << "application/x-apple-diskimage" - << "application/x-cd-image" - << "application/x-raw-disk-image"; - -KArchiveInfo::KArchiveInfo() - : encrypted(false), - size(0), - gid(-1), - uid(-1), - mode(0), - type(KArchiveInfo::None) { -} - -KArchiveInfo::KArchiveInfo(const KArchiveInfo &info) - : encrypted(info.encrypted), - size(info.size), - gid(info.gid), - uid(info.uid), - mode(info.mode), - hardlink(info.hardlink), - symlink(info.symlink), - pathname(info.pathname), - mimetype(info.mimetype), - type(info.type) { -} - -QString KArchiveInfo::fancyEncrypted() const { - if (encrypted) { - return QString::fromLatin1("yes"); - } - return QString::fromLatin1("No"); -} - -QString KArchiveInfo::fancySize() const { - return KGlobal::locale()->formatByteSize(size, 1); -} - -QString KArchiveInfo::fancyMode() const { - if (mode == 0) { - return QString::fromLatin1("---------"); - } - - char buffer[20]; - ::strmode(mode, buffer); - - return QString::fromLatin1(buffer); -} - -QString KArchiveInfo::fancyType() const { - switch (type) { - case KArchiveType::None: { - return "None"; - } - case KArchiveType::File: { - return "File"; - } - case KArchiveType::Directory: { - return "Directory"; - } - case KArchiveType::Link: { - return "Link"; - } - case KArchiveType::Character: { - return "Character"; - } - case KArchiveType::Block: { - return "Block"; - } - case KArchiveType::Fifo: { - return "Fifo"; - } - case KArchiveType::Socket: { - return "Socket"; - } - } - - Q_UNREACHABLE(); - return QString(); -} - -QIcon KArchiveInfo::fancyIcon() const { - switch (type) { - case KArchiveType::None: { - return QIcon::fromTheme("unknown"); - } - case KArchiveType::File: { - const KMimeType::Ptr mime = KMimeType::mimeType(mimetype); - if (mime) { - return QIcon(KIconLoader::global()->loadMimeTypeIcon(mime->iconName(), KIconLoader::Small)); - } - return QIcon::fromTheme("unknown"); - } - case KArchiveType::Directory: { - return QIcon::fromTheme("folder"); - } - case KArchiveType::Link: { - return QIcon::fromTheme("emblem-symbolic-link"); - } - case KArchiveType::Character: - case KArchiveType::Block: { - return QIcon::fromTheme("drive-harddisk"); - } - case KArchiveType::Fifo: - case KArchiveType::Socket: { - return QIcon::fromTheme("media-memory"); - } - } - - Q_UNREACHABLE(); - return QIcon(); -} - -bool KArchiveInfo::isNull() const { - return (type == KArchiveType::None); -} - -bool KArchiveInfo::operator==(const KArchiveInfo &info) const { - return (pathname == info.pathname); -} - -KArchiveInfo& KArchiveInfo::operator=(const KArchiveInfo &info) { - encrypted = info.encrypted; - size = info.size; - gid = info.gid; - uid = info.uid; - mode = info.mode; - hardlink = info.hardlink; - symlink = info.symlink; - pathname = info.pathname; - mimetype = info.mimetype; - type = info.type; - return *this; -} - -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug d, const KArchiveInfo &info) +static QIcon kFancyIcon(const mode_t mode, const QByteArray &pathname) { - d << "KArchiveInfo( encrypted:" << info.fancyEncrypted() - << ", size:" << info.fancySize() - << ", gid:" << info.gid - << ", uid:" << info.uid - << ", mode:" << info.fancyMode() - << ", hardlink:" << info.hardlink - << ", symlink:" << info.symlink - << ", pathname:" << info.pathname - << ", mimetype:" << info.mimetype - << ", type:" << info.fancyType() - << ")"; - return d; -} -#endif - -const QDBusArgument &operator<<(QDBusArgument &argument, const KArchiveInfo &info) { - argument.beginStructure(); - argument << info.encrypted; - argument << qlonglong(info.size); - argument << qlonglong(info.gid); - argument << qlonglong(info.uid); - argument << info.mode; - argument << info.hardlink; - argument << info.symlink; - argument << info.pathname; - argument << info.mimetype; - argument << int(info.type); - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, KArchiveInfo &info) { - int typebuf; - qlonglong sizebuff; - qlonglong gidbuff; - qlonglong uidbuff; - argument.beginStructure(); - argument >> info.encrypted; - argument >> sizebuff; - info.size = int64_t(sizebuff); - argument >> gidbuff; - info.gid = int64_t(gidbuff); - argument >> uidbuff; - info.uid = int64_t(uidbuff); - argument >> info.mode; - argument >> info.hardlink; - argument >> info.symlink; - argument >> info.pathname; - argument >> info.mimetype; - argument >> typebuf; - info.type = KArchiveInfo::KArchiveType(typebuf); - argument.endStructure(); - - return argument; -} - -class KArchiveManagerPrivate { - - public: - QString m_path; - bool m_writable; - - static struct archive* openRead(const QByteArray &path); - static struct archive* openWrite(const QByteArray &path); - static struct archive* openDisk(const bool preserve); - static bool closeRead(struct archive*); - static bool closeWrite(struct archive*); - - static bool copyData(struct archive* aread, struct archive* awrite); - static bool copyData(struct archive* aread, QByteArray &buffer); - - static QByteArray getMime(struct archive* aread); - static KArchiveInfo::KArchiveType getType(const mode_t mode); -}; - -struct archive* KArchiveManagerPrivate::openRead(const QByteArray &path) { - struct archive* m_archive = archive_read_new(); - - if (m_archive) { - archive_read_support_filter_all(m_archive); - archive_read_support_format_all(m_archive); - - if (archive_read_open_filename(m_archive, path, KARCHIVEMANAGER_BUFFSIZE) != ARCHIVE_OK) { - kWarning() << "archive_read_open_filename" << archive_error_string(m_archive); - } - } - - return m_archive; -} - -struct archive* KArchiveManagerPrivate::openWrite(const QByteArray &path) { - struct archive* m_archive = archive_write_new(); - - if (m_archive) { - if (archive_write_set_format_filter_by_ext(m_archive, path) != ARCHIVE_OK) { - kWarning() << "archive_write_set_format_filter_by_ext" << archive_error_string(m_archive); - archive_write_add_filter_none(m_archive); - } - - archive_write_set_format_pax_restricted(m_archive); - - if (archive_write_open_filename(m_archive, path) != ARCHIVE_OK) { - kWarning() << "archive_write_open_filename" << archive_error_string(m_archive); - } - } - - return m_archive; -} - -struct archive* KArchiveManagerPrivate::openDisk(const bool preserve) { - struct archive* m_archive = archive_write_disk_new(); - - if (m_archive) { - 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; - } - - archive_write_disk_set_options(m_archive, extractFlags); - archive_write_disk_set_standard_lookup(m_archive); - } - - return m_archive; -} - -bool KArchiveManagerPrivate::closeRead(struct archive* m_archive) { - if (m_archive) { - if (archive_read_close(m_archive) != ARCHIVE_OK) { - kWarning() << "archive_read_close" << archive_error_string(m_archive); - return false; - } - - if (archive_read_free(m_archive) != ARCHIVE_OK) { - kWarning() << "archive_read_free" << archive_error_string(m_archive); - return false; - } - } - - return true; -} - -bool KArchiveManagerPrivate::closeWrite(struct archive* m_archive) { - if (m_archive) { - if (archive_write_close(m_archive) != ARCHIVE_OK) { - kWarning() << "archive_write_close" << archive_error_string(m_archive); - return false; - } - - if (archive_write_free(m_archive) != ARCHIVE_OK) { - kWarning() << "archive_write_free" << archive_error_string(m_archive); - return false; - } - } - - return true; -} - -bool KArchiveManagerPrivate::copyData(struct archive* aread, struct archive* awrite) { - char readbuffer[KARCHIVEMANAGER_BUFFSIZE]; - ssize_t readsize = archive_read_data(aread, readbuffer, sizeof(readbuffer)); - while (readsize > 0) { - const int result = archive_errno(aread); - if (result != ARCHIVE_OK) { - kWarning() << "archive_read_data" << archive_error_string(aread); - return false; - } - - if (archive_write_data(awrite, readbuffer, readsize) != readsize) { - kWarning() << "archive_write_data" << archive_error_string(awrite); - return false; - } - - readsize = archive_read_data(aread, readbuffer, sizeof(readbuffer)); - } - - return true; -} - - -bool KArchiveManagerPrivate::copyData(struct archive* aread, QByteArray &buffer) { - char readbuffer[KARCHIVEMANAGER_BUFFSIZE]; - ssize_t readsize = archive_read_data(aread, readbuffer, sizeof(readbuffer)); - while (readsize > 0) { - const int result = archive_errno(aread); - if (result != ARCHIVE_OK) { - kWarning() << "archive_read_data" << archive_error_string(aread); - return false; - } - - // QByteArray::append() will attempt to copy the null terminator but this is raw data not - // string so instruct it to copy one less bit and will do the right thing - buffer.append(readbuffer, readsize - 1); - - readsize = archive_read_data(aread, readbuffer, sizeof(readbuffer)); - } - - return true; -} - -QByteArray KArchiveManagerPrivate::getMime(struct archive* aread) { - QByteArray buffer; - copyData(aread, buffer); - const KMimeType::Ptr mime = KMimeType::findByContent(buffer); - if (mime) { - return mime->name().toLatin1(); - } - return QByteArray("application/octet-stream"); -} - -KArchiveInfo::KArchiveType KArchiveManagerPrivate::getType(const mode_t mode) { if (S_ISREG(mode)) { - return KArchiveInfo::KArchiveType::File; - } else if (S_ISDIR(mode)) { - return KArchiveInfo::KArchiveType::Directory; - } else if (S_ISLNK(mode)) { - return KArchiveInfo::KArchiveType::Link; - } else if (S_ISCHR(mode)) { - return KArchiveInfo::KArchiveType::Character; - } else if (S_ISBLK(mode)) { - return KArchiveInfo::KArchiveType::Block; - } else if (S_ISFIFO(mode)) { - return KArchiveInfo::KArchiveType::Fifo; - } else if (S_ISSOCK(mode)) { - return KArchiveInfo::KArchiveType::Socket; - } else { - return KArchiveInfo::KArchiveType::None; + const KMimeType::Ptr mime = KMimeType::findByName(QFile::decodeName(pathname)); + if (mime) { + return QIcon(KIconLoader::global()->loadMimeTypeIcon(mime->iconName(), KIconLoader::Small)); + } + return QIcon::fromTheme("unknown"); } + if (S_ISDIR(mode)) { + return QIcon::fromTheme("folder"); + } + if (S_ISLNK(mode)) { + return QIcon::fromTheme("emblem-symbolic-link"); + } + if (S_ISCHR(mode) || S_ISBLK(mode)) { + return QIcon::fromTheme("drive-harddisk"); + } + if (S_ISFIFO(mode) || S_ISSOCK(mode)) { + return QIcon::fromTheme("media-memory"); + } + return QIcon::fromTheme("unknown"); } -KArchiveManager::KArchiveManager(const QString &path) - : d(new KArchiveManagerPrivate()) { - qRegisterMetaType(); - qDBusRegisterMetaType(); - - d->m_path = path; - - if (path.isEmpty()) { - kWarning() << "empty path"; - return; - } - - if (QFile::exists(path) && !supported(path)) { - kWarning() << "unsupported" << path; - d->m_writable = false; - return; - } - - const KMimeType::Ptr mime = KMimeType::findByUrl(path); - if (mime) { - if (s_writemimes.contains(mime->name())) { - d->m_writable = true; - kDebug() << "path is writable" << path << mime->name(); - } - } else { - d->m_writable = false; - } -} - -KArchiveManager::~KArchiveManager() { - if (d) { - delete d; - } -} - -bool KArchiveManager::add(const QStringList &paths, const QByteArray &strip, const QByteArray &destination) const { - bool result = false; - - if (d->m_path.isEmpty()) { - kWarning() << "no path is set"; - return result; - } else if (!d->m_writable) { - kWarning() << "path is not writable"; - return result; - } - - QFileInfo fileinfo(d->m_path); - QTemporaryFile tmpfile("XXXXXX." + fileinfo.completeSuffix()); - if (!tmpfile.open()) { - kWarning() << "could not open temporary file"; - return result; - } - - const QByteArray tmppath = tmpfile.fileName().toLocal8Bit(); - struct archive* m_write = d->openWrite(tmppath); - if (!m_write) { - kWarning() << "could not open temporary archive" << tmppath; - return result; - } - - QStringList recursivepaths; - foreach (const QString &path, paths) { - if (QDir(path).exists()) { - QDirIterator iterator(path, QDir::AllEntries | QDir::System, QDirIterator::Subdirectories); - while (iterator.hasNext()) { - recursivepaths << QDir::cleanPath(iterator.next()); - } - } else { - recursivepaths << QDir::cleanPath(path); - } - } - recursivepaths.removeDuplicates(); - - bool replace = false; - if (QFile::exists(d->m_path)) { - - replace = true; - - struct archive* m_read = d->openRead(d->m_path.toLocal8Bit()); - if (!m_read) { - kWarning() << "could not open archive" << d->m_path; - return result; - } - - struct archive_entry* entry = archive_entry_new(); - int ret = archive_read_next_header(m_read, &entry); - while (ret != ARCHIVE_EOF) { - if (ret < ARCHIVE_OK) { - kWarning() << "archive_read_next_header" << archive_error_string(m_read); - result = false; - break; - } - - const QByteArray pathname = archive_entry_pathname(entry); - if (recursivepaths.contains(strip + pathname)) { - kDebug() << "removing (update)" << pathname; - archive_read_data_skip(m_read); - ret = archive_read_next_header(m_read, &entry); - continue; - } - - if (archive_write_header(m_write, entry) != ARCHIVE_OK) { - kWarning() << "archive_write_header" << archive_error_string(m_write); - result = false; - break; - } - - if (!d->copyData(m_read, m_write)) { - result = false; - break; - } - - if (archive_write_finish_entry(m_write) != ARCHIVE_OK) { - kWarning() << "archive_write_finish_entry" << archive_error_string(m_write); - result = false; - break; - } - - ret = archive_read_next_header(m_read, &entry); - } - - d->closeRead(m_read); - } - - foreach (const QString &path, recursivepaths) { - const QByteArray localpath = path.toLocal8Bit(); - - struct stat statistic; - if (::lstat(localpath, &statistic) != 0) { - kWarning() << "stat error" << ::strerror(errno); - result = false; - break; - } - - QByteArray pathname = localpath; - if (pathname.startsWith(strip)) { - pathname.remove(0, strip.size()); - } - pathname.prepend(destination); - 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); - archive_entry_set_size(newentry, statistic.st_size); - archive_entry_set_gid(newentry, statistic.st_gid); - archive_entry_set_uid(newentry, statistic.st_uid); - 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); - - // filetype and mode are supposedly the same, permissions are set when mode is set - archive_entry_set_mode(newentry, statistic.st_mode); - - if (statistic.st_nlink > 1) { - // TODO: archive_entry_set_hardlink(newentry, pathname); - } - - if (S_ISLNK(statistic.st_mode)) { - QByteArray linkbuffer(PATH_MAX + 1, Qt::Uninitialized); - if (::readlink(localpath, linkbuffer.data(), PATH_MAX) == -1) { - kWarning() << "readlink error" << ::strerror(errno); - result = false; - break; - } - - if (linkbuffer.startsWith(strip)) { - linkbuffer.remove(0, strip.size()); - } - - archive_entry_set_symlink(newentry, linkbuffer.constData()); - } - - if (archive_write_header(m_write, newentry) != ARCHIVE_OK) { - kWarning() << "archive_write_header" << archive_error_string(m_write); - 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)) { - kWarning() << "could not open" << path; - result = false; - break; - } - - const QByteArray data = file.readAll(); - if (data.isEmpty() && statistic.st_size > 0) { - kWarning() << "could not read" << path; - result = false; - break; - } - - if (statistic.st_size > 0 && data.size() != statistic.st_size) { - kWarning() << "read and stat size are different" << path; - result = false; - break; - } - - if (archive_write_data(m_write, data.constData(), data.size()) != statistic.st_size) { - kWarning() << "archive_write_data" << archive_error_string(m_write); - result = false; - break; - } - } - - if (archive_write_finish_entry(m_write) != ARCHIVE_OK) { - kWarning() << "archive_write_finish_entry" << archive_error_string(m_write); - result = false; - break; - } - - result = true; - } - - d->closeWrite(m_write); - - if (result && replace) { - result = QFile::remove(d->m_path); - if (!result) { - kWarning() << "could not remove original" << d->m_path; - } - } - - if (result) { - result = QFile::rename(tmppath, d->m_path); - if (!result) { - kWarning() << "could not move" << tmppath << "to" << d->m_path; - } - } - - return result; -} - -bool KArchiveManager::remove(const QStringList &paths) const { - bool result = false; - - if (d->m_path.isEmpty()) { - kWarning() << "no path is set"; - return result; - } else if (!d->m_writable) { - kWarning() << "path is not writable"; - return result; - } else if (!QFile::exists(d->m_path)) { - kWarning() << "path does not exists" << d->m_path; - return result; - } - - struct archive* m_read = d->openRead(d->m_path.toLocal8Bit()); - if (!m_read) { - kWarning() << "could not open archive" << d->m_path; - return result; - } - - QFileInfo fileinfo(d->m_path); - QTemporaryFile tmpfile("XXXXXX." + fileinfo.completeSuffix()); - if (!tmpfile.open()) { - kWarning() << "could not open temporary file"; - return result; - } - - const QByteArray tmppath = tmpfile.fileName().toLocal8Bit(); - struct archive* m_write = d->openWrite(tmppath); - if (!m_write) { - kWarning() << "could not open temporary archive" << tmppath; - return result; - } - - QStringList notfound = paths; - - struct archive_entry* entry = archive_entry_new(); - int ret = archive_read_next_header(m_read, &entry); - while (ret != ARCHIVE_EOF) { - if (ret < ARCHIVE_OK) { - kWarning() << "archive_read_next_header" << archive_error_string(m_read); - result = false; - break; - } - - const QByteArray pathname = archive_entry_pathname(entry); - if (paths.contains(pathname)) { - kDebug() << "removing" << pathname; - notfound.removeAll(pathname); - archive_read_data_skip(m_read); - ret = archive_read_next_header(m_read, &entry); - result = true; - continue; - } - - if (archive_write_header(m_write, entry) != ARCHIVE_OK) { - kWarning() << "archive_write_header" << archive_error_string(m_write); - result = false; - break; - } - - if (!d->copyData(m_read, m_write)) { - result = false; - break; - } - - if (archive_write_finish_entry(m_write) != ARCHIVE_OK) { - kWarning() << "archive_write_finish_entry" << archive_error_string(m_write); - result = false; - break; - } - - ret = archive_read_next_header(m_read, &entry); - } - - d->closeWrite(m_write); - d->closeRead(m_read); - - if (result) { - kDebug() << "replacing" << d->m_path << "with" << tmppath; - - result = QFile::remove(d->m_path); - if (result) { - result = QFile::rename(tmppath, d->m_path); - if (!result) { - kWarning() << "could not move" << tmppath << "to" << d->m_path; - } - } else { - kWarning() << "could not remove original" << d->m_path; - } - } - - if (!notfound.isEmpty()) { - kWarning() << "entries not in archive" << notfound; - } - - return result; -} - -bool KArchiveManager::extract(const QStringList &paths, const QString &destination, bool preserve) const { - bool result = false; - - if (d->m_path.isEmpty()) { - kWarning() << "no path is set"; - return result; - } else if (!QFile::exists(d->m_path)) { - kWarning() << "path does not exists" << d->m_path; - return result; - } - - struct archive* m_read = d->openRead(d->m_path.toLocal8Bit()); - if (!m_read) { - kWarning() << "could not open archive" << d->m_path; - return result; - } - - const QString currentdir = QDir::currentPath(); - if (!QDir::setCurrent(destination)) { - kWarning() << "could not change to destination directory" << destination; - return result; - } - - struct archive* m_write = d->openDisk(preserve); - if (!m_write) { - kWarning() << "could not open write archive"; - return result; - } - - QStringList notfound = paths; - - struct archive_entry* entry = archive_entry_new(); - int ret = archive_read_next_header(m_read, &entry); - while (ret != ARCHIVE_EOF) { - if (ret < ARCHIVE_OK) { - kWarning() << "archive_read_next_header" << archive_error_string(m_read); - result = false; - break; - } - - const QByteArray pathname = archive_entry_pathname(entry);; - if (!paths.contains(pathname)) { - archive_read_data_skip(m_read); - ret = archive_read_next_header(m_read, &entry); - continue; - } - notfound.removeAll(pathname); - result = true; - - if (archive_write_header(m_write, entry) != ARCHIVE_OK) { - kWarning() << "archive_write_header" << archive_error_string(m_write); - result = false; - break; - } - - if (archive_read_extract2(m_read, entry, m_write) != ARCHIVE_OK) { - kWarning() << "archive_read_extract2" << archive_error_string(m_write); - result = false; - break; - } - - if (archive_write_finish_entry(m_write) != ARCHIVE_OK) { - kWarning() << "archive_write_finish_entry" << archive_error_string(m_write); - result = false; - break; - } - - ret = archive_read_next_header(m_read, &entry); - } - - d->closeWrite(m_write); - d->closeRead(m_read); - - Q_ASSERT_X(currentdir == QDir::currentPath(), "KArchiveManager::extract", "Current directory changed"); - if (!QDir::setCurrent(currentdir)) { - kWarning() << "could not change to orignal directory" << currentdir; - } - - if (!notfound.isEmpty()) { - kWarning() << "entries not in archive" << notfound; - } - - return result; -} - -QList KArchiveManager::list() const { - QList result; - - if (d->m_path.isEmpty()) { - kWarning() << "no path is set"; - return result; - } else if (!QFile::exists(d->m_path)) { - kWarning() << "path does not exists" << d->m_path; - return result; - } - - struct archive* m_read = d->openRead(d->m_path.toLocal8Bit()); - if (!m_read) { - kWarning() << "could not open archive" << d->m_path; - return result; - } - - struct archive_entry* entry = archive_entry_new(); - int ret = archive_read_next_header(m_read, &entry); - while (ret != ARCHIVE_EOF) { - if (ret < ARCHIVE_OK) { - kWarning() << "archive_read_next_header" << archive_error_string(m_read); - result.clear(); - break; - } - - KArchiveInfo info; - info.encrypted = bool(archive_entry_is_encrypted(entry)); - info.size = archive_entry_size(entry); - info.gid = archive_entry_gid(entry); - info.uid = archive_entry_uid(entry); - info.mode = archive_entry_mode(entry); - info.hardlink = QByteArray(archive_entry_hardlink(entry)); - info.symlink = QByteArray(archive_entry_symlink(entry)); - info.pathname = archive_entry_pathname(entry); - info.mimetype = d->getMime(m_read); - info.type = d->getType(info.mode); - - result << info; - - ret = archive_read_next_header(m_read, &entry); - } - - d->closeRead(m_read); - - return result; -} - -KArchiveInfo KArchiveManager::info(const QString &path) const { - KArchiveInfo result; - - if (d->m_path.isEmpty()) { - kWarning() << "no path is set"; - return result; - } - - struct archive* m_read = d->openRead(d->m_path.toLocal8Bit()); - if (!m_read) { - kWarning() << "could not open archive" << d->m_path; - return result; - } - - bool found = false; - struct archive_entry* entry = archive_entry_new(); - int ret = archive_read_next_header(m_read, &entry); - while (ret != ARCHIVE_EOF) { - if (ret < ARCHIVE_OK) { - kWarning() << "archive_read_next_header" << archive_error_string(m_read); - break; - } - - const QByteArray pathname = archive_entry_pathname(entry); - if (pathname == 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.hardlink = QByteArray(archive_entry_hardlink(entry)); - result.symlink = QByteArray(archive_entry_symlink(entry)); - result.pathname = pathname; - result.mimetype = d->getMime(m_read); - result.type = d->getType(result.mode); - - found = true; - break; - } - - ret = archive_read_next_header(m_read, &entry); - } - - d->closeRead(m_read); - - if (!found) { - kWarning() << "entry not in archive" << path; - } - - return result; -} - -bool KArchiveManager::writable() const { - return d->m_writable; -} - -bool KArchiveManager::supported(const QString &path) { - bool result = false; - - struct archive* m_read = KArchiveManagerPrivate::openRead(path.toLocal8Bit()); - if (m_read) { - result = true; - } - - KArchiveManagerPrivate::closeRead(m_read); - - return result; -} - -class KArchiveModelPrivate : public QThread { +class KArchiveModelPrivate : public QThread +{ Q_OBJECT +public: + KArchiveModelPrivate(QObject *parent = nullptr); - public: - KArchiveModelPrivate(QObject *parent = nullptr); + QString joinDir(const QString &dir1, const QString &dir2) const; + QStandardItem* makeColumn(const QString &string) const; - QString joinDir(const QString &dir1, const QString &dir2) const; - QStandardItem* makeColumn(const QString &string) const; + void appendDirectory(const QString &path); + void appendSpecial(const KArchiveEntry &info); - void appendDirectory(const QString &path); - void appendSpecial(const KArchiveInfo &info); + QStandardItem* m_root; + QMap m_directories; + QList m_list; + const KArchive *m_archive; + bool m_interrupt; - QStandardItem* m_root; - QMap m_directories; - QList m_list; - const KArchiveManager *m_archive; - bool m_interrupt; + void requestInterruption(); - void requestInterruption(); - - protected: - virtual void run(); +protected: + void run() final; }; KArchiveModelPrivate::KArchiveModelPrivate(QObject *parent) @@ -1003,21 +88,24 @@ KArchiveModelPrivate::KArchiveModelPrivate(QObject *parent) } // because altering paths is not easy -QString KArchiveModelPrivate::joinDir(const QString &dir1, const QString &dir2) const { +QString KArchiveModelPrivate::joinDir(const QString &dir1, const QString &dir2) const +{ if (dir1.isEmpty()) { return dir2; } return dir1 + "/" + dir2; } -QStandardItem* KArchiveModelPrivate::makeColumn(const QString &string) const { +QStandardItem* KArchiveModelPrivate::makeColumn(const QString &string) const +{ QStandardItem* item = new QStandardItem(string); item->setSelectable(false); item->setTextAlignment(Qt::AlignRight); return item; } -void KArchiveModelPrivate::appendDirectory(const QString &path) { +void KArchiveModelPrivate::appendDirectory(const QString &path) +{ QStandardItem* lastitem = m_root; QString dirsofar; foreach (const QString &dir, path.split("/")) { @@ -1032,8 +120,8 @@ void KArchiveModelPrivate::appendDirectory(const QString &path) { } // NOTE: directory entries are not consistent - KArchiveInfo info; - foreach (const KArchiveInfo &listinfo, m_list) { + KArchiveEntry info; + foreach (const KArchiveEntry &listinfo, m_list) { if (listinfo.pathname == dirsofar || listinfo.pathname == dirsofar + "/") { info = listinfo; break; @@ -1041,12 +129,12 @@ void KArchiveModelPrivate::appendDirectory(const QString &path) { } if (info.isNull()) { // fake it for now - info.type = KArchiveInfo::Directory; + info.mode |= S_IFDIR; } QList diritems; QStandardItem* diritem = new QStandardItem(dir); - diritem->setIcon(info.fancyIcon()); + diritem->setIcon(kFancyIcon(info.mode, info.pathname)); diritem->setWhatsThis("Directory"); diritem->setStatusTip(dirsofar); diritems << diritem; @@ -1062,7 +150,8 @@ void KArchiveModelPrivate::appendDirectory(const QString &path) { } } -void KArchiveModelPrivate::appendSpecial(const KArchiveInfo &info) { +void KArchiveModelPrivate::appendSpecial(const KArchiveEntry &info) +{ const QFileInfo fileinfo(info.pathname); const QString infopath = fileinfo.path(); const QString infoname = fileinfo.fileName(); @@ -1071,7 +160,7 @@ void KArchiveModelPrivate::appendSpecial(const KArchiveInfo &info) { QList specialitems; QStandardItem* specialitem = new QStandardItem(infoname); - specialitem->setIcon(info.fancyIcon()); + specialitem->setIcon(kFancyIcon(info.mode, info.pathname)); specialitem->setStatusTip(info.pathname); specialitems << specialitem; specialitems << makeColumn(info.fancyType()); @@ -1087,16 +176,17 @@ void KArchiveModelPrivate::appendSpecial(const KArchiveInfo &info) { } } -void KArchiveModelPrivate::run() { +void KArchiveModelPrivate::run() +{ m_list = m_archive->list(); m_interrupt = false; - foreach (const KArchiveInfo &info, m_list) { + foreach (const KArchiveEntry &info, m_list) { if(m_interrupt) { return; } - if (info.type == KArchiveInfo::KArchiveType::Directory) { + if (S_ISDIR(info.mode)) { appendDirectory(info.pathname); } else { appendSpecial(info); @@ -1104,7 +194,8 @@ void KArchiveModelPrivate::run() { } } -void KArchiveModelPrivate::requestInterruption() { +void KArchiveModelPrivate::requestInterruption() +{ m_interrupt = true; } @@ -1114,13 +205,15 @@ KArchiveModel::KArchiveModel(QObject *parent) connect(d, SIGNAL(finished()), this, SLOT(slotLoadFinished())); } -KArchiveModel::~KArchiveModel() { +KArchiveModel::~KArchiveModel() +{ if (d) { delete d; } } -bool KArchiveModel::loadArchive(const KArchiveManager *archive) { +bool KArchiveModel::loadArchive(const KArchive *archive) +{ bool result = false; if (!archive) { @@ -1163,14 +256,15 @@ QString KArchiveModel::path(const QModelIndex &index) const { return result; } -QStringList KArchiveModel::paths(const QModelIndex &index) const { +QStringList KArchiveModel::paths(const QModelIndex &index) const +{ QStringList result; const QByteArray indexpath = path(index).toLocal8Bit(); if (!indexpath.isEmpty() && index.isValid()) { const QStandardItem *item = itemFromIndex(index); if (item->whatsThis() == "Directory") { - foreach (const KArchiveInfo &info, d->m_list) { + foreach (const KArchiveEntry &info, d->m_list) { if (info.pathname.startsWith(indexpath)) { result << info.pathname; } @@ -1183,7 +277,8 @@ QStringList KArchiveModel::paths(const QModelIndex &index) const { return result; } -QString KArchiveModel::dir(const QModelIndex &index) const { +QString KArchiveModel::dir(const QModelIndex &index) const +{ QString result; if (!index.isValid()) { @@ -1201,7 +296,8 @@ QString KArchiveModel::dir(const QModelIndex &index) const { return result; } -void KArchiveModel::slotLoadFinished() { +void KArchiveModel::slotLoadFinished() +{ setHeaderData(0, Qt::Horizontal, QVariant("Name")); setHeaderData(1, Qt::Horizontal, QVariant("Type")); setHeaderData(2, Qt::Horizontal, QVariant("Size")); diff --git a/karchivemanager/karchivemanager.hpp b/karchivemanager/karchivemanager.hpp index b19e9082..8fed5fd2 100644 --- a/karchivemanager/karchivemanager.hpp +++ b/karchivemanager/karchivemanager.hpp @@ -22,139 +22,7 @@ #include #include #include -#include - -#include - -/*! - Archive entry information holder, valid object is obtained via @p KArchiveManager::info - - @note It is up to the programmer to keep the integrity of the structure - @note D-Bus signature for the type is (bxxxussssi) - @warning The structure is not very portable (size, gid, uid) - @ingroup Types - - @see KArchiveManager - @see https://dbus.freedesktop.org/doc/dbus-specification.html#container-types -*/ -class KArchiveInfo { - - public: - enum KArchiveType { - None = 0, - File = 1, - Directory = 2, - Link = 3, - Character = 4, - Block = 5, - Fifo = 6, - Socket = 7, - }; - - KArchiveInfo(); - KArchiveInfo(const KArchiveInfo &info); - - bool encrypted; - int64_t size; - int64_t gid; - int64_t uid; - mode_t mode; - QByteArray hardlink; - QByteArray symlink; - QByteArray pathname; - QByteArray mimetype; - KArchiveType type; - - //! @brief Fancy encrypted for the purpose of widgets - QString fancyEncrypted() const; - //! @brief Fancy size for the purpose of widgets - QString fancySize() const; - //! @brief Fancy mode for the purpose of widgets - QString fancyMode() const; - //! @brief Fancy type for the purpose of widgets - QString fancyType() const; - //! @brief Fancy icon for the purpose of widgets - QIcon fancyIcon() const; - - //! @brief Returns if the info is valid or not - bool isNull() const; - - bool operator==(const KArchiveInfo &i) const; - KArchiveInfo &operator=(const KArchiveInfo &i); -}; -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug, const KArchiveInfo &i); -#endif -const QDBusArgument &operator<<(QDBusArgument &, const KArchiveInfo &i); -const QDBusArgument &operator>>(const QDBusArgument &, KArchiveInfo &i); - -class KArchiveManagerPrivate; - -/*! - Archive manager with support for many formats - - Example: - \code - KArchiveManager archive("/home/joe/archive.tar.gz"); - kDebug() << archive.list(); - - QDir::mkpath("/tmp/destination"); - archive.extract("dir/in/archive/", "/tmp/destination"); - archive.delete("file/in/archive.txt"); - \endcode - - @note Paths ending with "/" will be considered as directories - @warning The operations are done on temporary file, copy of the orignal, which after - successfull operation (add or remove) replaces the orignal thus if it is interrupted the - source may get corrupted - - @see KArchiveInfo - - @todo encrypted paths handling - @todo make listing consistent by faking dir info? - @todo set permissions on file after copy, same as the original - @todo error reporting -*/ -class KArchiveManager { - - public: - KArchiveManager(const QString &path); - ~KArchiveManager(); - - /*! - @brief Add paths to the archive - @param strip string to remove from the start of every path - @param destination relative path where paths should be added to - */ - bool add(const QStringList &paths, const QByteArray &strip = "/", const QByteArray &destination = "") const; - //! @brief Remove paths from the archive - bool remove(const QStringList &paths) const; - /*! - @brief Extract paths to destination - @param destination existing directory, you can use @p QDir::mkpath(QString) - @param preserve preserve advanced attributes (ACL/ATTR) - */ - bool extract(const QStringList &paths, const QString &destination, bool preserve = true) const; - - /*! - @brief List the content of the archive - @note may return empty list on both failure and success - @note some formats list directories, some do not - @todo report failure somehow - */ - QList list() const; - - //! @brief Get information for path in archive - KArchiveInfo info(const QString &path) const; - - //! @brief Report if archive is writable - bool writable() const; - //! @brief Report if path is supported archive - static bool supported(const QString &path); - - private: - KArchiveManagerPrivate *d; -}; +#include class KArchiveModelPrivate; @@ -165,52 +33,49 @@ class KArchiveModelPrivate; \code QTreeView view; KArchiveModel model; - KArchiveManager archive("/home/joe/archive.tar.gz"); + KArchive archive("/home/joe/archive.tar.gz"); view.setModel(model); model.loadArchive(&archive); \endcode - @see KArchiveManager, KArchiveInfo - @todo sorting by column values is borked */ -class KArchiveModel : public QStandardItemModel { +class KArchiveModel : public QStandardItemModel +{ Q_OBJECT - public: - KArchiveModel(QObject *parent = nullptr); - ~KArchiveModel(); +public: + KArchiveModel(QObject *parent = nullptr); + ~KArchiveModel(); - //! @brief Load archive into the model - bool loadArchive(const KArchiveManager *archive); - //! @brief Get path for index, propagates to parents to retrieve the full path - QString path(const QModelIndex &index) const; - /*! - @brief Get paths for index, propagates to childs to retrieve all sub-paths. - Usefull for obtaining the selected item paths for add, remove and extract - operations - */ - QStringList paths(const QModelIndex &index) const; - /*! - @brief Get parent directory for index. Usefull for obtaining the current dir - for add operations - */ - QString dir(const QModelIndex &index) const; + //! @brief Load archive into the model + bool loadArchive(const KArchive *archive); + //! @brief Get path for index, propagates to parents to retrieve the full path + QString path(const QModelIndex &index) const; + /*! + @brief Get paths for index, propagates to childs to retrieve all sub-paths. + Usefull for obtaining the selected item paths for add, remove and extract + operations + */ + QStringList paths(const QModelIndex &index) const; + /*! + @brief Get parent directory for index. Usefull for obtaining the current dir + for add operations + */ + QString dir(const QModelIndex &index) const; - Q_SIGNALS: - //! @brief Signals load was started - void loadStarted(); - //! @brief Signals load was finished - void loadFinished(); +Q_SIGNALS: + //! @brief Signals load was started + void loadStarted(); + //! @brief Signals load was finished + void loadFinished(); - private Q_SLOTS: - void slotLoadFinished(); +private Q_SLOTS: + void slotLoadFinished(); - private: - KArchiveModelPrivate *d; +private: + KArchiveModelPrivate *d; }; -Q_DECLARE_METATYPE(KArchiveInfo); - #endif // KARCHIVEMANAGER_H diff --git a/karchivemanager/strmode.c b/karchivemanager/strmode.c deleted file mode 100644 index 3b276503..00000000 --- a/karchivemanager/strmode.c +++ /dev/null @@ -1,143 +0,0 @@ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * 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. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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 -#include -#include -#include - -void -strmode(mode_t mode, char *p) -{ - /* print type */ - switch (mode & S_IFMT) { - case S_IFDIR: /* directory */ - *p++ = 'd'; - break; - case S_IFCHR: /* character special */ - *p++ = 'c'; - break; - case S_IFBLK: /* block special */ - *p++ = 'b'; - break; - case S_IFREG: /* regular */ - *p++ = '-'; - break; - case S_IFLNK: /* symbolic link */ - *p++ = 'l'; - break; - case S_IFSOCK: /* socket */ - *p++ = 's'; - break; -#ifdef S_IFIFO - case S_IFIFO: /* fifo */ - *p++ = 'p'; - break; -#endif -#ifdef S_IFWHT - case S_IFWHT: /* whiteout */ - *p++ = 'w'; - break; -#endif - default: /* unknown */ - *p++ = '?'; - break; - } - /* usr */ - if (mode & S_IRUSR) - *p++ = 'r'; - else - *p++ = '-'; - if (mode & S_IWUSR) - *p++ = 'w'; - else - *p++ = '-'; - switch (mode & (S_IXUSR | S_ISUID)) { - case 0: - *p++ = '-'; - break; - case S_IXUSR: - *p++ = 'x'; - break; - case S_ISUID: - *p++ = 'S'; - break; - case S_IXUSR | S_ISUID: - *p++ = 's'; - break; - } - /* group */ - if (mode & S_IRGRP) - *p++ = 'r'; - else - *p++ = '-'; - if (mode & S_IWGRP) - *p++ = 'w'; - else - *p++ = '-'; - switch (mode & (S_IXGRP | S_ISGID)) { - case 0: - *p++ = '-'; - break; - case S_IXGRP: - *p++ = 'x'; - break; - case S_ISGID: - *p++ = 'S'; - break; - case S_IXGRP | S_ISGID: - *p++ = 's'; - break; - } - /* other */ - if (mode & S_IROTH) - *p++ = 'r'; - else - *p++ = '-'; - if (mode & S_IWOTH) - *p++ = 'w'; - else - *p++ = '-'; - switch (mode & (S_IXOTH | S_ISVTX)) { - case 0: - *p++ = '-'; - break; - case S_IXOTH: - *p++ = 'x'; - break; - case S_ISVTX: - *p++ = 'T'; - break; - case S_IXOTH | S_ISVTX: - *p++ = 't'; - break; - } - *p++ = ' '; /* will be a '+' if ACL's implemented */ - *p = '\0'; -}