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

0001 /*
0002     SPDX-FileCopyrightText: 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
0003     SPDX-FileCopyrightText: 2009 Raphael Kubo da Costa <rakuco@FreeBSD.org>
0004 
0005     SPDX-License-Identifier: BSD-2-Clause
0006 */
0007 
0008 #include "addtoarchive.h"
0009 #include "archiveentry.h"
0010 #include "ark_debug.h"
0011 #include "createdialog.h"
0012 #include "jobs.h"
0013 
0014 #include <KIO/JobTracker>
0015 #include <KJobTrackerInterface>
0016 #include <KLocalizedString>
0017 #include <KMessageBox>
0018 
0019 #include <KFileUtils>
0020 #include <QDir>
0021 #include <QFileInfo>
0022 #include <QMimeDatabase>
0023 #include <QPointer>
0024 #include <QTimer>
0025 
0026 namespace Kerfuffle
0027 {
0028 AddToArchive::AddToArchive(QObject *parent)
0029     : KJob(parent)
0030     , m_changeToFirstPath(false)
0031     , m_enableHeaderEncryption(false)
0032 {
0033 }
0034 
0035 AddToArchive::~AddToArchive()
0036 {
0037 }
0038 
0039 void AddToArchive::setAutoFilenameSuffix(const QString &suffix)
0040 {
0041     m_autoFilenameSuffix = suffix;
0042 }
0043 
0044 void AddToArchive::setChangeToFirstPath(bool value)
0045 {
0046     m_changeToFirstPath = value;
0047 }
0048 
0049 void AddToArchive::setFilename(const QUrl &path)
0050 {
0051     m_filename = path.toLocalFile();
0052 }
0053 
0054 void AddToArchive::setMimeType(const QString &mimeType)
0055 {
0056     m_mimeType = mimeType;
0057 }
0058 
0059 void AddToArchive::setPassword(const QString &password)
0060 {
0061     m_password = password;
0062 }
0063 
0064 void AddToArchive::setHeaderEncryptionEnabled(bool enabled)
0065 {
0066     m_enableHeaderEncryption = enabled;
0067 }
0068 
0069 bool AddToArchive::showAddDialog(QWidget *parentWidget)
0070 {
0071     qCDebug(ARK) << "Opening add dialog";
0072 
0073     if (m_filename.isEmpty()) {
0074         m_filename = getFileNameForEntries(m_entries, QString());
0075     }
0076 
0077     QPointer<Kerfuffle::CreateDialog> dialog = new Kerfuffle::CreateDialog(parentWidget, // parent
0078                                                                            i18n("Compress to Archive"), // caption
0079                                                                            QUrl::fromLocalFile(QFileInfo(m_filename).path())); // startDir
0080 
0081     dialog->setFileName(QFileInfo(m_filename).fileName());
0082 
0083     bool ret = dialog.data()->exec();
0084 
0085     if (ret) {
0086         qCDebug(ARK) << "CreateDialog returned URL:" << dialog.data()->selectedUrl().toString();
0087         qCDebug(ARK) << "CreateDialog returned mime:" << dialog.data()->currentMimeType().name();
0088         setFilename(dialog.data()->selectedUrl());
0089         setMimeType(dialog.data()->currentMimeType().name());
0090         setPassword(dialog.data()->password());
0091         setHeaderEncryptionEnabled(dialog.data()->isHeaderEncryptionEnabled());
0092         m_options.setCompressionLevel(dialog.data()->compressionLevel());
0093         m_options.setCompressionMethod(dialog.data()->compressionMethod());
0094         m_options.setEncryptionMethod(dialog.data()->encryptionMethod());
0095         m_options.setVolumeSize(dialog.data()->volumeSize());
0096     }
0097 
0098     delete dialog.data();
0099 
0100     return ret;
0101 }
0102 
0103 bool AddToArchive::addInput(const QUrl &url)
0104 {
0105     Archive::Entry *entry = new Archive::Entry(this);
0106     entry->setFullPath(url.toLocalFile());
0107     m_entries << entry;
0108 
0109     if (m_firstPath.isEmpty()) {
0110         QString firstEntry = url.toLocalFile();
0111         m_firstPath = QFileInfo(firstEntry).dir().absolutePath();
0112     }
0113 
0114     return true;
0115 }
0116 
0117 void AddToArchive::start()
0118 {
0119     qCDebug(ARK) << "Starting job";
0120 
0121     QTimer::singleShot(0, this, &AddToArchive::slotStartJob);
0122 }
0123 
0124 bool AddToArchive::doKill()
0125 {
0126     return m_createJob && m_createJob->kill();
0127 }
0128 
0129 void AddToArchive::slotStartJob()
0130 {
0131     if (m_entries.isEmpty()) {
0132         KMessageBox::error(nullptr, i18n("No input files were given."));
0133         emitResult();
0134         return;
0135     }
0136 
0137     if (m_filename.isEmpty()) {
0138         if (m_autoFilenameSuffix.isEmpty()) {
0139             KMessageBox::error(nullptr,
0140                                xi18n("You need to either supply a filename for the archive or a suffix (such as rar, tar.gz) with the "
0141                                      "<command>--autofilename</command> argument."));
0142             emitResult();
0143             return;
0144         }
0145 
0146         if (m_firstPath.isEmpty()) {
0147             qCWarning(ARK) << "Weird, this should not happen. no firstpath defined. aborting";
0148             emitResult();
0149             return;
0150         }
0151 
0152         detectFileName();
0153     }
0154 
0155     if (m_changeToFirstPath) {
0156         if (m_firstPath.isEmpty()) {
0157             qCWarning(ARK) << "Weird, this should not happen. no firstpath defined. aborting";
0158             emitResult();
0159             return;
0160         }
0161 
0162         const QDir stripDir(m_firstPath);
0163 
0164         for (Archive::Entry *entry : std::as_const(m_entries)) {
0165             entry->setFullPath(stripDir.absoluteFilePath(entry->fullPath()));
0166         }
0167 
0168         qCDebug(ARK) << "Setting GlobalWorkDir to " << stripDir.path();
0169         m_options.setGlobalWorkDir(stripDir.path());
0170     }
0171 
0172     m_createJob = Archive::create(m_filename, m_mimeType, m_entries, m_options, this);
0173 
0174     m_createJob->setProperty("immediateProgressReporting", m_immediateProgressReporting);
0175     m_createJob->setProperty("destUrl", QUrl::fromLocalFile(m_filename));
0176 
0177     if (!m_password.isEmpty()) {
0178         m_createJob->enableEncryption(m_password, m_enableHeaderEncryption);
0179     }
0180 
0181     KIO::getJobTracker()->registerJob(m_createJob);
0182     connect(m_createJob, &KJob::result, this, &AddToArchive::slotFinished);
0183     m_createJob->start();
0184 }
0185 
0186 void AddToArchive::detectFileName()
0187 {
0188     const QString suffix = !m_autoFilenameSuffix.isEmpty() ? m_autoFilenameSuffix : QString();
0189     const QString finalName = getFileNameForEntries(m_entries, suffix);
0190 
0191     qCDebug(ARK) << "Autoset filename to" << finalName;
0192     m_filename = finalName;
0193 }
0194 
0195 void AddToArchive::slotFinished(KJob *job)
0196 {
0197     qCDebug(ARK) << "job finished";
0198 
0199     if (job->error() && !job->errorString().isEmpty()) {
0200         KMessageBox::error(nullptr, job->errorString());
0201     }
0202 
0203     emitResult();
0204 }
0205 
0206 QString findCommonPrefixForUrls(const QList<QUrl> &list)
0207 {
0208     Q_ASSERT(!list.isEmpty());
0209     QString prefix = list.front().fileName();
0210     for (QList<QUrl>::const_iterator it = list.begin(); it != list.end(); ++it) {
0211         QString fileName = it->fileName();
0212         // Strip filename of its extension, but only if present (see #362690).
0213         // Use loops to handle cases like `*.tar.gz`.
0214         while (!QMimeDatabase().mimeTypeForFile(fileName, QMimeDatabase::MatchExtension).isDefault()) {
0215             const QString strippedName = QFileInfo(fileName).completeBaseName();
0216             if (strippedName == fileName) {
0217                 break;
0218             }
0219             fileName = strippedName;
0220         }
0221 
0222         if (prefix.length() > fileName.length()) {
0223             prefix.truncate(fileName.length());
0224         }
0225 
0226         for (int i = 0; i < prefix.length(); ++i) {
0227             if (prefix.at(i) != fileName.at(i)) {
0228                 prefix.truncate(i);
0229                 break;
0230             }
0231         }
0232     }
0233 
0234     return prefix;
0235 }
0236 
0237 QString AddToArchive::getFileNameForUrls(const QList<QUrl> &urls, const QString &suffix)
0238 {
0239     Q_ASSERT(!urls.isEmpty());
0240 
0241     const QFileInfo fileInfo = QFileInfo(urls.constFirst().toLocalFile());
0242     QString base = findCommonPrefixForUrls(urls);
0243     if (urls.size() > 1 && base.length() < 5) {
0244         base = i18nc("Default name of a newly-created multi-file archive", "Archive");
0245     }
0246 
0247     const QString path = fileInfo.absolutePath() + QStringLiteral("/");
0248 
0249     if (suffix.isEmpty()) {
0250         return path + base;
0251     }
0252     QString finalName = base + QLatin1Char('.') + suffix;
0253     if (QFileInfo::exists(path + finalName)) {
0254         finalName = KFileUtils::suggestName(QUrl::fromLocalFile(path), finalName);
0255     }
0256     return path + finalName;
0257 }
0258 
0259 QString AddToArchive::getFileNameForEntries(const QVector<Archive::Entry *> &entries, const QString &suffix)
0260 {
0261     QList<QUrl> urls;
0262     for (const auto &entry : entries) {
0263         urls.append(QUrl::fromLocalFile(entry->fullPath()));
0264     }
0265     return getFileNameForUrls(urls, suffix);
0266 }
0267 
0268 void AddToArchive::setImmediateProgressReporting(bool immediateProgressReporting)
0269 {
0270     m_immediateProgressReporting = immediateProgressReporting;
0271 }
0272 }
0273 
0274 #include "moc_addtoarchive.cpp"