File indexing completed on 2024-05-05 05:50:39

0001 /*
0002     SPDX-FileCopyrightText: 2007 Henrique Pinto <henrique.pinto@kdemail.net>
0003     SPDX-FileCopyrightText: 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
0004     SPDX-FileCopyrightText: 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
0005     SPDX-FileCopyrightText: 2016 Vladyslav Batyrenko <mvlabat@gmail.com>
0006 
0007     SPDX-License-Identifier: BSD-2-Clause
0008 */
0009 
0010 #include "archiveinterface.h"
0011 #include "ark_debug.h"
0012 #include "jobs.h"
0013 #include "mimetypes.h"
0014 #include "windows_stat.h"
0015 
0016 #include <QDir>
0017 #include <QFileInfo>
0018 
0019 namespace Kerfuffle
0020 {
0021 ReadOnlyArchiveInterface::ReadOnlyArchiveInterface(QObject *parent, const QVariantList &args)
0022     : QObject(parent)
0023     , m_numberOfVolumes(0)
0024     , m_numberOfEntries(0)
0025     , m_waitForFinishedSignal(false)
0026     , m_isHeaderEncryptionEnabled(false)
0027     , m_isCorrupt(false)
0028     , m_isMultiVolume(false)
0029     , m_unpackedSize(0)
0030 {
0031     Q_ASSERT(args.size() >= 2);
0032     qRegisterMetaType<Kerfuffle::Query *>();
0033 
0034     qCDebug(ARK) << "Created read-only interface for" << args.first().toString();
0035     m_filename = args.first().toString();
0036     m_mimetype = determineMimeType(m_filename);
0037     connect(this, &ReadOnlyArchiveInterface::entry, this, &ReadOnlyArchiveInterface::onEntry);
0038     m_metaData = args.at(1).value<KPluginMetaData>();
0039 }
0040 
0041 ReadOnlyArchiveInterface::~ReadOnlyArchiveInterface()
0042 {
0043 }
0044 
0045 void ReadOnlyArchiveInterface::onEntry(Archive::Entry *archiveEntry)
0046 {
0047     Q_ASSERT(archiveEntry);
0048     m_numberOfEntries += 1;
0049     m_unpackedSize += archiveEntry->isSparse() ? archiveEntry->sparseSize() : archiveEntry->size();
0050 }
0051 
0052 QString ReadOnlyArchiveInterface::filename() const
0053 {
0054     return m_filename;
0055 }
0056 
0057 QString ReadOnlyArchiveInterface::comment() const
0058 {
0059     return m_comment;
0060 }
0061 
0062 bool ReadOnlyArchiveInterface::isReadOnly() const
0063 {
0064     return true;
0065 }
0066 
0067 bool ReadOnlyArchiveInterface::open()
0068 {
0069     return true;
0070 }
0071 
0072 void ReadOnlyArchiveInterface::setPassword(const QString &password)
0073 {
0074     m_password = password;
0075 }
0076 
0077 void ReadOnlyArchiveInterface::setHeaderEncryptionEnabled(bool enabled)
0078 {
0079     m_isHeaderEncryptionEnabled = enabled;
0080 }
0081 
0082 QString ReadOnlyArchiveInterface::password() const
0083 {
0084     return m_password;
0085 }
0086 
0087 bool ReadOnlyArchiveInterface::doKill()
0088 {
0089     // default implementation
0090     return false;
0091 }
0092 
0093 void ReadOnlyArchiveInterface::setCorrupt(bool isCorrupt)
0094 {
0095     m_isCorrupt = isCorrupt;
0096 }
0097 
0098 bool ReadOnlyArchiveInterface::isCorrupt() const
0099 {
0100     return m_isCorrupt;
0101 }
0102 
0103 bool ReadOnlyArchiveInterface::isMultiVolume() const
0104 {
0105     return m_isMultiVolume;
0106 }
0107 
0108 void ReadOnlyArchiveInterface::setMultiVolume(bool value)
0109 {
0110     m_isMultiVolume = value;
0111 }
0112 
0113 int ReadOnlyArchiveInterface::numberOfVolumes() const
0114 {
0115     return m_numberOfVolumes;
0116 }
0117 
0118 QString ReadOnlyArchiveInterface::multiVolumeName() const
0119 {
0120     return filename();
0121 }
0122 
0123 ReadWriteArchiveInterface::ReadWriteArchiveInterface(QObject *parent, const QVariantList &args)
0124     : ReadOnlyArchiveInterface(parent, args)
0125 {
0126     qCDebug(ARK) << "Created read-write interface for" << args.first().toString();
0127 
0128     connect(this, &ReadWriteArchiveInterface::entryRemoved, this, &ReadWriteArchiveInterface::onEntryRemoved);
0129 }
0130 
0131 ReadWriteArchiveInterface::~ReadWriteArchiveInterface()
0132 {
0133 }
0134 
0135 bool ReadOnlyArchiveInterface::waitForFinishedSignal()
0136 {
0137     return m_waitForFinishedSignal;
0138 }
0139 
0140 int ReadOnlyArchiveInterface::moveRequiredSignals() const
0141 {
0142     return 1;
0143 }
0144 
0145 int ReadOnlyArchiveInterface::copyRequiredSignals() const
0146 {
0147     return 1;
0148 }
0149 
0150 void ReadOnlyArchiveInterface::setWaitForFinishedSignal(bool value)
0151 {
0152     m_waitForFinishedSignal = value;
0153 }
0154 
0155 qulonglong ReadOnlyArchiveInterface::unpackedSize() const
0156 {
0157     return m_unpackedSize;
0158 }
0159 
0160 QString ReadOnlyArchiveInterface::permissionsToString(mode_t perm)
0161 {
0162     QString modeval;
0163     if ((perm & S_IFMT) == QT_STAT_DIR) {
0164         modeval.append(QLatin1Char('d'));
0165     } else if ((perm & S_IFMT) == QT_STAT_LNK) {
0166         modeval.append(QLatin1Char('l'));
0167     } else {
0168         modeval.append(QLatin1Char('-'));
0169     }
0170     modeval.append((perm & S_IRUSR) ? QLatin1Char('r') : QLatin1Char('-'));
0171     modeval.append((perm & S_IWUSR) ? QLatin1Char('w') : QLatin1Char('-'));
0172     if ((perm & S_ISUID) && (perm & S_IXUSR)) {
0173         modeval.append(QLatin1Char('s'));
0174     } else if ((perm & S_ISUID)) {
0175         modeval.append(QLatin1Char('S'));
0176     } else if ((perm & S_IXUSR)) {
0177         modeval.append(QLatin1Char('x'));
0178     } else {
0179         modeval.append(QLatin1Char('-'));
0180     }
0181     modeval.append((perm & S_IRGRP) ? QLatin1Char('r') : QLatin1Char('-'));
0182     modeval.append((perm & S_IWGRP) ? QLatin1Char('w') : QLatin1Char('-'));
0183     if ((perm & S_ISGID) && (perm & S_IXGRP)) {
0184         modeval.append(QLatin1Char('s'));
0185     } else if ((perm & S_ISGID)) {
0186         modeval.append(QLatin1Char('S'));
0187     } else if ((perm & S_IXGRP)) {
0188         modeval.append(QLatin1Char('x'));
0189     } else {
0190         modeval.append(QLatin1Char('-'));
0191     }
0192     modeval.append((perm & S_IROTH) ? QLatin1Char('r') : QLatin1Char('-'));
0193     modeval.append((perm & S_IWOTH) ? QLatin1Char('w') : QLatin1Char('-'));
0194     if ((perm & S_ISVTX) && (perm & S_IXOTH)) {
0195         modeval.append(QLatin1Char('t'));
0196     } else if ((perm & S_ISVTX)) {
0197         modeval.append(QLatin1Char('T'));
0198     } else if ((perm & S_IXOTH)) {
0199         modeval.append(QLatin1Char('x'));
0200     } else {
0201         modeval.append(QLatin1Char('-'));
0202     }
0203     return modeval;
0204 }
0205 
0206 QStringList ReadOnlyArchiveInterface::entryFullPaths(const QVector<Archive::Entry *> &entries, PathFormat format)
0207 {
0208     QStringList filesList;
0209     for (const Archive::Entry *file : entries) {
0210         filesList << file->fullPath(format);
0211     }
0212     return filesList;
0213 }
0214 
0215 QVector<Archive::Entry *> ReadOnlyArchiveInterface::entriesWithoutChildren(const QVector<Archive::Entry *> &entries)
0216 {
0217     // QMap is easy way to get entries sorted by their fullPath.
0218     QMap<QString, Archive::Entry *> sortedEntries;
0219     for (Archive::Entry *entry : entries) {
0220         sortedEntries.insert(entry->fullPath(), entry);
0221     }
0222 
0223     QVector<Archive::Entry *> filteredEntries;
0224     QString lastFolder;
0225     for (Archive::Entry *entry : std::as_const(sortedEntries)) {
0226         if (!lastFolder.isEmpty() && entry->fullPath().startsWith(lastFolder)) {
0227             continue;
0228         }
0229 
0230         lastFolder = (entry->fullPath().right(1) == QLatin1String("/")) ? entry->fullPath() : QString();
0231         filteredEntries << entry;
0232     }
0233 
0234     return filteredEntries;
0235 }
0236 
0237 QStringList ReadOnlyArchiveInterface::entryPathsFromDestination(QStringList entries, const Archive::Entry *destination, int entriesWithoutChildren)
0238 {
0239     QStringList paths = QStringList();
0240     entries.sort();
0241     QString lastFolder;
0242     const QString destinationPath = (destination == nullptr) ? QString() : destination->fullPath();
0243 
0244     QString newPath;
0245     int nameLength = 0;
0246     for (const QString &entryPath : std::as_const(entries)) {
0247         if (!lastFolder.isEmpty() && entryPath.startsWith(lastFolder)) {
0248             // Replace last moved or copied folder path with destination path.
0249             int charsCount = entryPath.length() - lastFolder.length();
0250             if (entriesWithoutChildren != 1) {
0251                 charsCount += nameLength;
0252             }
0253             newPath = destinationPath + entryPath.right(charsCount);
0254         } else {
0255             const QString name = entryPath.split(QLatin1Char('/'), Qt::SkipEmptyParts).last();
0256             if (entriesWithoutChildren != 1) {
0257                 newPath = destinationPath + name;
0258                 if (entryPath.right(1) == QLatin1String("/")) {
0259                     newPath += QLatin1Char('/');
0260                 }
0261             } else {
0262                 // If the mode is set to Move and there is only one passed file in the list,
0263                 // we have to use destination as newPath.
0264                 newPath = destinationPath;
0265             }
0266             if (entryPath.right(1) == QLatin1String("/")) {
0267                 nameLength = name.length() + 1; // plus slash
0268                 lastFolder = entryPath;
0269             } else {
0270                 nameLength = 0;
0271                 lastFolder = QString();
0272             }
0273         }
0274         paths << newPath;
0275     }
0276 
0277     return paths;
0278 }
0279 
0280 bool ReadOnlyArchiveInterface::isHeaderEncryptionEnabled() const
0281 {
0282     return m_isHeaderEncryptionEnabled;
0283 }
0284 
0285 QMimeType ReadOnlyArchiveInterface::mimetype() const
0286 {
0287     return m_mimetype;
0288 }
0289 
0290 bool ReadOnlyArchiveInterface::hasBatchExtractionProgress() const
0291 {
0292     return false;
0293 }
0294 
0295 bool ReadOnlyArchiveInterface::isLocked() const
0296 {
0297     return false;
0298 }
0299 
0300 bool ReadWriteArchiveInterface::isReadOnly() const
0301 {
0302     if (isLocked()) {
0303         return true;
0304     }
0305 
0306     // We set corrupt archives to read-only to avoid add/delete actions, that
0307     // are likely to fail anyway.
0308     if (isCorrupt()) {
0309         return true;
0310     }
0311 
0312     QFileInfo fileInfo(filename());
0313     if (fileInfo.exists()) {
0314         return !fileInfo.isWritable();
0315     } else {
0316         return !fileInfo.dir().exists(); // TODO: Should also check if we can create a file in that directory
0317     }
0318 }
0319 
0320 uint ReadOnlyArchiveInterface::numberOfEntries() const
0321 {
0322     return m_numberOfEntries;
0323 }
0324 
0325 void ReadWriteArchiveInterface::onEntryRemoved(const QString &path)
0326 {
0327     Q_UNUSED(path)
0328     m_numberOfEntries--;
0329 }
0330 
0331 } // namespace Kerfuffle
0332 
0333 #include "moc_archiveinterface.cpp"