diff --git a/importer/fileutils.cpp b/importer/fileutils.cpp index a51a18cf..997b28f4 100644 --- a/importer/fileutils.cpp +++ b/importer/fileutils.cpp @@ -1,162 +1,143 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ // Self #include "fileutils.h" // Qt #include #include #include // KDE #include #include #include #include #include #include #include -// libc -#include -#include -#include - namespace Gwenview { namespace FileUtils { bool contentsAreIdentical(const QUrl& url1, const QUrl& url2, QWidget* authWindow) { // FIXME: Support remote urls KIO::StatJob *statJob = KIO::mostLocalUrl(url1); KJobWidgets::setWindow(statJob, authWindow); if (!statJob->exec()) { qWarning() << "Unable to stat" << url1; return false; } QFile file1(statJob->mostLocalUrl().toLocalFile()); if (!file1.open(QIODevice::ReadOnly)) { // Can't read url1, assume it's different from url2 qWarning() << "Can't read" << url1; return false; } statJob = KIO::mostLocalUrl(url2); KJobWidgets::setWindow(statJob, authWindow); if (!statJob->exec()) { qWarning() << "Unable to stat" << url2; return false; } QFile file2(statJob->mostLocalUrl().toLocalFile()); if (!file2.open(QIODevice::ReadOnly)) { // Can't read url2, assume it's different from url1 qWarning() << "Can't read" << url2; return false; } const int CHUNK_SIZE = 4096; while (!file1.atEnd() && !file2.atEnd()) { QByteArray url1Array = file1.read(CHUNK_SIZE); QByteArray url2Array = file2.read(CHUNK_SIZE); if (url1Array != url2Array) { return false; } } if (file1.atEnd() && file2.atEnd()) { return true; } else { qWarning() << "One file ended before the other"; return false; } } RenameResult rename(const QUrl& src, const QUrl& dst_, QWidget* authWindow) { QUrl dst = dst_; RenameResult result = RenamedOK; int count = 1; QFileInfo fileInfo(dst.fileName()); QString prefix = fileInfo.completeBaseName() + '_'; QString suffix = '.' + fileInfo.suffix(); // Get src size KIO::StatJob *sourceStat = KIO::stat(src); KJobWidgets::setWindow(sourceStat, authWindow); if (!sourceStat->exec()) { return RenameFailed; } KFileItem item(sourceStat->statResult(), src, true /* delayedMimeTypes */); KIO::filesize_t srcSize = item.size(); // Find unique name KIO::StatJob *statJob = KIO::stat(dst); KJobWidgets::setWindow(statJob, authWindow); while (statJob->exec()) { // File exists. If it's not the same, try to create a new name item = KFileItem(statJob->statResult(), dst, true /* delayedMimeTypes */); KIO::filesize_t dstSize = item.size(); if (srcSize == dstSize && contentsAreIdentical(src, dst, authWindow)) { // Already imported, skip it KIO::Job* job = KIO::file_delete(src, KIO::HideProgressInfo); KJobWidgets::setWindow(job, authWindow); return Skipped; } result = RenamedUnderNewName; dst.setPath(dst.adjusted(QUrl::RemoveFilename).path() + prefix + QString::number(count) + suffix); statJob = KIO::stat(dst); KJobWidgets::setWindow(statJob, authWindow); ++count; } // Rename KIO::Job* job = KIO::rename(src, dst, KIO::HideProgressInfo); KJobWidgets::setWindow(job, authWindow); if (!job->exec()) { result = RenameFailed; } return result; } -QString createTempDir(const QString& baseDir, const QString& prefix, QString* errorMessage) -{ - Q_ASSERT(errorMessage); - - QByteArray name = QFile::encodeName(baseDir + '/' + prefix + "XXXXXX"); - - if (!mkdtemp(name.data())) { - // Failure - *errorMessage = QString::fromLocal8Bit(::strerror(errno)); - return QString(); - } - return QFile::decodeName(name + '/'); -} - } // namespace } // namespace diff --git a/importer/fileutils.h b/importer/fileutils.h index a464b94e..cf579487 100644 --- a/importer/fileutils.h +++ b/importer/fileutils.h @@ -1,63 +1,54 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ #ifndef FILEUTILS_H #define FILEUTILS_H class QString; class QWidget; class QUrl; namespace Gwenview { namespace FileUtils { enum RenameResult { RenamedOK, /** Renamed without problem */ RenamedUnderNewName, /** Destination already existed, so rename() added a suffix to make the name unique */ Skipped, /** Destination already existed and contained the same data as source, so rename() just removed the source */ RenameFailed /** Rename failed */ }; /** * Compare content of two urls, returns whether they are the same */ bool contentsAreIdentical(const QUrl& url1, const QUrl& url2, QWidget* authWindow = nullptr); /** * Rename src to dst, returns RenameResult */ RenameResult rename(const QUrl& src, const QUrl& dst, QWidget* authWindow = nullptr); -/** - * Create a temp dir in baseDir, starting with prefix. If successful returns - * the created dir otherwise returns an empty string and writes the error message - * in errorMessage. - * - * errorMessage must be a valid pointer. - */ -QString createTempDir(const QString& baseDir, const QString& prefix, QString* errorMessage); - } // namespace } // namespace #endif /* FILEUTILS_H */ diff --git a/importer/importer.cpp b/importer/importer.cpp index 4043c65c..38219821 100644 --- a/importer/importer.cpp +++ b/importer/importer.cpp @@ -1,238 +1,247 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ // Self #include "importer.h" // Qt #include #include #include +#include // KDE #include #include #include #include #include #include #include // stdc++ #include // Local #include #include #include #include namespace Gwenview { struct ImporterPrivate { Importer* q; QWidget* mAuthWindow; std::unique_ptr mFileNameFormater; QUrl mTempImportDirUrl; + QTemporaryDir* mTempImportDir; /* @defgroup reset Should be reset in start() * @{ */ QList mUrlList; QList mImportedUrlList; QList mSkippedUrlList; int mRenamedCount; int mProgress; int mJobProgress; /* @} */ QUrl mCurrentUrl; bool createImportDir(const QUrl& url) { Q_ASSERT(url.isLocalFile()); // FIXME: Support remote urls if (!QDir().mkpath(url.toLocalFile())) { emit q->error(i18n("Could not create destination folder.")); return false; } - QString message; - QString dir = FileUtils::createTempDir(url.toLocalFile(), ".gwenview_importer-", &message); - mTempImportDirUrl = QUrl::fromLocalFile(dir); - if (mTempImportDirUrl.isEmpty()) { - emit q->error(i18n("Could not create temporary upload folder:\n%1", message)); + + QString tempDirPath = url.toLocalFile() + "/.gwenview_importer-XXXXXX"; + mTempImportDir = new QTemporaryDir(tempDirPath); + + if (!mTempImportDir->isValid()) { + emit q->error(i18n("Could not create temporary upload folder.")); return false; } + + mTempImportDirUrl = QUrl::fromLocalFile(mTempImportDir->path() + '/'); + if (!mTempImportDirUrl.isValid()) { + emit q->error(i18n("Could not create temporary upload folder.")); + return false; + } + return true; } void importNext() { if (mUrlList.empty()) { q->finalizeImport(); return; } mCurrentUrl = mUrlList.takeFirst(); QUrl dst = mTempImportDirUrl; dst.setPath(dst.path() + mCurrentUrl.fileName()); KIO::Job* job = KIO::copy(mCurrentUrl, dst, KIO::HideProgressInfo); KJobWidgets::setWindow(job, mAuthWindow); QObject::connect(job, &KJob::result, q, &Importer::slotCopyDone); QObject::connect(job, SIGNAL(percent(KJob*,ulong)), q, SLOT(slotPercent(KJob*,ulong))); } void renameImportedUrl(const QUrl& src) { QUrl dst = src.resolved(QUrl("..")); QString fileName; if (mFileNameFormater.get()) { KFileItem item(src); item.setDelayedMimeTypes(true); // Get the document time, but do not cache the result because the // 'src' url is temporary: if we import "foo/image.jpg" and // "bar/image.jpg", both images will be temporarily saved in the // 'src' url. QDateTime dateTime = TimeUtils::dateTimeForFileItem(item, TimeUtils::SkipCache); fileName = mFileNameFormater->format(src, dateTime); } else { fileName = src.fileName(); } dst.setPath(dst.path() + fileName); FileUtils::RenameResult result = FileUtils::rename(src, dst, mAuthWindow); switch (result) { case FileUtils::RenamedOK: mImportedUrlList << mCurrentUrl; break; case FileUtils::RenamedUnderNewName: mRenamedCount++; mImportedUrlList << mCurrentUrl; break; case FileUtils::Skipped: mSkippedUrlList << mCurrentUrl; break; case FileUtils::RenameFailed: qWarning() << "Rename failed for" << mCurrentUrl; } q->advance(); importNext(); } }; Importer::Importer(QWidget* parent) : QObject(parent) , d(new ImporterPrivate) { d->q = this; d->mAuthWindow = parent; } Importer::~Importer() { delete d; } void Importer::setAutoRenameFormat(const QString& format) { if (format.isEmpty()) { d->mFileNameFormater.reset(nullptr); } else { d->mFileNameFormater.reset(new FileNameFormater(format)); } } void Importer::start(const QList& list, const QUrl& destination) { d->mUrlList = list; d->mImportedUrlList.clear(); d->mSkippedUrlList.clear(); d->mRenamedCount = 0; d->mProgress = 0; d->mJobProgress = 0; emitProgressChanged(); emit maximumChanged(d->mUrlList.count() * 100); if (!d->createImportDir(destination)) { qWarning() << "Could not create import dir"; return; } d->importNext(); } void Importer::slotCopyDone(KJob* _job) { KIO::CopyJob* job = static_cast(_job); QUrl url = job->destUrl(); if (job->error()) { qWarning() << "FIXME: What do we do with failed urls?"; advance(); d->importNext(); return; } d->renameImportedUrl(url); } void Importer::finalizeImport() { - KIO::Job* job = KIO::del(d->mTempImportDirUrl, KIO::HideProgressInfo); - KJobWidgets::setWindow(job, d->mAuthWindow); + delete d->mTempImportDir; emit importFinished(); } void Importer::advance() { ++d->mProgress; d->mJobProgress = 0; emitProgressChanged(); } void Importer::slotPercent(KJob*, unsigned long percent) { d->mJobProgress = percent; emitProgressChanged(); } void Importer::emitProgressChanged() { emit progressChanged(d->mProgress * 100 + d->mJobProgress); } QList Importer::importedUrlList() const { return d->mImportedUrlList; } QList Importer::skippedUrlList() const { return d->mSkippedUrlList; } int Importer::renamedCount() const { return d->mRenamedCount; } } // namespace